Building the Swift Project on Linux with lld instead of gold

Context

A few weeks ago, I announced that I had managed to package Swift 5.10.1 for Gentoo using the official binaries built for Fedora 39. Fortuitous timing, of course, because as of a few days ago, Gentoo officially no longer supports the gold linker as being effectively abandoned upstream. Gentoo has used ld/lld as its default linker (depending on profile) for years while allowing you to install gold if you want, but packages can now no longer rely on gold. This is a problem, because all of the prebuilt Swift binaries currently built for Linux default to using gold, since it's readily available on those systems, and means that I'll need to transition over to source builds of Swift in order to keep packaging it.

In order to do this, I need to:

  1. Build the Swift project using lld on Linux in such a way that,
  2. The built Swift defaults to also using lld

I haven't gotten to explore (2) yet, though, because I cannot get the Swift project to build using lld.

(For reference, the latest Gentoo profile uses Clang + LLVM 17 systemwide)

What I've Tried

The base build-script invocation I'm currently using is

./utils/build-script \
	--verbose-build \
	--release \
	--swift-install-components='autolink-driver;compiler;clang-resource-dir-symlink;libexec;stdlib;swift-remote-mirror;sdk-overlay;static-mirror-lib;toolchain-tools;license;sourcekit-inproc' \
	--llvm-install-components='llvm-ar;llvm-cov;llvm-profdata;IndexStore;clang;clang-resource-headers;compiler-rt;clangd;lld;LTO;clang-features-file' \
	--llbuild --skip-test-llbuild \
	--swiftpm --skip-test-swiftpm \
	--swift-driver --skip-test-swift-driver \
	--xctest --skip-build-xctest \
	--libicu --skip-test-libicu \
	--swiftdocc --skip-test-swiftdocc \
	--lldb --skip-test-lldb \
	--foundation --skip-test-foundation \
	--libdispatch --skip-test-libdispatch \
	--indexstore-db --skip-test-indexstore-db \
	--sourcekit-lsp --skip-test-sourcekit-lsp \
	--bootstrapping=bootstrapping \
	--skip-early-swift-driver --skip-early-swiftsyntax

