-
Notifications
You must be signed in to change notification settings - Fork 13.5k
rust: Import LLD for linking wasm objects #48125
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
cc @yurydelendik I think this may regress the source map support for wasm32-unknown-unknown as I'm not sure if lld has that implemented like binaryen does, but I could be wrong! |
// rethought from the ground up once a linker (lld) is available, so this is all | ||
// temporary and should improve in the future. | ||
// There's some trickery below on crate types supported and various defaults | ||
// (aka panic=abort by default), but otherwise this is ing eneral a relatively |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ing eneral
-> in general
?
Correct, it will regress the rust source maps support. I was planing to look into exporting source maps from LLVM next. |
ddf7169
to
8b11f93
Compare
Is that the case for all the targets or only for the wasm32-unknown-unknown target? Because I think it would be great to be able to link ARM Cortex-M binaries using the lld shipped with rustc; that way we don't need to install arm-none-eabi-ld on the host. |
@japaric it is indeed true for all targets! I haven't added support for LLD for other targets yet in this PR but I'm hopefully structuring it in such a way that it should be fairly easy. That I'm hoping can be a quick followup :) |
It's also important to note that this PR actually regresses the apparent hello size of "hello world" in wasm. Fear not, though, this is intended and not as bad as it seems! Today this program: #![crate_type = "cdylib"]
#[no_mangle]
pub extern fn foo(a: u32) -> u32 {
a + 1
} generates a 17k hello world with With this PR, however, the size of the first compilation is 633k (!) and with wasm-gc it only reduces to 209k (!). Not to fear, though, this is expected! This "bug" is due to the fact that the To recap, here's the size of the program above in various stages:
As always for the smallest binaries it's recommended to compile with |
@japaric hm actually I've been meaning to refactor this for quite some time now, so f359356 should do the trick. In theory, once this lands, you can simply specify (this will likely be buggy when it first starts out as it's never been used before!) |
@alexcrichton awesome! I've also confirmed that the version of LLD that ihis PR will ship can link ARM Cortex-M applications (older versions of LLD had problems with the linker scripts we use) |
With this PR, how is memory imported then? |
@badboy with LLD you've now got a whole slew of options for configuring memory and how everything gets linked, but I think you'd at least start with |
Thanks! |
☔ The latest upstream changes (presumably #48113) made this pull request unmergeable. Please resolve the merge conflicts. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this in general looks good to me but I'm not familiar with the linker bits in rustc_trans
and rustc_back
so might be good for someone else to review those. r=me if you feel good about them (they all look relatively fine to me).
.travis.yml
Outdated
@@ -81,7 +81,7 @@ matrix: | |||
# OSX 10.7 and `xcode7` is the latest Xcode able to compile LLVM for 10.7. | |||
- env: > | |||
RUST_CHECK_TARGET=dist | |||
RUST_CONFIGURE_ARGS="--build=i686-apple-darwin --enable-extended --enable-profiler --enable-emscripten" | |||
RUST_CONFIGURE_ARGS="--build=i686-apple-darwin --enable-tier1-tools --enable-profiler" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tier1-tools
is probably not quite the best name for this -- since you can enable it (presumably) on non-tier1 platforms, and the platforms being "tier1" really has nothing to do with it -- I'd prefer something like full-tools
or similar.
lld_install_root: &Path) { | ||
let target = target_compiler.host; | ||
|
||
let dst = builder.sysroot_libdir(target_compiler, target) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, so I feel like I'd sort of expect this to be in a non-target specific place -- that is, lld
would presumably work for both x86_64-unknown-linux-gnu
as much as wasm32-unknown-unknown
... I don't think we have a good place to put it (since the sysroot/bin
directory is probably a bad idea)... but something to think about. We may want to change this down the road to avoid duplicating it across the various platforms that will probably want to default to lld down the road.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah so this isn't necessarily target-specific, it's also where we put the codegen backends for example. The target in the directory name is basically "things in here are only relevant for this target", which for the case of binaries just means that platform the binaries run on. We'll use the lld
executable here (by placing it in PATH
) regardless of the actual target of the compiler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, okay. Seems slightly odd, since presumably that means that we'd have to have the rustlib for, say, x86_64-unknown-linux-gnu installed if that's where we ship lld even if you want to use lld for some windows target. But maybe I'm creating problems where none exist.
fn finalize(&mut self) -> Command { | ||
self.cmd.arg("--threads"); | ||
|
||
// FIXME we probably shouldn't pass this but instead pass an explicit |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe worth opening an issue about this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Certainly!
I find this somewhat suspicious. Did LLD tip regress? It looks like it gc's less out of the wasm files... |
@Mark-Simulacrum heh I had to double check as well. LLD didn't regress per se but it did emit a few more annotations/exports for things I didn't quite recognize (but seemed injected in LLVM/LLD/etc). In general LLD-for-wasm I believe is still early-days, so I suspect that this'll get smoothed out over time! |
Any schedule to turn on lld by default on Tier-2 platform? |
[Oops, wrong thread...] |
Use lld by default on `x86_64-unknown-linux-gnu` stable This PR and stabilization report is joint work with `@Kobzol.` #### Use LLD on `x86_64-unknown-linux-gnu` by default, and stabilize `-Clinker-features=-lld` and `-Clink-self-contained=-linker` This PR proposes making LLD the default linker on the `x86_64-unknown-linux-gnu` target for the artifacts we distribute, and also stabilizing the `-Clinker-features=-lld` and `-Clink-self-contained=-linker` codegen options to make it possible to opt out. LLD has been used as the default linker on nightly and CI on this target since May 2024 ([PR](#124129), [blog post](https://blog.rust-lang.org/2024/05/17/enabling-rust-lld-on-linux.html)), and it seems like it is working fine, so we would like to propose stabilizing it. The main motivation for using LLD instead of the default BFD linker is improving [compilation times](https://perf.rust-lang.org/compare.html?start=b3e117044c7f707293edc040edb93e7ec5f7040a&end=baed03c51a68376c1789cc373581eea0daf89967&stat=instructions%3Au&tab=compile). For example, in the linked benchmark, it makes incremental recompilation of `ripgrep` in `debug` more than twice faster. Another benefit is that Rust compilation becomes more consistent and self-contained, because we will use a known version of the LLD linker, rather than "whatever GNU ld version is on the user's system". Due to the performance benefit being so huge, many people already opt into using LLD (or other fast linkers, such as mold) using various approaches ([1](https://github.com/search?type=code&q=%2Flinker-flavor%5B%3D+%5Dgnu-lld-cc%2F), [2](https://github.com/search?type=code&q=%2Flinker-features%5B%3D+%5D%5C%2Blld%2F), [3](https://github.com/search?type=code&q=language%3Atoml+%22-fuse-ld%3Dlld%22), [4](https://github.com/search?type=code&q=language%3Arust+%22-fuse-ld%3Dlld%22)). By making LLD the default linker on the `x86_64-unknown-linux-gnu` target, we will be able to speed up Rust compilation out of the box, without users having to opt in or know about it. > You can find an extended version of this stabilization report which includes analysis of crater results and more data [here](https://hackmd.io/tFDifkUcSLGoHPBRIl0z8w?view). ## What is being stabilized - `rust-lld` being used as the default linker on the `x86_64-unknown-linux-gnu` target. - Note that `rust-lld` is being enabled by default in the compiler artifacts distributed by our CI/rustup. It is still possible to use the system linker by default using `rust.lld = false` in `bootstrap.toml`, which can be useful e.g. for some Linux distros that might not want to use the LLD we distribute. - This is done by activating the LLD linker feature and using the self-contained linker on that target. Both of which are also usable on the CLI, if some opt outs are necessary, as described below. - `-Clinker-features=-lld` on the `x86_64-unknown-linux-gnu` target. This codegen option tells rustc to disable using the LLD linker. - Note that other options for this codegen flag (`cc`) remain unstable. - Note that only the opt-out is being stabilized, and only for `x86_64-unknown-linux-gnu`: opting in, or using the flag on other targets would still need to pass `-Zunstable-options`. - This flag is being stabilized so that users can opt out of LLD on stable, which would it turn also opt out of using the self-contained linker (since it's an LLD). - `-Clink-self-contained=-linker`. This codegen option tells rustc to use the self-contained linker. It's not particularly useful to turn it on by itself, but when enabled and combined with `-Clinker-features=+lld`, rustc will use the `rust-lld` linker wrapper shipped with the compiler toolchain, instead of some `lld` binary that the linker driver will find in the `PATH`. - Note that other options for this codegen flag (other than the previously stable `y/yes/n/no`). - Note that only the opt-out is being stabilized, and only for `x86_64-unknown-linux-gnu`: opting in, or using this flag on other targets would still need to pass `-Zunstable-options`. - This flag is being stabilized so that users can opt out of using self-contained linking on stable. Doing this would then fall back to using the system `lld`. To opt out of using LLD, `RUSTFLAGS="-Clinker-features=-lld"` would be used. To opt out of using `rust-lld`, falling back to the LLD installed on the system, `RUSTFLAGS="-Clink-self-contained=-linker"` would be used. ## Tests When enabling `rust-lld` on nightly, we also switched x64 linux to use it at stage >= 1, meaning that all tests have been running with lld since May 2024, on CI as well as contributors' machines. (Post opt-dist tests also had been using it when running their test subset earlier than that). There are also a few tests dedicated to the CLI behavior, or ensuring the default linker is indeed the one we expect: - [link-self-contained-consistency](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/ui/linking/link-self-contained-consistency.rs): Checks that `-Clink-self-contained` options are not inconsistent (i.e. that passing both `+linker` and `-linker` is an error). - [link-self-contained-unstable](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/ui/linking/link-self-contained-unstable.rs): Checks that only the `-linker` and `y/yes/n/no` options for `-Clink-self-contained` are stable. - [linker-features-unstable-cc](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/ui/linking/linker-features-unstable-cc.rs): Checks that only the non-lld options of `-Clinker-features` are unstable. - [linker-features-lld-disallowed](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/ui/linking/linker-features-lld-disallowed.rs): Checks that `-Clinker-features=-lld` is only stable on `x86_64-unknown-linux-gnu`. - [link-self-contained-linker-disallowed](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/ui/linking/link-self-contained-linker-disallowed.rs): Checks that `-Clink-self-contained=-linker` is only stable on `x86_64-unknown-linux-gnu`. - [no-gc-encapsulation-symbols](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/ui/linking/no-gc-encapsulation-symbols.rs): Checks that that linker encapsulation symbols are not garbage collected by LLD, so that crates like [linkme](https://github.com/dtolnay/linkme) still work. - [rust-lld](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/run-make/rust-lld): Checks that LLD is actually used when enabled with `-Clinker-features=+lld` and `-Clink-self-contained=+linker`. - [rust-lld-x86_64-unknown-linux-gnu](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/run-make/rust-lld-x86_64-unknown-linux-gnu): Checks that LLD is used by default on `x86_64-unknown-linux-gnu` when the bootstrap `rust.lld` config option is `true`. - [rust-lld-x86_64-unknown-linux-gnu-dist](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/run-make/rust-lld-x86_64-unknown-linux-gnu-dist): Dist test that checks that our distributed `x86_64-unknown-linux-gnu` archives actually use LLD by default. ## Ecosystem impact As already stated, LLD has been used as the default linker on x64 Linux on nightly for almost a year, and we haven't seen any blockers to stabilization in that time. There were a handful of issues reported, these are discussed later below. Furthermore, two crater runs ([November 2023](https://crater-reports.s3.amazonaws.com/pr-117684-2/index.html), [February 2025](https://crater-reports.s3.amazonaws.com/pr-137044-3/index.html)), were performed to test the impact of using LLD as the default linker. A triage of the earlier crater run was previously done [here](https://hackmd.io/OAJxlxc6Te6YUot9ftYSKQ), but all the important findings from both crater runs are reported below. Below is a list of compatibility differences between BFD and LLD that we have encountered. There is a more thorough list of differences in [this post](https://maskray.me/blog/2020-12-19-lld-and-gnu-linker-incompatibilities) from the current LLD maintainer. From that post, "99.9% pieces of software work with ld.lld without a change". --- ### `.ctors/.dtors` sections [#128286](#128286) reported an issue where LLD was unable to link certain CUDA library was using these sections that were using the `.ctors/.dtors` ELF sections. These were deprecated a long time ago (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=46770), replaced with a more modern `.init_array/.fini_array` sections. LLD doesn't (and won't) support these sections ([1](llvm/llvm-project#68071), [2](llvm/llvm-project#30572)), so if they appear in input object files, the linked artifact might produce incorrect behavior, because e.g. some global variables might not get initialized properly. However, the usage of `.ctors/.dtors` should be very rare in practice. We have performed a [crater run](#137044) to test this. It has identified only 8 crates where the `.ctors/.dtors` section is occurring in the final linked artifact. It was caused by a few crates using the `.ctors` link section manually, and by using a very (~6 year) old version of the [ctor](https://crates.io/crates/ctor) crate. [Crater run analysis](https://hackmd.io/tFDifkUcSLGoHPBRIl0z8w?view#ctorsdtors-sections) **Possible workaround** It is possible to [detect](e5e2316) if `.ctors/.dtors` section is present in the final linked artifact (LLD will keep it there, but it won't be populated), and warn users about it. This check is very cheap and doesn't even appear on [perf](#112049 (comment)). We have benchmarked the check on a 240 MiB Chrome binary, where it took 0.8ms with page cache flushed, and 0.06ms with page cache primed (which should be the common case, as the linked artifact is written to disk just before the check is performed). In theory, this could be also solved with a linker script that moves `.ctors` to `.init_array`. We think that these sections should be so rare that it is not worth it to implement any workarounds for now. --- ### Different garbage collection behavior [#130397](#130397) reported an issue where LLD prunes a local symbol, so it is missing in the linked artifact. However, BFD keeps the same symbol, so it is a regression. This is caused by a difference in linker garbage collection. Rust uses `--gc-sections` and puts each function into a separate linker section, which prunes unused code. There is some code (specifically the somewhat popular [linkme](https://github.com/dtolnay/linkme) crate) that (arguably ab-)uses so called linker encapsulation symbols to achieve distributed slices. BFD (2.37+) uses a conservative linking mode that works as intended with this behavior, but it might slightly increase binary size of the linked artifact. LLD does not use this workaround by default, which causes the sections to be eliminated, but it can be made to use the conservative mode using [`-z nostart-stop-gc`](https://lld.llvm.org/ELF/start-stop-gc.html#z-start-stop-gc). To avoid this issue, we told LLD to use the [conservative mode](#137685), which maintains backwards compatibility with BFD. We found that it has [no effect](#112049 (comment)) on compilation performance and binary size in our benchmark suite. With this change, `linkme` works. Since then, #140872 removed `linkme` distributed slice's dependence on conservative GC behavior, so this PR also removes that conservative mode: no transition period is necessary, as the PR immediately fixed the crate with no source changes. [Crater run analysis](https://hackmd.io/tFDifkUcSLGoHPBRIl0z8w?view#Different-garbage-collection-behavior) --- ### Various uncommon issues A small number of issues that only occurred in a handful of instances were found in crater, and it is unclear if LLD is at fault or if there is some other issue that was not detected with BFD. You can examine these [here](https://hackmd.io/tFDifkUcSLGoHPBRIl0z8w?view#Various-uncommon-issues). --- ### Missing jobserver support LLD doesn't support the jobserver protocol for limiting the number of threads used, it simply defaults to using all available cores, and is one of the reasons why it's faster than BFD. However, this should mostly be a non-issue, because most of the linking done during high parallelism sections of `cargo build` is linking of build scripts and proc macros, which are typically very fast to link (e.g. ~50ms), and a potential oversubscription of cores thus doesn't hurt that much. When the final artifact is linked (which typically takes the most time), there should be no other sources of parallelism conflicts from compiling other code, so LLD should be able to use all available threads. That being said, it is a difference of behavior, where previously a `-j` flag was generally not using more cpu than the specified limit. It can be impactful in some resource-constrained systems, but to be clear that is already the case today due to [cargo parallelism](rust-lang/cargo#9157). This could be one reason to opt out of using `rust-lld` on some systems. LLD has support for limiting the number of threads to use, so in theory rustc could try to get all the jobserver tokens available and use that as lld's thread limit. It'd still be suboptimal as new tokens would not be dynamically detected, and we could be using less threads than available. We did a benchmark on a real-world crate that shows that using multiple LLD threads for intermediate artifacts doesn't seem to have a performance effect. You can find it [here](https://hackmd.io/tFDifkUcSLGoHPBRIl0z8w?view#Missing-jobserver-support). --- #### Opting out of LLD in the ecosystem We have also examined repositories where people opted out of LLD on nightly, using [this GitHub query](https://github.com/search?q=%22linker-features%3D-lld%22&type=code). The summary can be found below: <details> <summary>Summary of LLD opt outs</summary> > This examination was performed on 2025-03-09. Here we briefly examine the most common reasons why people use `-Zlinker-features=-lld`, based on comments and git history. - Nix/NixOS ([1](https://github.com/rszyma/vscode-kanata/blob/59d703dff5a238b14ab3759cac27f73fb34bbcfe/flake.nix#L33), [2](https://github.com/sbernauer/breakwater/blob/3cc3449fc126c5c99d4a971733fd32be589884e0/.cargo/config.toml#L4), [3](https://github.com/tiiuae/ebpf-firewall/blame/32bdb17cedd1c9bea1ab3482623de458d95da7d0/.cargo/config.toml#L2), [4](https://github.com/jules-sommer/wavetheme-gen/blob/f5f657d014d4a30607625afb70f810c229c0294e/Cargo.toml#L4), [5](https://github.com/LayerTwo-Labs/zside-rust/blob/e4266f5c5571a1b180a9c70cf0939c7070e410c7/.cargo/config.toml#L10), [6](https://github.com/przyjacielpkp/zkml/blob/22a4aef24e9d2c77789229d7c634fc67e9eb1184/README.md?plain=1#L78), [7](https://github.com/LayerTwo-Labs/thunder-rust/blob/2222d53474c8d2d0428b4c56f8157095dced6d5a/.cargo/config.toml#L2), [8](https://github.com/enesoztrk/nixos-tc-aya-test/blob/b2ffa59d3eba8b60fd04b0a4c8bbe047400eb981/.cargo/config.toml#L4), [9](https://github.com/lowRISC/container-hotplug/blob/3ead4ef9c7f79c303392178c99677dbecff1aea6/.cargo/config.toml#L2), [10](https://github.com/Eliah-Lakhin/ad-astra/blob/ca6b8c8a5dba7bb5e894f3f1013f17876962a021/work/examples/lsp-client/src/extension.ts#L94)) - There was an [issue](NixOS/nixpkgs#312661) with LLD, which seems to have been fixed with NixOS/nixpkgs#314268. It's unclear whether that fixed all the Nix issues though. - Issues with linkme ([1](https://github.com/0xPolygonZero/zk_evm/blob/ef388619ffbd5305209519a3a5bc0396185d68ac/.cargo/config.toml#L4), [2](https://github.com/conjure-cp/conjure-oxide/blob/be0fc5827ff90e8486d416cc184b6ce24f73bf01/README.md?plain=1#L20), [3](https://github.com/clchiou/garage/blob/c5d8444d56bb6ee24ca95e5c6b9880ed996f4918/rust/.cargo/config.toml#L6), [4](https://github.com/PonasKovas/craftflow/blob/5b4cc1a5196e08a975368399fefda4b71f3a2f6f/.cargo/config.toml#L3), [5](https://github.com/kezhuw/zookeeper-client-rust/blob/4e27c7de2a7cc5e709af012b791c8fea9bb47f1f/.github/workflows/ci.yml#L82), [6](https://github.com/niklasdewally/conjure-oxide/blob/8fe60c12bca7011a2f9eded4b7c95ad0e77b6f44/.github/workflows/code-coverage.yml#L48), [7](https://github.com/kezhuw/spawns/blob/c8b468379805de9df3287c01b94b4ed3db6b61ed/.github/workflows/ci.yml#L74)) - These should be resolved with the conservative garbage collection ([#137685](#137685)). - Bazel ([1](https://github.com/google-parfait/confidential-federated-compute/blob/1823f69ed8f5f4f819f7bfa21da1ca629fdc826b/.bazelrc#L71)), WASM ([1](https://github.com/Eliah-Lakhin/ad-astra/blob/ca6b8c8a5dba7bb5e894f3f1013f17876962a021/work/examples/wasm-build.sh#L37), [2](https://github.com/yacineb/pgrx-wasi-test/blob/2bf99037ca1b650b2cbc35f1257a87fb6ead0920/build.sh#L21)), uncategorized ([2](https://github.com/nbdd0121/r2vm/blob/5118be6b9e757c6fef2f019385873f403c23c548/.cargo/config.toml#L3), [3](https://github.com/Wyvern/Img/blame/45020c7e1dc4926c8129647014c708db0c13f463/.cargo/config.toml#L209), [4](https://github.com/arnaudpoullet/leptos-i18n-compile-error/blob/042eb835f7ca0dc36be67cf7fe65b35b22b6059f/README.md?plain=1#L89), [5](https://github.com/JonLeeCon/numerical-rust-cpu/blob/fd0b3006768ed81c56147044dc05c92b11b7b6f0/exercises/.cargo/config.toml#L13), [6](https://github.com/PonasKovas/shallowclone/blob/be65f2ec923cac6ceedbc8db520c89969ebfce7c/.github/workflows/rust.yml#L20)) - Reason unclear. </details> ## History The idea to use a faster linker by default has been on the radar for quite some time ([#39915](#39915), [#71515](#71515)). There were [very early attempts](#29974) to use the gold linker by default, but these had to be [reverted](#30913) because of compatibility issues. Support for LLD was implemented back in [2017](#40018), but it has not been made default yet, except for some more niche targets, such as [WASM](#48125), [ARM Cortex](#53648) or [RISC-V](#53822). It took quite some time to figure out how should the interface for selecting the linker (and the way it is invoked) look like, as it differs a lot between different platforms, linkers and compiler drivers. During that time, LLD has matured and achieved [almost perfect compatibility](https://maskray.me/blog/2020-12-19-lld-and-gnu-linker-incompatibilities) with the default Linux linker (BFD). - [#56351](#56351) stabilized `-Clinker-flavor`, which is used to determine how to invoke the linker. It is especially useful on targets where selecting the linker directly with `-Clinker` is not possible or is impractical. - December 2018, author `@davidtwco,` reviewer `@nagisa` - [#76158](#76158) stabilized `-Clink-self-contained=[y|n]`, which allows overriding the compiler's heuristic for deciding whether it should use self-contained or external tools (linker, sanitizers, libc, etc.). It only allowed using the self-contained mode either for everything (`y`) or nothing (`n`), but did not allow granular choice. - September 2020, author `@mati864,` reviewer `@petrochenkov` - [#85961](#85961) implemented the `-Zgcc-ld` flag, which was a hacky way of opting into LLD usage. - June 2021, author `@sledgehammervampire,` reviewer `@petrochenkov` - [MCP 510](rust-lang/compiler-team#510) proposed stabilizing the behavior of `-Zgcc-ld` using more granular flags (`-Clink-self-contained=linker -Clinker-flavor=gcc-lld`). - Initially implemented in [#96827](#96827), but `@petrochenkov` [suggested](#96827 (comment)) a slightly different approach. - The PR was split into [#96884](#96884), where it was decided what will be the individual components of `-Clink-self-contained=linker`. - And [#96401](#96401), which implemented the `-Clinker-flavor` part. - The MCP was finally implemented in [#112910](#112910). - [#116514](#116514) then removed `-Zgcc-ld`, as it was replaced by `-Clinker-flavor=gnu-lld-cc` + `-Clink-self-contained=linker`. - April 2022 - October 2023, author `@lqd,` reviewer `@petrochenkov` - Various linker handling refactorings were performed in the meantime: [#97375](#97375), [#98212](#98212), [#100126](#100126), [#100552](#100552), [#102836](#102836), [#110807](#110807), [#101988](#101988), [#116515](#116515) - The implementation of linker flavors with LLD was causing a sort of a combinatorial explosion of various options. [#119906](#119906) suggested a different approach for linker flavors (described [here](#119906 (comment))), where the individual flavors could be enabled separately using `+/-` (e.g. `+lld`). - After some back and forth, this idea was moved to `-Clinker-features` (see [comment 1](#119906 (comment)) and [comment 2](#119906 (comment))), which was implemented in [#123656](#123656). - April 2024, author `@lqd,` reviewer `@petrochenkov` - [#124129](#124129) enabled LLD by default on nightly. - April 2024, author `@lqd,` reviewer `@petrochenkov` - [#137685](#137685), [#137926](#137926) enabled the conservative gargage collection mode (`-znostart-stop-gc`) to improve compatibility with BFD. - February 2025, author `@lqd,` reviewer `@petrochenkov` (implementation), author `@kobzol,` reviewer `@lqd` (test) - [#96025](#96025) (April 2022), [#117684](#117684) (November 2023), [#137044](#137044) (February 2025): crater runs. ## Unresolved questions/concerns - Is changing the linker considered a breaking change? In (hopefully very rare) cases, it might break some existing code. It should mostly only affect the final linked artifact, so it should be easy to opt out. - Similarly, is the single-threaded behavior of such tools encompassed in our stability guarantee: it can be observed via the `-j` job limit (though I believe we have/had some open issues on sometimes using more CPU resources than the job count limit implied). As mentioned above, LLD does not support the jobserver protocol. - A concern [was raised](#71515 (comment)) about increased memory usage of LLD. We should probably let users know about the possibly increased memory usage, and jobserver incompatibility: we did so when announcing this landing on nightly. - LLD seems to produce [slightly larger](https://perf.rust-lang.org/compare.html?start=b3e117044c7f707293edc040edb93e7ec5f7040a&end=baed03c51a68376c1789cc373581eea0daf89967&stat=size%3Alinked_artifact&tab=compile) binary artifacts. This can be partially clawed back using Identical Code Folding (`-Clink-args=-Wl,--icf=all`). - Should we detect the outdated `.ctors/.dtors` sections to provide a better error message, even if that should be rare in practice? --- ### Next steps After the FCP completes: - we should land this PR at the beginning of a beta cycle, to maximize time for testing - keep an eye on the beta crater run results for possible linker issues (or do a dedicated beta crater run with only this change) - release a blog post announcing the change, and asking for testing feedback of the appropriate beta - depending on feedback, or if a period of testing of 6 weeks is not long enough, we could keep this change on beta for another cycle --- Development, testing, try builds were done in #138645. r? `@petrochenkov` `@rustbot` label +needs-fcp +T-compiler try-job: aarch64-gnu try-job: i686-gnu-*
Use lld by default on `x86_64-unknown-linux-gnu` stable This PR and stabilization report is joint work with `@Kobzol.` #### Use LLD on `x86_64-unknown-linux-gnu` by default, and stabilize `-Clinker-features=-lld` and `-Clink-self-contained=-linker` This PR proposes making LLD the default linker on the `x86_64-unknown-linux-gnu` target for the artifacts we distribute, and also stabilizing the `-Clinker-features=-lld` and `-Clink-self-contained=-linker` codegen options to make it possible to opt out. LLD has been used as the default linker on nightly and CI on this target since May 2024 ([PR](#124129), [blog post](https://blog.rust-lang.org/2024/05/17/enabling-rust-lld-on-linux.html)), and it seems like it is working fine, so we would like to propose stabilizing it. The main motivation for using LLD instead of the default BFD linker is improving [compilation times](https://perf.rust-lang.org/compare.html?start=b3e117044c7f707293edc040edb93e7ec5f7040a&end=baed03c51a68376c1789cc373581eea0daf89967&stat=instructions%3Au&tab=compile). For example, in the linked benchmark, it makes incremental recompilation of `ripgrep` in `debug` more than twice faster. Another benefit is that Rust compilation becomes more consistent and self-contained, because we will use a known version of the LLD linker, rather than "whatever GNU ld version is on the user's system". Due to the performance benefit being so huge, many people already opt into using LLD (or other fast linkers, such as mold) using various approaches ([1](https://github.com/search?type=code&q=%2Flinker-flavor%5B%3D+%5Dgnu-lld-cc%2F), [2](https://github.com/search?type=code&q=%2Flinker-features%5B%3D+%5D%5C%2Blld%2F), [3](https://github.com/search?type=code&q=language%3Atoml+%22-fuse-ld%3Dlld%22), [4](https://github.com/search?type=code&q=language%3Arust+%22-fuse-ld%3Dlld%22)). By making LLD the default linker on the `x86_64-unknown-linux-gnu` target, we will be able to speed up Rust compilation out of the box, without users having to opt in or know about it. > You can find an extended version of this stabilization report which includes analysis of crater results and more data [here](https://hackmd.io/tFDifkUcSLGoHPBRIl0z8w?view). ## What is being stabilized - `rust-lld` being used as the default linker on the `x86_64-unknown-linux-gnu` target. - Note that `rust-lld` is being enabled by default in the compiler artifacts distributed by our CI/rustup. It is still possible to use the system linker by default using `rust.lld = false` in `bootstrap.toml`, which can be useful e.g. for some Linux distros that might not want to use the LLD we distribute. - This is done by activating the LLD linker feature and using the self-contained linker on that target. Both of which are also usable on the CLI, if some opt outs are necessary, as described below. - `-Clinker-features=-lld` on the `x86_64-unknown-linux-gnu` target. This codegen option tells rustc to disable using the LLD linker. - Note that other options for this codegen flag (`cc`) remain unstable. - Note that only the opt-out is being stabilized, and only for `x86_64-unknown-linux-gnu`: opting in, or using the flag on other targets would still need to pass `-Zunstable-options`. - This flag is being stabilized so that users can opt out of LLD on stable, which would it turn also opt out of using the self-contained linker (since it's an LLD). - `-Clink-self-contained=-linker`. This codegen option tells rustc to use the self-contained linker. It's not particularly useful to turn it on by itself, but when enabled and combined with `-Clinker-features=+lld`, rustc will use the `rust-lld` linker wrapper shipped with the compiler toolchain, instead of some `lld` binary that the linker driver will find in the `PATH`. - Note that other options for this codegen flag (other than the previously stable `y/yes/n/no`). - Note that only the opt-out is being stabilized, and only for `x86_64-unknown-linux-gnu`: opting in, or using this flag on other targets would still need to pass `-Zunstable-options`. - This flag is being stabilized so that users can opt out of using self-contained linking on stable. Doing this would then fall back to using the system `lld`. To opt out of using LLD, `RUSTFLAGS="-Clinker-features=-lld"` would be used. To opt out of using `rust-lld`, falling back to the LLD installed on the system, `RUSTFLAGS="-Clink-self-contained=-linker"` would be used. ## Tests When enabling `rust-lld` on nightly, we also switched x64 linux to use it at stage >= 1, meaning that all tests have been running with lld since May 2024, on CI as well as contributors' machines. (Post opt-dist tests also had been using it when running their test subset earlier than that). There are also a few tests dedicated to the CLI behavior, or ensuring the default linker is indeed the one we expect: - [link-self-contained-consistency](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/ui/linking/link-self-contained-consistency.rs): Checks that `-Clink-self-contained` options are not inconsistent (i.e. that passing both `+linker` and `-linker` is an error). - [link-self-contained-unstable](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/ui/linking/link-self-contained-unstable.rs): Checks that only the `-linker` and `y/yes/n/no` options for `-Clink-self-contained` are stable. - [linker-features-unstable-cc](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/ui/linking/linker-features-unstable-cc.rs): Checks that only the non-lld options of `-Clinker-features` are unstable. - [linker-features-lld-disallowed](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/ui/linking/linker-features-lld-disallowed.rs): Checks that `-Clinker-features=-lld` is only stable on `x86_64-unknown-linux-gnu`. - [link-self-contained-linker-disallowed](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/ui/linking/link-self-contained-linker-disallowed.rs): Checks that `-Clink-self-contained=-linker` is only stable on `x86_64-unknown-linux-gnu`. - [no-gc-encapsulation-symbols](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/ui/linking/no-gc-encapsulation-symbols.rs): Checks that that linker encapsulation symbols are not garbage collected by LLD, so that crates like [linkme](https://github.com/dtolnay/linkme) still work. - [rust-lld](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/run-make/rust-lld): Checks that LLD is actually used when enabled with `-Clinker-features=+lld` and `-Clink-self-contained=+linker`. - [rust-lld-x86_64-unknown-linux-gnu](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/run-make/rust-lld-x86_64-unknown-linux-gnu): Checks that LLD is used by default on `x86_64-unknown-linux-gnu` when the bootstrap `rust.lld` config option is `true`. - [rust-lld-x86_64-unknown-linux-gnu-dist](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/run-make/rust-lld-x86_64-unknown-linux-gnu-dist): Dist test that checks that our distributed `x86_64-unknown-linux-gnu` archives actually use LLD by default. ## Ecosystem impact As already stated, LLD has been used as the default linker on x64 Linux on nightly for almost a year, and we haven't seen any blockers to stabilization in that time. There were a handful of issues reported, these are discussed later below. Furthermore, two crater runs ([November 2023](https://crater-reports.s3.amazonaws.com/pr-117684-2/index.html), [February 2025](https://crater-reports.s3.amazonaws.com/pr-137044-3/index.html)), were performed to test the impact of using LLD as the default linker. A triage of the earlier crater run was previously done [here](https://hackmd.io/OAJxlxc6Te6YUot9ftYSKQ), but all the important findings from both crater runs are reported below. Below is a list of compatibility differences between BFD and LLD that we have encountered. There is a more thorough list of differences in [this post](https://maskray.me/blog/2020-12-19-lld-and-gnu-linker-incompatibilities) from the current LLD maintainer. From that post, "99.9% pieces of software work with ld.lld without a change". --- ### `.ctors/.dtors` sections [#128286](#128286) reported an issue where LLD was unable to link certain CUDA library was using these sections that were using the `.ctors/.dtors` ELF sections. These were deprecated a long time ago (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=46770), replaced with a more modern `.init_array/.fini_array` sections. LLD doesn't (and won't) support these sections ([1](llvm/llvm-project#68071), [2](llvm/llvm-project#30572)), so if they appear in input object files, the linked artifact might produce incorrect behavior, because e.g. some global variables might not get initialized properly. However, the usage of `.ctors/.dtors` should be very rare in practice. We have performed a [crater run](#137044) to test this. It has identified only 8 crates where the `.ctors/.dtors` section is occurring in the final linked artifact. It was caused by a few crates using the `.ctors` link section manually, and by using a very (~6 year) old version of the [ctor](https://crates.io/crates/ctor) crate. [Crater run analysis](https://hackmd.io/tFDifkUcSLGoHPBRIl0z8w?view#ctorsdtors-sections) **Possible workaround** It is possible to [detect](e5e2316) if `.ctors/.dtors` section is present in the final linked artifact (LLD will keep it there, but it won't be populated), and warn users about it. This check is very cheap and doesn't even appear on [perf](#112049 (comment)). We have benchmarked the check on a 240 MiB Chrome binary, where it took 0.8ms with page cache flushed, and 0.06ms with page cache primed (which should be the common case, as the linked artifact is written to disk just before the check is performed). In theory, this could be also solved with a linker script that moves `.ctors` to `.init_array`. We think that these sections should be so rare that it is not worth it to implement any workarounds for now. --- ### Different garbage collection behavior [#130397](#130397) reported an issue where LLD prunes a local symbol, so it is missing in the linked artifact. However, BFD keeps the same symbol, so it is a regression. This is caused by a difference in linker garbage collection. Rust uses `--gc-sections` and puts each function into a separate linker section, which prunes unused code. There is some code (specifically the somewhat popular [linkme](https://github.com/dtolnay/linkme) crate) that (arguably ab-)uses so called linker encapsulation symbols to achieve distributed slices. BFD (2.37+) uses a conservative linking mode that works as intended with this behavior, but it might slightly increase binary size of the linked artifact. LLD does not use this workaround by default, which causes the sections to be eliminated, but it can be made to use the conservative mode using [`-z nostart-stop-gc`](https://lld.llvm.org/ELF/start-stop-gc.html#z-start-stop-gc). To avoid this issue, we told LLD to use the [conservative mode](#137685), which maintains backwards compatibility with BFD. We found that it has [no effect](#112049 (comment)) on compilation performance and binary size in our benchmark suite. With this change, `linkme` works. Since then, #140872 removed `linkme` distributed slice's dependence on conservative GC behavior, so this PR also removes that conservative mode: no transition period is necessary, as the PR immediately fixed the crate with no source changes. [Crater run analysis](https://hackmd.io/tFDifkUcSLGoHPBRIl0z8w?view#Different-garbage-collection-behavior) --- ### Various uncommon issues A small number of issues that only occurred in a handful of instances were found in crater, and it is unclear if LLD is at fault or if there is some other issue that was not detected with BFD. You can examine these [here](https://hackmd.io/tFDifkUcSLGoHPBRIl0z8w?view#Various-uncommon-issues). --- ### Missing jobserver support LLD doesn't support the jobserver protocol for limiting the number of threads used, it simply defaults to using all available cores, and is one of the reasons why it's faster than BFD. However, this should mostly be a non-issue, because most of the linking done during high parallelism sections of `cargo build` is linking of build scripts and proc macros, which are typically very fast to link (e.g. ~50ms), and a potential oversubscription of cores thus doesn't hurt that much. When the final artifact is linked (which typically takes the most time), there should be no other sources of parallelism conflicts from compiling other code, so LLD should be able to use all available threads. That being said, it is a difference of behavior, where previously a `-j` flag was generally not using more cpu than the specified limit. It can be impactful in some resource-constrained systems, but to be clear that is already the case today due to [cargo parallelism](rust-lang/cargo#9157). This could be one reason to opt out of using `rust-lld` on some systems. LLD has support for limiting the number of threads to use, so in theory rustc could try to get all the jobserver tokens available and use that as lld's thread limit. It'd still be suboptimal as new tokens would not be dynamically detected, and we could be using less threads than available. We did a benchmark on a real-world crate that shows that using multiple LLD threads for intermediate artifacts doesn't seem to have a performance effect. You can find it [here](https://hackmd.io/tFDifkUcSLGoHPBRIl0z8w?view#Missing-jobserver-support). --- #### Opting out of LLD in the ecosystem We have also examined repositories where people opted out of LLD on nightly, using [this GitHub query](https://github.com/search?q=%22linker-features%3D-lld%22&type=code). The summary can be found below: <details> <summary>Summary of LLD opt outs</summary> > This examination was performed on 2025-03-09. Here we briefly examine the most common reasons why people use `-Zlinker-features=-lld`, based on comments and git history. - Nix/NixOS ([1](https://github.com/rszyma/vscode-kanata/blob/59d703dff5a238b14ab3759cac27f73fb34bbcfe/flake.nix#L33), [2](https://github.com/sbernauer/breakwater/blob/3cc3449fc126c5c99d4a971733fd32be589884e0/.cargo/config.toml#L4), [3](https://github.com/tiiuae/ebpf-firewall/blame/32bdb17cedd1c9bea1ab3482623de458d95da7d0/.cargo/config.toml#L2), [4](https://github.com/jules-sommer/wavetheme-gen/blob/f5f657d014d4a30607625afb70f810c229c0294e/Cargo.toml#L4), [5](https://github.com/LayerTwo-Labs/zside-rust/blob/e4266f5c5571a1b180a9c70cf0939c7070e410c7/.cargo/config.toml#L10), [6](https://github.com/przyjacielpkp/zkml/blob/22a4aef24e9d2c77789229d7c634fc67e9eb1184/README.md?plain=1#L78), [7](https://github.com/LayerTwo-Labs/thunder-rust/blob/2222d53474c8d2d0428b4c56f8157095dced6d5a/.cargo/config.toml#L2), [8](https://github.com/enesoztrk/nixos-tc-aya-test/blob/b2ffa59d3eba8b60fd04b0a4c8bbe047400eb981/.cargo/config.toml#L4), [9](https://github.com/lowRISC/container-hotplug/blob/3ead4ef9c7f79c303392178c99677dbecff1aea6/.cargo/config.toml#L2), [10](https://github.com/Eliah-Lakhin/ad-astra/blob/ca6b8c8a5dba7bb5e894f3f1013f17876962a021/work/examples/lsp-client/src/extension.ts#L94)) - There was an [issue](NixOS/nixpkgs#312661) with LLD, which seems to have been fixed with NixOS/nixpkgs#314268. It's unclear whether that fixed all the Nix issues though. - Issues with linkme ([1](https://github.com/0xPolygonZero/zk_evm/blob/ef388619ffbd5305209519a3a5bc0396185d68ac/.cargo/config.toml#L4), [2](https://github.com/conjure-cp/conjure-oxide/blob/be0fc5827ff90e8486d416cc184b6ce24f73bf01/README.md?plain=1#L20), [3](https://github.com/clchiou/garage/blob/c5d8444d56bb6ee24ca95e5c6b9880ed996f4918/rust/.cargo/config.toml#L6), [4](https://github.com/PonasKovas/craftflow/blob/5b4cc1a5196e08a975368399fefda4b71f3a2f6f/.cargo/config.toml#L3), [5](https://github.com/kezhuw/zookeeper-client-rust/blob/4e27c7de2a7cc5e709af012b791c8fea9bb47f1f/.github/workflows/ci.yml#L82), [6](https://github.com/niklasdewally/conjure-oxide/blob/8fe60c12bca7011a2f9eded4b7c95ad0e77b6f44/.github/workflows/code-coverage.yml#L48), [7](https://github.com/kezhuw/spawns/blob/c8b468379805de9df3287c01b94b4ed3db6b61ed/.github/workflows/ci.yml#L74)) - These should be resolved with the conservative garbage collection ([#137685](#137685)). - Bazel ([1](https://github.com/google-parfait/confidential-federated-compute/blob/1823f69ed8f5f4f819f7bfa21da1ca629fdc826b/.bazelrc#L71)), WASM ([1](https://github.com/Eliah-Lakhin/ad-astra/blob/ca6b8c8a5dba7bb5e894f3f1013f17876962a021/work/examples/wasm-build.sh#L37), [2](https://github.com/yacineb/pgrx-wasi-test/blob/2bf99037ca1b650b2cbc35f1257a87fb6ead0920/build.sh#L21)), uncategorized ([2](https://github.com/nbdd0121/r2vm/blob/5118be6b9e757c6fef2f019385873f403c23c548/.cargo/config.toml#L3), [3](https://github.com/Wyvern/Img/blame/45020c7e1dc4926c8129647014c708db0c13f463/.cargo/config.toml#L209), [4](https://github.com/arnaudpoullet/leptos-i18n-compile-error/blob/042eb835f7ca0dc36be67cf7fe65b35b22b6059f/README.md?plain=1#L89), [5](https://github.com/JonLeeCon/numerical-rust-cpu/blob/fd0b3006768ed81c56147044dc05c92b11b7b6f0/exercises/.cargo/config.toml#L13), [6](https://github.com/PonasKovas/shallowclone/blob/be65f2ec923cac6ceedbc8db520c89969ebfce7c/.github/workflows/rust.yml#L20)) - Reason unclear. </details> ## History The idea to use a faster linker by default has been on the radar for quite some time ([#39915](#39915), [#71515](#71515)). There were [very early attempts](#29974) to use the gold linker by default, but these had to be [reverted](#30913) because of compatibility issues. Support for LLD was implemented back in [2017](#40018), but it has not been made default yet, except for some more niche targets, such as [WASM](#48125), [ARM Cortex](#53648) or [RISC-V](#53822). It took quite some time to figure out how should the interface for selecting the linker (and the way it is invoked) look like, as it differs a lot between different platforms, linkers and compiler drivers. During that time, LLD has matured and achieved [almost perfect compatibility](https://maskray.me/blog/2020-12-19-lld-and-gnu-linker-incompatibilities) with the default Linux linker (BFD). - [#56351](#56351) stabilized `-Clinker-flavor`, which is used to determine how to invoke the linker. It is especially useful on targets where selecting the linker directly with `-Clinker` is not possible or is impractical. - December 2018, author `@davidtwco,` reviewer `@nagisa` - [#76158](#76158) stabilized `-Clink-self-contained=[y|n]`, which allows overriding the compiler's heuristic for deciding whether it should use self-contained or external tools (linker, sanitizers, libc, etc.). It only allowed using the self-contained mode either for everything (`y`) or nothing (`n`), but did not allow granular choice. - September 2020, author `@mati864,` reviewer `@petrochenkov` - [#85961](#85961) implemented the `-Zgcc-ld` flag, which was a hacky way of opting into LLD usage. - June 2021, author `@sledgehammervampire,` reviewer `@petrochenkov` - [MCP 510](rust-lang/compiler-team#510) proposed stabilizing the behavior of `-Zgcc-ld` using more granular flags (`-Clink-self-contained=linker -Clinker-flavor=gcc-lld`). - Initially implemented in [#96827](#96827), but `@petrochenkov` [suggested](#96827 (comment)) a slightly different approach. - The PR was split into [#96884](#96884), where it was decided what will be the individual components of `-Clink-self-contained=linker`. - And [#96401](#96401), which implemented the `-Clinker-flavor` part. - The MCP was finally implemented in [#112910](#112910). - [#116514](#116514) then removed `-Zgcc-ld`, as it was replaced by `-Clinker-flavor=gnu-lld-cc` + `-Clink-self-contained=linker`. - April 2022 - October 2023, author `@lqd,` reviewer `@petrochenkov` - Various linker handling refactorings were performed in the meantime: [#97375](#97375), [#98212](#98212), [#100126](#100126), [#100552](#100552), [#102836](#102836), [#110807](#110807), [#101988](#101988), [#116515](#116515) - The implementation of linker flavors with LLD was causing a sort of a combinatorial explosion of various options. [#119906](#119906) suggested a different approach for linker flavors (described [here](#119906 (comment))), where the individual flavors could be enabled separately using `+/-` (e.g. `+lld`). - After some back and forth, this idea was moved to `-Clinker-features` (see [comment 1](#119906 (comment)) and [comment 2](#119906 (comment))), which was implemented in [#123656](#123656). - April 2024, author `@lqd,` reviewer `@petrochenkov` - [#124129](#124129) enabled LLD by default on nightly. - April 2024, author `@lqd,` reviewer `@petrochenkov` - [#137685](#137685), [#137926](#137926) enabled the conservative gargage collection mode (`-znostart-stop-gc`) to improve compatibility with BFD. - February 2025, author `@lqd,` reviewer `@petrochenkov` (implementation), author `@kobzol,` reviewer `@lqd` (test) - [#96025](#96025) (April 2022), [#117684](#117684) (November 2023), [#137044](#137044) (February 2025): crater runs. ## Unresolved questions/concerns - Is changing the linker considered a breaking change? In (hopefully very rare) cases, it might break some existing code. It should mostly only affect the final linked artifact, so it should be easy to opt out. - Similarly, is the single-threaded behavior of such tools encompassed in our stability guarantee: it can be observed via the `-j` job limit (though I believe we have/had some open issues on sometimes using more CPU resources than the job count limit implied). As mentioned above, LLD does not support the jobserver protocol. - A concern [was raised](#71515 (comment)) about increased memory usage of LLD. We should probably let users know about the possibly increased memory usage, and jobserver incompatibility: we did so when announcing this landing on nightly. - LLD seems to produce [slightly larger](https://perf.rust-lang.org/compare.html?start=b3e117044c7f707293edc040edb93e7ec5f7040a&end=baed03c51a68376c1789cc373581eea0daf89967&stat=size%3Alinked_artifact&tab=compile) binary artifacts. This can be partially clawed back using Identical Code Folding (`-Clink-args=-Wl,--icf=all`). - Should we detect the outdated `.ctors/.dtors` sections to provide a better error message, even if that should be rare in practice? --- ### Next steps After the FCP completes: - we should land this PR at the beginning of a beta cycle, to maximize time for testing - keep an eye on the beta crater run results for possible linker issues (or do a dedicated beta crater run with only this change) - release a blog post announcing the change, and asking for testing feedback of the appropriate beta - depending on feedback, or if a period of testing of 6 weeks is not long enough, we could keep this change on beta for another cycle --- Development, testing, try builds were done in #138645. r? `@petrochenkov` `@rustbot` label +needs-fcp +T-compiler
Use lld by default on `x86_64-unknown-linux-gnu` stable This PR and stabilization report is joint work with `@Kobzol.` #### Use LLD on `x86_64-unknown-linux-gnu` by default, and stabilize `-Clinker-features=-lld` and `-Clink-self-contained=-linker` This PR proposes making LLD the default linker on the `x86_64-unknown-linux-gnu` target for the artifacts we distribute, and also stabilizing the `-Clinker-features=-lld` and `-Clink-self-contained=-linker` codegen options to make it possible to opt out. LLD has been used as the default linker on nightly and CI on this target since May 2024 ([PR](#124129), [blog post](https://blog.rust-lang.org/2024/05/17/enabling-rust-lld-on-linux.html)), and it seems like it is working fine, so we would like to propose stabilizing it. The main motivation for using LLD instead of the default BFD linker is improving [compilation times](https://perf.rust-lang.org/compare.html?start=b3e117044c7f707293edc040edb93e7ec5f7040a&end=baed03c51a68376c1789cc373581eea0daf89967&stat=instructions%3Au&tab=compile). For example, in the linked benchmark, it makes incremental recompilation of `ripgrep` in `debug` more than twice faster. Another benefit is that Rust compilation becomes more consistent and self-contained, because we will use a known version of the LLD linker, rather than "whatever GNU ld version is on the user's system". Due to the performance benefit being so huge, many people already opt into using LLD (or other fast linkers, such as mold) using various approaches ([1](https://github.com/search?type=code&q=%2Flinker-flavor%5B%3D+%5Dgnu-lld-cc%2F), [2](https://github.com/search?type=code&q=%2Flinker-features%5B%3D+%5D%5C%2Blld%2F), [3](https://github.com/search?type=code&q=language%3Atoml+%22-fuse-ld%3Dlld%22), [4](https://github.com/search?type=code&q=language%3Arust+%22-fuse-ld%3Dlld%22)). By making LLD the default linker on the `x86_64-unknown-linux-gnu` target, we will be able to speed up Rust compilation out of the box, without users having to opt in or know about it. > You can find an extended version of this stabilization report which includes analysis of crater results and more data [here](https://hackmd.io/tFDifkUcSLGoHPBRIl0z8w?view). ## What is being stabilized - `rust-lld` being used as the default linker on the `x86_64-unknown-linux-gnu` target. - Note that `rust-lld` is being enabled by default in the compiler artifacts distributed by our CI/rustup. It is still possible to use the system linker by default using `rust.lld = false` in `bootstrap.toml`, which can be useful e.g. for some Linux distros that might not want to use the LLD we distribute. - This is done by activating the LLD linker feature and using the self-contained linker on that target. Both of which are also usable on the CLI, if some opt outs are necessary, as described below. - `-Clinker-features=-lld` on the `x86_64-unknown-linux-gnu` target. This codegen option tells rustc to disable using the LLD linker. - Note that other options for this codegen flag (`cc`) remain unstable. - Note that only the opt-out is being stabilized, and only for `x86_64-unknown-linux-gnu`: opting in, or using the flag on other targets would still need to pass `-Zunstable-options`. - This flag is being stabilized so that users can opt out of LLD on stable, which would it turn also opt out of using the self-contained linker (since it's an LLD). - `-Clink-self-contained=-linker`. This codegen option tells rustc to use the self-contained linker. It's not particularly useful to turn it on by itself, but when enabled and combined with `-Clinker-features=+lld`, rustc will use the `rust-lld` linker wrapper shipped with the compiler toolchain, instead of some `lld` binary that the linker driver will find in the `PATH`. - Note that other options for this codegen flag (other than the previously stable `y/yes/n/no`). - Note that only the opt-out is being stabilized, and only for `x86_64-unknown-linux-gnu`: opting in, or using this flag on other targets would still need to pass `-Zunstable-options`. - This flag is being stabilized so that users can opt out of using self-contained linking on stable. Doing this would then fall back to using the system `lld`. To opt out of using LLD, `RUSTFLAGS="-Clinker-features=-lld"` would be used. To opt out of using `rust-lld`, falling back to the LLD installed on the system, `RUSTFLAGS="-Clink-self-contained=-linker"` would be used. ## Tests When enabling `rust-lld` on nightly, we also switched x64 linux to use it at stage >= 1, meaning that all tests have been running with lld since May 2024, on CI as well as contributors' machines. (Post opt-dist tests also had been using it when running their test subset earlier than that). There are also a few tests dedicated to the CLI behavior, or ensuring the default linker is indeed the one we expect: - [link-self-contained-consistency](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/ui/linking/link-self-contained-consistency.rs): Checks that `-Clink-self-contained` options are not inconsistent (i.e. that passing both `+linker` and `-linker` is an error). - [link-self-contained-unstable](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/ui/linking/link-self-contained-unstable.rs): Checks that only the `-linker` and `y/yes/n/no` options for `-Clink-self-contained` are stable. - [linker-features-unstable-cc](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/ui/linking/linker-features-unstable-cc.rs): Checks that only the non-lld options of `-Clinker-features` are unstable. - [linker-features-lld-disallowed](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/ui/linking/linker-features-lld-disallowed.rs): Checks that `-Clinker-features=-lld` is only stable on `x86_64-unknown-linux-gnu`. - [link-self-contained-linker-disallowed](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/ui/linking/link-self-contained-linker-disallowed.rs): Checks that `-Clink-self-contained=-linker` is only stable on `x86_64-unknown-linux-gnu`. - [no-gc-encapsulation-symbols](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/ui/linking/no-gc-encapsulation-symbols.rs): Checks that that linker encapsulation symbols are not garbage collected by LLD, so that crates like [linkme](https://github.com/dtolnay/linkme) still work. - [rust-lld](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/run-make/rust-lld): Checks that LLD is actually used when enabled with `-Clinker-features=+lld` and `-Clink-self-contained=+linker`. - [rust-lld-x86_64-unknown-linux-gnu](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/run-make/rust-lld-x86_64-unknown-linux-gnu): Checks that LLD is used by default on `x86_64-unknown-linux-gnu` when the bootstrap `rust.lld` config option is `true`. - [rust-lld-x86_64-unknown-linux-gnu-dist](https://github.com/rust-lang/rust/blob/1117bc1e6ce049495b0044dfe756afafc817d2d7/tests/run-make/rust-lld-x86_64-unknown-linux-gnu-dist): Dist test that checks that our distributed `x86_64-unknown-linux-gnu` archives actually use LLD by default. ## Ecosystem impact As already stated, LLD has been used as the default linker on x64 Linux on nightly for almost a year, and we haven't seen any blockers to stabilization in that time. There were a handful of issues reported, these are discussed later below. Furthermore, two crater runs ([November 2023](https://crater-reports.s3.amazonaws.com/pr-117684-2/index.html), [February 2025](https://crater-reports.s3.amazonaws.com/pr-137044-3/index.html)), were performed to test the impact of using LLD as the default linker. A triage of the earlier crater run was previously done [here](https://hackmd.io/OAJxlxc6Te6YUot9ftYSKQ), but all the important findings from both crater runs are reported below. Below is a list of compatibility differences between BFD and LLD that we have encountered. There is a more thorough list of differences in [this post](https://maskray.me/blog/2020-12-19-lld-and-gnu-linker-incompatibilities) from the current LLD maintainer. From that post, "99.9% pieces of software work with ld.lld without a change". --- ### `.ctors/.dtors` sections [#128286](#128286) reported an issue where LLD was unable to link certain CUDA library was using these sections that were using the `.ctors/.dtors` ELF sections. These were deprecated a long time ago (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=46770), replaced with a more modern `.init_array/.fini_array` sections. LLD doesn't (and won't) support these sections ([1](llvm/llvm-project#68071), [2](llvm/llvm-project#30572)), so if they appear in input object files, the linked artifact might produce incorrect behavior, because e.g. some global variables might not get initialized properly. However, the usage of `.ctors/.dtors` should be very rare in practice. We have performed a [crater run](#137044) to test this. It has identified only 8 crates where the `.ctors/.dtors` section is occurring in the final linked artifact. It was caused by a few crates using the `.ctors` link section manually, and by using a very (~6 year) old version of the [ctor](https://crates.io/crates/ctor) crate. [Crater run analysis](https://hackmd.io/tFDifkUcSLGoHPBRIl0z8w?view#ctorsdtors-sections) **Possible workaround** It is possible to [detect](e5e2316) if `.ctors/.dtors` section is present in the final linked artifact (LLD will keep it there, but it won't be populated), and warn users about it. This check is very cheap and doesn't even appear on [perf](#112049 (comment)). We have benchmarked the check on a 240 MiB Chrome binary, where it took 0.8ms with page cache flushed, and 0.06ms with page cache primed (which should be the common case, as the linked artifact is written to disk just before the check is performed). In theory, this could be also solved with a linker script that moves `.ctors` to `.init_array`. We think that these sections should be so rare that it is not worth it to implement any workarounds for now. --- ### Different garbage collection behavior [#130397](#130397) reported an issue where LLD prunes a local symbol, so it is missing in the linked artifact. However, BFD keeps the same symbol, so it is a regression. This is caused by a difference in linker garbage collection. Rust uses `--gc-sections` and puts each function into a separate linker section, which prunes unused code. There is some code (specifically the somewhat popular [linkme](https://github.com/dtolnay/linkme) crate) that (arguably ab-)uses so called linker encapsulation symbols to achieve distributed slices. BFD (2.37+) uses a conservative linking mode that works as intended with this behavior, but it might slightly increase binary size of the linked artifact. LLD does not use this workaround by default, which causes the sections to be eliminated, but it can be made to use the conservative mode using [`-z nostart-stop-gc`](https://lld.llvm.org/ELF/start-stop-gc.html#z-start-stop-gc). To avoid this issue, we told LLD to use the [conservative mode](#137685), which maintains backwards compatibility with BFD. We found that it has [no effect](#112049 (comment)) on compilation performance and binary size in our benchmark suite. With this change, `linkme` works. Since then, #140872 removed `linkme` distributed slice's dependence on conservative GC behavior, so this PR also removes that conservative mode: no transition period is necessary, as the PR immediately fixed the crate with no source changes. [Crater run analysis](https://hackmd.io/tFDifkUcSLGoHPBRIl0z8w?view#Different-garbage-collection-behavior) --- ### Various uncommon issues A small number of issues that only occurred in a handful of instances were found in crater, and it is unclear if LLD is at fault or if there is some other issue that was not detected with BFD. You can examine these [here](https://hackmd.io/tFDifkUcSLGoHPBRIl0z8w?view#Various-uncommon-issues). --- ### Missing jobserver support LLD doesn't support the jobserver protocol for limiting the number of threads used, it simply defaults to using all available cores, and is one of the reasons why it's faster than BFD. However, this should mostly be a non-issue, because most of the linking done during high parallelism sections of `cargo build` is linking of build scripts and proc macros, which are typically very fast to link (e.g. ~50ms), and a potential oversubscription of cores thus doesn't hurt that much. When the final artifact is linked (which typically takes the most time), there should be no other sources of parallelism conflicts from compiling other code, so LLD should be able to use all available threads. That being said, it is a difference of behavior, where previously a `-j` flag was generally not using more cpu than the specified limit. It can be impactful in some resource-constrained systems, but to be clear that is already the case today due to [cargo parallelism](rust-lang/cargo#9157). This could be one reason to opt out of using `rust-lld` on some systems. LLD has support for limiting the number of threads to use, so in theory rustc could try to get all the jobserver tokens available and use that as lld's thread limit. It'd still be suboptimal as new tokens would not be dynamically detected, and we could be using less threads than available. We did a benchmark on a real-world crate that shows that using multiple LLD threads for intermediate artifacts doesn't seem to have a performance effect. You can find it [here](https://hackmd.io/tFDifkUcSLGoHPBRIl0z8w?view#Missing-jobserver-support). --- #### Opting out of LLD in the ecosystem We have also examined repositories where people opted out of LLD on nightly, using [this GitHub query](https://github.com/search?q=%22linker-features%3D-lld%22&type=code). The summary can be found below: <details> <summary>Summary of LLD opt outs</summary> > This examination was performed on 2025-03-09. Here we briefly examine the most common reasons why people use `-Zlinker-features=-lld`, based on comments and git history. - Nix/NixOS ([1](https://github.com/rszyma/vscode-kanata/blob/59d703dff5a238b14ab3759cac27f73fb34bbcfe/flake.nix#L33), [2](https://github.com/sbernauer/breakwater/blob/3cc3449fc126c5c99d4a971733fd32be589884e0/.cargo/config.toml#L4), [3](https://github.com/tiiuae/ebpf-firewall/blame/32bdb17cedd1c9bea1ab3482623de458d95da7d0/.cargo/config.toml#L2), [4](https://github.com/jules-sommer/wavetheme-gen/blob/f5f657d014d4a30607625afb70f810c229c0294e/Cargo.toml#L4), [5](https://github.com/LayerTwo-Labs/zside-rust/blob/e4266f5c5571a1b180a9c70cf0939c7070e410c7/.cargo/config.toml#L10), [6](https://github.com/przyjacielpkp/zkml/blob/22a4aef24e9d2c77789229d7c634fc67e9eb1184/README.md?plain=1#L78), [7](https://github.com/LayerTwo-Labs/thunder-rust/blob/2222d53474c8d2d0428b4c56f8157095dced6d5a/.cargo/config.toml#L2), [8](https://github.com/enesoztrk/nixos-tc-aya-test/blob/b2ffa59d3eba8b60fd04b0a4c8bbe047400eb981/.cargo/config.toml#L4), [9](https://github.com/lowRISC/container-hotplug/blob/3ead4ef9c7f79c303392178c99677dbecff1aea6/.cargo/config.toml#L2), [10](https://github.com/Eliah-Lakhin/ad-astra/blob/ca6b8c8a5dba7bb5e894f3f1013f17876962a021/work/examples/lsp-client/src/extension.ts#L94)) - There was an [issue](NixOS/nixpkgs#312661) with LLD, which seems to have been fixed with NixOS/nixpkgs#314268. It's unclear whether that fixed all the Nix issues though. - Issues with linkme ([1](https://github.com/0xPolygonZero/zk_evm/blob/ef388619ffbd5305209519a3a5bc0396185d68ac/.cargo/config.toml#L4), [2](https://github.com/conjure-cp/conjure-oxide/blob/be0fc5827ff90e8486d416cc184b6ce24f73bf01/README.md?plain=1#L20), [3](https://github.com/clchiou/garage/blob/c5d8444d56bb6ee24ca95e5c6b9880ed996f4918/rust/.cargo/config.toml#L6), [4](https://github.com/PonasKovas/craftflow/blob/5b4cc1a5196e08a975368399fefda4b71f3a2f6f/.cargo/config.toml#L3), [5](https://github.com/kezhuw/zookeeper-client-rust/blob/4e27c7de2a7cc5e709af012b791c8fea9bb47f1f/.github/workflows/ci.yml#L82), [6](https://github.com/niklasdewally/conjure-oxide/blob/8fe60c12bca7011a2f9eded4b7c95ad0e77b6f44/.github/workflows/code-coverage.yml#L48), [7](https://github.com/kezhuw/spawns/blob/c8b468379805de9df3287c01b94b4ed3db6b61ed/.github/workflows/ci.yml#L74)) - These should be resolved with the conservative garbage collection ([#137685](#137685)). - Bazel ([1](https://github.com/google-parfait/confidential-federated-compute/blob/1823f69ed8f5f4f819f7bfa21da1ca629fdc826b/.bazelrc#L71)), WASM ([1](https://github.com/Eliah-Lakhin/ad-astra/blob/ca6b8c8a5dba7bb5e894f3f1013f17876962a021/work/examples/wasm-build.sh#L37), [2](https://github.com/yacineb/pgrx-wasi-test/blob/2bf99037ca1b650b2cbc35f1257a87fb6ead0920/build.sh#L21)), uncategorized ([2](https://github.com/nbdd0121/r2vm/blob/5118be6b9e757c6fef2f019385873f403c23c548/.cargo/config.toml#L3), [3](https://github.com/Wyvern/Img/blame/45020c7e1dc4926c8129647014c708db0c13f463/.cargo/config.toml#L209), [4](https://github.com/arnaudpoullet/leptos-i18n-compile-error/blob/042eb835f7ca0dc36be67cf7fe65b35b22b6059f/README.md?plain=1#L89), [5](https://github.com/JonLeeCon/numerical-rust-cpu/blob/fd0b3006768ed81c56147044dc05c92b11b7b6f0/exercises/.cargo/config.toml#L13), [6](https://github.com/PonasKovas/shallowclone/blob/be65f2ec923cac6ceedbc8db520c89969ebfce7c/.github/workflows/rust.yml#L20)) - Reason unclear. </details> ## History The idea to use a faster linker by default has been on the radar for quite some time ([#39915](#39915), [#71515](#71515)). There were [very early attempts](#29974) to use the gold linker by default, but these had to be [reverted](#30913) because of compatibility issues. Support for LLD was implemented back in [2017](#40018), but it has not been made default yet, except for some more niche targets, such as [WASM](#48125), [ARM Cortex](#53648) or [RISC-V](#53822). It took quite some time to figure out how should the interface for selecting the linker (and the way it is invoked) look like, as it differs a lot between different platforms, linkers and compiler drivers. During that time, LLD has matured and achieved [almost perfect compatibility](https://maskray.me/blog/2020-12-19-lld-and-gnu-linker-incompatibilities) with the default Linux linker (BFD). - [#56351](#56351) stabilized `-Clinker-flavor`, which is used to determine how to invoke the linker. It is especially useful on targets where selecting the linker directly with `-Clinker` is not possible or is impractical. - December 2018, author `@davidtwco,` reviewer `@nagisa` - [#76158](#76158) stabilized `-Clink-self-contained=[y|n]`, which allows overriding the compiler's heuristic for deciding whether it should use self-contained or external tools (linker, sanitizers, libc, etc.). It only allowed using the self-contained mode either for everything (`y`) or nothing (`n`), but did not allow granular choice. - September 2020, author `@mati864,` reviewer `@petrochenkov` - [#85961](#85961) implemented the `-Zgcc-ld` flag, which was a hacky way of opting into LLD usage. - June 2021, author `@sledgehammervampire,` reviewer `@petrochenkov` - [MCP 510](rust-lang/compiler-team#510) proposed stabilizing the behavior of `-Zgcc-ld` using more granular flags (`-Clink-self-contained=linker -Clinker-flavor=gcc-lld`). - Initially implemented in [#96827](#96827), but `@petrochenkov` [suggested](#96827 (comment)) a slightly different approach. - The PR was split into [#96884](#96884), where it was decided what will be the individual components of `-Clink-self-contained=linker`. - And [#96401](#96401), which implemented the `-Clinker-flavor` part. - The MCP was finally implemented in [#112910](#112910). - [#116514](#116514) then removed `-Zgcc-ld`, as it was replaced by `-Clinker-flavor=gnu-lld-cc` + `-Clink-self-contained=linker`. - April 2022 - October 2023, author `@lqd,` reviewer `@petrochenkov` - Various linker handling refactorings were performed in the meantime: [#97375](#97375), [#98212](#98212), [#100126](#100126), [#100552](#100552), [#102836](#102836), [#110807](#110807), [#101988](#101988), [#116515](#116515) - The implementation of linker flavors with LLD was causing a sort of a combinatorial explosion of various options. [#119906](#119906) suggested a different approach for linker flavors (described [here](#119906 (comment))), where the individual flavors could be enabled separately using `+/-` (e.g. `+lld`). - After some back and forth, this idea was moved to `-Clinker-features` (see [comment 1](#119906 (comment)) and [comment 2](#119906 (comment))), which was implemented in [#123656](#123656). - April 2024, author `@lqd,` reviewer `@petrochenkov` - [#124129](#124129) enabled LLD by default on nightly. - April 2024, author `@lqd,` reviewer `@petrochenkov` - [#137685](#137685), [#137926](#137926) enabled the conservative gargage collection mode (`-znostart-stop-gc`) to improve compatibility with BFD. - February 2025, author `@lqd,` reviewer `@petrochenkov` (implementation), author `@kobzol,` reviewer `@lqd` (test) - [#96025](#96025) (April 2022), [#117684](#117684) (November 2023), [#137044](#137044) (February 2025): crater runs. ## Unresolved questions/concerns - Is changing the linker considered a breaking change? In (hopefully very rare) cases, it might break some existing code. It should mostly only affect the final linked artifact, so it should be easy to opt out. - Similarly, is the single-threaded behavior of such tools encompassed in our stability guarantee: it can be observed via the `-j` job limit (though I believe we have/had some open issues on sometimes using more CPU resources than the job count limit implied). As mentioned above, LLD does not support the jobserver protocol. - A concern [was raised](#71515 (comment)) about increased memory usage of LLD. We should probably let users know about the possibly increased memory usage, and jobserver incompatibility: we did so when announcing this landing on nightly. - LLD seems to produce [slightly larger](https://perf.rust-lang.org/compare.html?start=b3e117044c7f707293edc040edb93e7ec5f7040a&end=baed03c51a68376c1789cc373581eea0daf89967&stat=size%3Alinked_artifact&tab=compile) binary artifacts. This can be partially clawed back using Identical Code Folding (`-Clink-args=-Wl,--icf=all`). - Should we detect the outdated `.ctors/.dtors` sections to provide a better error message, even if that should be rare in practice? --- ### Next steps After the FCP completes: - we should land this PR at the beginning of a beta cycle, to maximize time for testing - keep an eye on the beta crater run results for possible linker issues (or do a dedicated beta crater run with only this change) - release a blog post announcing the change, and asking for testing feedback of the appropriate beta - depending on feedback, or if a period of testing of 6 weeks is not long enough, we could keep this change on beta for another cycle --- Development, testing, try builds were done in #138645. r? `@petrochenkov` `@rustbot` label +needs-fcp +T-compiler
This commit imports the LLD project from LLVM to serve as the default linker for
the
wasm32-unknown-unknown
target. Thebinaryen
submoule is consequentlyremoved along with "binaryen linker" support in rustc.
Moving to LLD brings with it a number of benefits for wasm code:
with LTO any more. As a result builds should be much speedier as LTO is no
longer forcibly enabled for all builds of the wasm target.
This, I believe at least, is intended to be the main supported linker for
native code and wasm moving forward. Picking up support early on should help
ensure that we can help LLD identify bugs and otherwise prove that it works
great for all our use cases!
and LLD (from what I can tell at least), so it's in general much better to be
on this bandwagon for bugfixes and new features.
wasm-gc
will soon no longer be necessary, LLDwill natively implement
--gc-sections
(better thanwasm-gc
!) whichmeans a postprocessor is no longer needed to show off Rust's "small wasm
binary size".
LLD is added in a pretty standard way to rustc right now. A new rustbuild target
was defined for building LLD, and this is executed when a compiler's sysroot is
being assembled. LLD is compiled against the LLVM that we've got in tree, which
means we're currently on the
release_60
branch, but this may get upgraded inthe near future!
LLD is placed into rustc's sysroot in a
bin
directory. This is similar towhere
gcc.exe
can be found on Windows. This directory is automatically addedto
PATH
whenever rustc executes the linker, allowing us to define aWasmLd
linker which implements the interface that
wasm-ld
, LLD's frontend, expects.Like Emscripten the LLD target is currently only enabled for Tier 1 platforms,
notably OSX/Windows/Linux, and will need to be installed manually for compiling
to wasm on other platforms. LLD is by default turned off in rustbuild, and
requires a
config.toml
option to be enabled to turn it on.Finally the unstable
#![wasm_import_memory]
attribute was also removed as LLDhas a native option for controlling this.