Most of these flags are pulled directly from the presets.ini for the Linux buildbot, with bootstrapping enabled (and since the system doesn't have a Swift installation, we have to bootstrap and skip the early Swift driver and Swift Syntax; I don't know what the bootstrapping story will be for Swift 6, but one thing at a time).

  • If I run this invocation, the project manages to build LLVM and cmark before failing to build Swift due to trying to link using -fuse-ld=gold

  • If I add --extra-cmake-options="-DSWIFT_USE_LINKER=lld" to force the usage of lld, the project manages to build LLVM and cmark, before failing to build Swift due to missing symbols in Swift

    • It appears from searching around that lld behavior differs for ELF around --gc-sections, and the missing symbols are due to them being GC'd out by default
  • If I add --extra-cmake-options="-DSWIFT_USE_LINKER=lld" --extra-swift-args="^.*\$;-Xlinker\;-z\;-Xlinker\;nostart-stop-gc", the project manages to build LLVM, cmark, Swift, and a few other dependencies before another one down the road fails to build due to -fuse-ld=gold

  • It seems that all dependencies respect the LLVM_USE_LINKER setting (including Swift), so if I try --extra-cmake-options="-DLLVM_USE_LINKER=lld" --extra-swift-args="^.*\$;-Xlinker\;-z\;-Xlinker\;nostart-stop-gc", LLVM itself doesn't build due to undefined symbols:

    FAILED: lib/libIndexStore.so.15git
    : && /usr/lib/llvm/17/bin/clang++ --target=x86_64-unknown-linux-gnu -fPIC -Wno-unknown-warning-option -Werror=unguarded-availability-new -fno-stack-protector -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -Werror=unguarded-availability-new -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -pedantic -Wno-long-long -Wc++98-compat-extra-semi -Wimplicit-fallthrough -Wcovered-switch-default -Wno-class-memaccess -Wno-noexcept-type -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wsuggest-override -Wno-comment -Wstring-conversion -Wmisleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -fno-common -Woverloaded-virtual -Wno-nested-anon-types -O3 -DNDEBUG  -Wl,-z,defs -Wl,-z,nodelete -fuse-ld=lld -Wl,--color-diagnostics    -Wl,--version-script,"/home/itai/Development/swift-project/build/Ninja-ReleaseAssert/llvm-linux-x86_64/tools/clang/tools/IndexStore/IndexStore.exports" -shared -Wl,-soname,libIndexStore.so.15git -o lib/libIndexStore.so.15git tools/clang/tools/IndexStore/CMakeFiles/IndexStore.dir/IndexStore.cpp.o  -Wl,-rpath,"\$ORIGIN/../lib:"  lib/libclangIndex.a  lib/libclangIndexDataStore.a  lib/libLLVMSupport.a  lib/libclangIndex.a  lib/libclangFormat.a  lib/libclangToolingInclusions.a  lib/libclangFrontend.a  lib/libclangDriver.a  lib/libLLVMWindowsDriver.a  lib/libclangParse.a  lib/libclangCAS.a  lib/libLLVMOption.a  lib/libLLVMRemoteCachingService.a  lib/libLLVMCAS.a  lib/libLLVMRemoteNullService.a  lib/libclangSerialization.a  lib/libclangSema.a  lib/libclangAPINotes.a  lib/libclangEdit.a  lib/libclangAnalysis.a  lib/libclangASTMatchers.a  lib/libclangAST.a  lib/libLLVMFrontendOpenMP.a  lib/libLLVMScalarOpts.a  lib/libLLVMAggressiveInstCombine.a  lib/libLLVMInstCombine.a  lib/libLLVMTransformUtils.a  lib/libLLVMAnalysis.a  lib/libLLVMProfileData.a  lib/libLLVMSymbolize.a  lib/libLLVMDebugInfoPDB.a  lib/libLLVMDebugInfoMSF.a  lib/libLLVMDebugInfoDWARF.a  lib/libLLVMObject.a  lib/libLLVMMCParser.a  lib/libLLVMMC.a  lib/libLLVMDebugInfoCodeView.a  lib/libLLVMIRReader.a  lib/libLLVMAsmParser.a  lib/libLLVMTextAPI.a  lib/libclangSupport.a  lib/libclangToolingCore.a  lib/libclangRewrite.a  lib/libclangLex.a  lib/libclangBasic.a  lib/libLLVMBitReader.a  lib/libLLVMCore.a  lib/libLLVMBinaryFormat.a  lib/libLLVMRemarks.a  lib/libLLVMBitstreamReader.a  lib/libclangDirectoryWatcher.a  lib/libLLVMSupport.a  -lrt  -ldl  -lm  /usr/lib64/libz.so  /usr/lib64/libzstd.so  /usr/lib64/libtinfo.so  lib/libLLVMDemangle.a && :
    ld.lld: error: version script assignment of 'LLVM_15' to symbol 'indexstore_store_units_apply' failed: symbol not defined
    ld.lld: error: version script assignment of 'LLVM_15' to symbol 'indexstore_store_set_unit_event_handler' failed: symbol not defined
    ld.lld: error: version script assignment of 'LLVM_15' to symbol 'indexstore_occurrence_relations_apply' failed: symbol not defined
    ld.lld: error: version script assignment of 'LLVM_15' to symbol 'indexstore_record_reader_search_symbols' failed: symbol not defined
    ld.lld: error: version script assignment of 'LLVM_15' to symbol 'indexstore_record_reader_symbols_apply' failed: symbol not defined
    ld.lld: error: version script assignment of 'LLVM_15' to symbol 'indexstore_record_reader_occurrences_apply' failed: symbol not defined
    ld.lld: error: version script assignment of 'LLVM_15' to symbol 'indexstore_record_reader_occurrences_in_line_range_apply' failed: symbol not defined
    ld.lld: error: version script assignment of 'LLVM_15' to symbol 'indexstore_record_reader_occurrences_of_symbols_apply' failed: symbol not defined
    ld.lld: error: version script assignment of 'LLVM_15' to symbol 'indexstore_unit_reader_dependencies_apply' failed: symbol not defined
    ld.lld: error: version script assignment of 'LLVM_15' to symbol 'indexstore_unit_reader_includes_apply' failed: symbol not defined
    clang++: error: linker command failed with exit code 1 (use -v to see invocation)
    

    It seems that the build script for LLVM toggles some setting when LLVM_USE_LINKER is explicitly set, because it uses lld just fine when I don't set that

  • I've also tried using --extra-cmake-options="-DLLVM_USE_LINKER=lld,-DLLVM_NO_DEAD_STRIP:BOOL=TRUE" because --gc-sections is disabled when LLVM_NO_DEAD_STRIP isn't set, but this doesn't appear to be the the issue

  • I've also tried removing --indexstore-db --skip-test-indexstore-db but it doesn't make a difference, as expected

  • I've also tried, in conjunction with the above, pulling a page from docs/OpenBSD.md and adding --skip-build-clang-tools-extra --skip-build-compiler-rt --llvm-targets-to-build=host just in case it made a difference, but no dice

Is there a way to reliably build the Swift project on Linux with lld?

I'm not sure whether this is a supported configuration at all, or whether I should expect this to work. I could try somehow setting LLVM_USE_LINKER=lld for all dependencies other than LLVM itself, but I'm not sure that's a better solution than having consistent flags for compilation everywhere.

Are there any other flags or toggles I should be setting or trying to get things up and running? Thanks!

1 Like

Which version of Swift are you building? Swift 6 should work, I think, but 5.10 will be missing some important patches that are required to make this work seamlessly.

Sorry, I should've specified! This is Swift 5.10. Trying to build Swift 6 failed for me for a host of other reasons, but I was hoping to make both available. I can give Swift 6 another shot (though I was under the impression that Swift 6 doesn't support bootstrapping without having an existing Swift compiler).

If you're aware of the patches and would be able to link them, I can try to backport to 5.10 too!

we had a similar problem trying to port Swift to Amazon Linux 2023, and the only thing that really worked for us was symlinking ld.gold to ld.

The person to talk to is probably @etcwilde as he’s been working on exactly that kind of problem while bringing Swift up on some newer Linux distros.

I don't think 5.10 supports bootstrapping either, which is likely the main issue you're running into.

Since you say the prebuilt toolchain for Fedora works fine except for defaulting to the gold linker, I would download it and add it to your PATH before trying to build 5.10.1 from source, while removing the --bootstrapping=bootstrapping flag. That should kick in hosttools mode using the prebuilt compiler.

If you're having problems building LLVM with LLVM_USE_LINKER=lld, I'd stick with SWIFT_USE_LINKER=lld instead. You shouldn't need the nostart-stop-gc flags passed in, though you may need to pull in this patch to this single CMake file that I added after 5.10 branched.

Finally, SWIFT_USE_LINKER=lld only determines which linker is used to build a fresh Swift compiler. You will also have to change the default linker that the fresh compiler uses to lld or backport @etcwilde's recent pull not to have a default linker on Unix but to defer to clang.

In order to do this, I need to:

  1. Build the Swift project using lld on Linux in such a way that,
  2. The built Swift defaults to also using lld

1. Building with LLD:

I'm doing a 3-stage build and it seems to have worked out thus far. The first has bootstrapping and macros disabled since I don't have an initial Swift compiler.
There are a handful of variables, but it generally looks like this:

STAGE0_DIR=$BASE_INSTALL/swift-stage0-install
$SOURCES/swift/utils/build-script --release \
  --build-subdir=amazonlinux2023/stage0 \
  --install-destdir=$STAGE0_DIR \
  --skip-build-benchmarks --skip-test-cmark \
  --skip-early-swift-driver --skip-early-swiftsyntax \
  --bootstrapping=off \
  --llvm-targets-to-build=X86 \
  --libdispatch --foundation --swiftpm --xctest --llbuild --swift-driver \
  --extra-cmake-options='-DSWIFT_USE_LINKER=lld' \
  --install-all

Then since I have a Swift compiler, I can build the Swift bits of the compiler, so that is stage 1:

STAGE1_DIR=$BASE_INSTALL/swift-stage1-install
PATH="$STAGE0_DIR/usr/bin:$PATH" \
$SOURCES/swift/utils/build-script --release \
  --build-subdir=amazonlinux2023/stage1 \
  --install-destdir=$STAGE1_DIR \
  --skip-build-benchmarks --skip-test-cmark \
  --bootstrapping=off \
  --llvm-targets-to-build=X86 \
  --libdispatch --foundation --swiftpm --xctest --llbuild --swift-driver \
  --extra-cmake-options='-DSWIFT_USE_LINKER=lld' \
  --verbose-build \
  --install-all

And finally, we have a fully working Swift compiler with all the features, we can build the actual toolchain in the third stage:

STAGE2_DIR=$BASE_INSTALL/swift-stage2-install
PATH="$STAGE1_DIR/usr/bin:$PATH" \
$SOURCES/swift/utils/build-script \
  --preset=buildbot_linux,lld,no_test \
  build_subdir="amazonlinux2023/stage2" \
  installable_package=$STAGE2_DIR/x86_64-swift-5.10-amazonlinux-2023-notest.tar.gz \
  install_destdir=$STAGE2_DIR

I've got extra presets for pulling in lld in the full toolchain, so you'll need to add this to the utils/build-presets.ini file before running that stage2:

[preset: mixin_lld]
extra-cmake-options=
  -DSWIFT_USE_LINKER=lld

llvm-cmake-options=
  -DCLANG_DEFAULT_LINKER=lld

[preset: buildbot_linux,lld]
mixin-preset=
  buildbot_linux
  mixin_lld

[preset: buildbot_linux,lld,no_assertions]
mixin-preset=
  buildbot_linux,no_assertions
  mixin_lld

[preset: buildbot_linux,lld,no_test]
mixin-preset=
  buildbot_linux,no_test
  mixin_lld

[preset: buildbot_linux,lld,no_assertions,no_test]
mixin-preset=
  buildbot_linux,no_assertions,no_test
  mixin_lld

Then I do one more build, but sharing the build directory of the third-stage where I enable and run the tests.

PATH="$STAGE1_DIR/usr/bin:$PATH" \
$SOURCES/swift/utils/build-script \
  --preset=buildbot_linux,lld \
  build_subdir="amazonlinus2023/stage2" \
  installable_package=$STAGE2_DIR/x86_64-swift-5.10-amazonlinux-2023.tar.gz \
  install_destdir=$STAGE2_DIR 2>&1 | tee $SOURCES/build/amazonlinux2023/test.log

It'a definitely a process.
I don't know what the "assingment of LLVM_15 to symbol indexstore_blarpy failed: symbol not defined" is though.
I haven't run into that.

2. Changing the default linker of the produced toolchain

On 5.10, the default linker is hard-coded string in both drivers, so there's no knob to turn to change the default. To work around it, I'm currently just applying a patch that rips out all of the logic in defaultLinker and replaces it with this:

  private func defaultLinker(for targetTriple: Triple) -> String? {
    "lld"
  }

This ensures that the driver still automatically adds -Xlinker -z -Xlinker nostart-stop-gc so that we keep the missing symbols.

On main, I've removed all of the cleverness so that the swift-driver just uses the clang-linker to decide. This means that setting CLANG_DEFAULT_LINKER=lld during the toolchain build will mean that the produced toolchain will use lld as the default linker, both for C/C++ and Swift.

Another piece of good news, BFD (the normal ld on Linux) was patched so that it can handle relocating the protected symbols (the issue preventing using it in the first place). At the very least, AmazonLinux2023 has a new enough ld that it works out-of-the-box. I don't know which version Gentoo has, but if it's recent, you may not even need lld and clang will "just work".

3 Likes

Your stage 2 build also has --bootstrapping=off? Maybe it's getting overridden internally because of the presence of the stage 1 build, in which case, better not add that flag if only because it's confusing.

I've been seeing this lately when cross-compiling the Swift compiler for Android using lld from the Android NDK, worked around it here. I haven't looked into why it happens, likely tied to the lld version.

Thanks, all, for this info!

Hmm, based on this comment, and on @etcwilde's scripts, it sounds like I may be misunderstanding what --bootstrapping does in practice? I thought --bootstrapping=bootstrapping is supposed to build the compiler in multiple stages to allow it to automatically build in isolation without having on a pre-existing Swift toolchain, but maybe not? (i.e., based on swift/SwiftCompilerSources/README.md, it sounded like it automated the 3-stage build that @etcwilde proposes with a level-0, level-1, and final build, but I might be misunderstanding entirely)


@etcwilde, thanks so much for the detailed invocations! I'll give the manual multi-stage build a shot and see where it gets me.

Edit: to clarify, I'm trying this out first over either downloading a pre-built compiler depending on gold or symlinking ld.gold to ld.lld just because it feels like the failure modes of either of those solutions are more subtle/fragile than an "in-source" solution. I'll try them out for completeness, too.

Ack! I think I copied the wrong part of the script there.
That should be bootstrapping=hosttools. 5.10 didn't do anything clever there yet. The change to require bootstrapping landed in the 6.0 and main branches.

I've been seeing this lately when cross-compiling the Swift compiler for Android using lld from the Android NDK

Interesting. I wonder if those functions were removed or something and the symbol export list is out of sync with the library itself? If only I had a time machine to look into all the things.

Yes, it built the compiler multiple times, first without the portions of the Swift frontend written in Swift, then after building the Swift stdlib using that first stage, compiling the portions of the compiler written in Swift and linking those in too. It's relatively fast because it can keep reusing the built C++ components, just building and using more Swift components at each bootstrap stage. @etcwilde's alternative rebuilds everything, including LLVM, three times, which will take forever.

The problem is that the built-in bootstrapping of the compiler fell into disrepair once the CI all switched to using a prebuilt compiler, ie --bootstrapping=hosttools, which you do not even need to specify as it is the default on linux.

I strongly suggest you simply use that hosttools mode with the prebuilt compiler for Fedora as the hosttools. You have nothing to gain from a multi-stage build.

Yes, I think that's why, was just too lazy to confirm. :wink:

1 Like

it sounds like I may be misunderstanding what --bootstrapping does in practice?

Yeah, it's a tad complicated. The bootstrapping flag controls the swift repo itself, but not what gets built in the other parts of the toolchain, so if you only want to build the base compiler to bring up a limited toolchain, then you have to call build-script multiple times (The early-swiftsyntax and early-swift-driver pieces are separate from the bootstrapping flag, for instance).

If the fedora tools work, sure. I'm not going to say it's "supported" in any fashion, but neither are the patches against the driver, so it's up to you what you want to do there. I was using these scripts for bringing up the platforms, so I wanted to be absolutely sure that the stages weren't accidentally pulling pieces from eachother. It takes longer, but means I mostly know what is happening.

1 Like

Ah, that all makes sense. Thanks!

I'll give this a shot to be sure, but are the hosttools used to build the Swift portions of the compiler not also used to link them together? I assume not, for this to work?

They are, but passing in SWIFT_USE_LINKER=lld should make sure lld is used to link the freshly-built compiler, and patching the driver source for your freshly-built Swift compiler, in the places shown above, will make sure the freshly-built Swift compiler uses lld to build the rest of the toolchain.

I say "should" because I use lld all the time to build the toolchain for Android, but you'll notice the default linker for Android was already changed to lld years ago, in the driver source I linked. It's possible the CMake build scripts do not override the linker for linux properly somewhere, in which case that will need to be patched.

Hi folks, I have successfully built Swift for Amazon Linux 2023, which doesn't have the gold linker, so like other folks, I had to use lld unconditionally.

It seems patching the source was the easiest way to achieve this.

See my code (rpmspec and patches) at Tree - rpms/swift-lang - src.fedoraproject.org. The build log for Amazon Linux 2023 is here: https://download.copr.fedorainfracloud.org/results/leebc/swift-lang/amazonlinux-2023-x86_64/07726553-swift-lang/builder-live.log.gz

1 Like

Unfortunately, building Swift first checks if swiftc is valid, and bails because this swiftc can't actually compile a program (which is true):

-- The C compiler identification is Clang 15.0.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /home/itai/Development/swift-built/swift-5.10.1-RELEASE-fedora39/usr/bin/clang - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- The CXX compiler identification is Clang 15.0.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /home/itai/Development/swift-built/swift-5.10.1-RELEASE-fedora39/usr/bin/clang++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- The ASM compiler identification is Clang with GNU-like command-line
-- Found assembler: /home/itai/Development/swift-built/swift-5.10.1-RELEASE-fedora39/usr/bin/clang
-- The Swift compiler identification is Apple 5.10.1
-- Check for working Swift compiler: /home/itai/Development/swift-built/swift-5.10.1-RELEASE-fedora39/usr/bin/swiftc
-- Check for working Swift compiler: /home/itai/Development/swift-built/swift-5.10.1-RELEASE-fedora39/usr/bin/swiftc - broken
CMake Error at /usr/share/cmake/Modules/CMakeTestSwiftCompiler.cmake:40 (message):
  The Swift compiler

    "/home/itai/Development/swift-built/swift-5.10.1-RELEASE-fedora39/usr/bin/swiftc"

  is not able to compile a simple test program.

  It fails with the following output:

    Change Dir: '/home/itai/Development/swift-project/build/hosttools/swift-linux-x86_64/CMakeFiles/CMakeScratch/TryCompile-06ihz1'

    Run Build Command(s): /usr/bin/ninja -v cmTC_9ea18
    [1/1][100%][0.185s] : && /home/itai/Development/swift-built/swift-5.10.1-RELEASE-fedora39/usr/bin/swiftc -j 24 -num-threads 24 -emit-executable -o cmTC_9ea18 -emit-dependencies  -output-file-map CMakeFiles/cmTC_9ea18.dir//output-file-map.json  /home/itai/Development/swift-project/build/hosttools/swift-linux-x86_64/CMakeFiles/CMakeScratch/TryCompile-06ihz1/main.swift    && :
    FAILED: cmTC_9ea18 CMakeFiles/cmTC_9ea18.dir/main.swift.o
    : && /home/itai/Development/swift-built/swift-5.10.1-RELEASE-fedora39/usr/bin/swiftc -j 24 -num-threads 24 -emit-executable -o cmTC_9ea18 -emit-dependencies  -output-file-map CMakeFiles/cmTC_9ea18.dir//output-file-map.json  /home/itai/Development/swift-project/build/hosttools/swift-linux-x86_64/CMakeFiles/CMakeScratch/TryCompile-06ihz1/main.swift    && :
    error: link command failed with exit code 1 (use -v to see invocation)
    clang-15: error: invalid linker name in argument '-fuse-ld=gold'
    error: fatalError
    ninja: build stopped: subcommand failed.





  CMake will not be able to correctly generate this project.
Call Stack (most recent call first):
  CMakeLists.txt:103 (enable_language)


-- Configuring incomplete, errors occurred!

I'm not sure if it's possible to bypass this check, but if not, a multi-stage build may be my only option.

To build Swift on unsupported platforms, I believe that the best approach is to use an older version of Swift, which can be bootstrapped using C++ compilers. From what I understand, Swift 5.10+ requires a working Swift compiler to build, which is not possible on Gentoo based on your explanation.

Two options to work around it for now:

  1. Simply add a symlink from gold to lld in your prebuilt toolchain:
> ln -s lld /home/itai/Development/swift-built/swift-5.10.1-RELEASE-fedora39/usr/bin/ld.gold
  1. Passing in something like CMAKE_Swift_COMPILER_WORKS=true should disable that initial compiler check. That's fine because the Swift compiler build doesn't use CMake's built-in Swift support yet, still using its home-grown scripts.

Ah, of course. I'll give this another shot later today.

To close the loop here: I've managed to get a Swift toolchain built for Gentoo :tada:

  • Unfortunately, I never managed to get the prebuilt Fedora 39 toolchain working to build the compiler: linking lld as ld.gold got me compiling, but I'm still hitting undefined symbol issues due to GC. It appears that because the prebuilt toolchain was built with these symbols GC'd, we can't link to them:

    ld.lld: error: undefined hidden symbol: __start_swift5_protocols
    >>> referenced by SwiftRT-ELF-WASM.cpp
    >>>               /home/itai/Development/swift-built/swift-5.10.1-RELEASE-fedora39/usr/lib/swift/linux/x86_64/swiftrt.o:(swift_image_constructor())
    >>> the encapsulation symbol needs to be retained under --gc-sections properly; consider -z nostart-stop-gc (see https://lld.llvm.org/ELF/start-stop-gc)
    

    (This is with both --extra-swift-args and the patch to AddSwift.cmake to add -Xlinker -z -Xlinker nostart-stop-gc)

  • I did manage to get @etcwilde's multi-stage approach to work, and at that, I was able to get it working by symlinking some of the build products from one stage to the next (e.g., LLVM doesn't need to be rebuilt, so symlinking the build products saves a ton of time)

  • It took me so long to update here because I had to play whack-a-mole with actual linkage failures:

    1. Some of the build targets for, e.g., llbuild, attempt to link against curses explicitly β€” Gentoo uses ncurses with no backwards compatibility for symbols, so -lcurses fails. I had to replace all references to curses with explicit references to ncurses)
    2. It turns out that the version of ncurses for Gentoo also doesn't re-export some of the expected symbols that come from terminfo/tinfo, so I had to make sure that everywhere that ncurses was used, tinfo was being linked too

Now that I have this working, I'm going to try to pare back some of the clumsier changes I made to try to get things compiling and see if I can put together a minimal script to stitch everything together.

Thanks, all, for the help!