Skip to content

Make #[used(linker)] the default on ELF too #140872

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

Merged
merged 1 commit into from
Jun 6, 2025

Conversation

bjorn3
Copy link
Member

@bjorn3 bjorn3 commented May 9, 2025

#[used] currently is an alias for #[used(linker)] on all platforms except ELF based ones where it is an alias for #[used(compiler)]. The latter has surprising behavior and the LLVM LangRef explicitly states that it "should only be used in rare circumstances, and should not be exposed to source languages." 1

The reason #[used] still was an alias to #[used(compiler)] on ELF is because the gold linker has issues with it. Luckily gold has been deprecated with GCC 15 2 and seems to be unable to bootstrap rustc anyway 3. As such we shouldn't really care about supporting gold.

This would also allow re-enabling start-stop-gc with lld.

cc #93798
Likely fixes #85045

Footnotes

  1. https://llvm.org/docs/LangRef.html#the-llvm-compiler-used-global-variable

  2. https://lists.gnu.org/archive/html/info-gnu/2025-02/msg00001.html

  3. https://github.com/rust-lang/rust/issues/139425

@rustbot
Copy link
Collaborator

rustbot commented May 9, 2025

r? @nnethercote

rustbot has assigned @nnethercote.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added A-attributes Area: Attributes (`#[…]`, `#![…]`) S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels May 9, 2025
@rustbot
Copy link
Collaborator

rustbot commented May 9, 2025

Some changes occurred in compiler/rustc_codegen_ssa

cc @WaffleLapkin

Some changes occurred in compiler/rustc_codegen_ssa/src/codegen_attrs.rs

cc @jdonszelmann

@bjorn3 bjorn3 added A-linkage Area: linking into static, shared libraries and binaries F-used_with_arg `#![feature(used_with_arg)]` labels May 9, 2025
@bjorn3
Copy link
Member Author

bjorn3 commented May 9, 2025

cg_clif currently treats #[used(linker)] as #[used(compiler)] (bytecodealliance/wasmtime#10758), but I don't think that should be a blocker for this PR.

@nnethercote
Copy link
Contributor

Looks ok to me, but as this is the first I've heard of these attributes, I will run it past the person who first suggested them:

r? @nikic

@rustbot rustbot assigned nikic and unassigned nnethercote May 21, 2025
@nikic
Copy link
Contributor

nikic commented May 21, 2025

This generally sounds reasonable to me in the interest of having consistent behavior across platforms. Some questions:

  • What is the interaction of this with symbols.o? I have a vague understanding that symbols.o already gets us this behavior in most cases, but maybe I'm wrong about that. (Was that only about making sure the symbols get pulled in at all from archives?)
  • IIRC one of the issues was that SHF_RETAIN is a new flag. While only gold was actively miscompiling it at the time, there was still the issue that non-bleeding-edge linkers wouldn't actually support it, giving you different behavior depending on how new your linker is. I guess the situation here is somewhat better now.
  • I assume the intention here is that this is still just an implementation detail, and the guarantee continues to only be the #[used(compiler)] behavior?
  • There seems to be some doubt on Linking to a static library does not always include exported symbols #99721 whether this PR would actually fix it. Can we have a test (run-make presumably?) for it? Or at least we should make sure the "likely" fixes don't auto-close.

@bjorn3
Copy link
Member Author

bjorn3 commented May 21, 2025

What is the interaction of this with symbols.o? I have a vague understanding that symbols.o already gets us this behavior in most cases, but maybe I'm wrong about that. (Was that only about making sure the symbols get pulled in at all from archives?)

symbols.o only ensures that the object file containing the symbol is pulled in from an archive. It does nothing to stop --gc-sections from removing a static if the linker believes it to be unused. #[used(linker)] however ensures that the linker will keep it even if it isn't referenced.

I assume the intention here is that this is still just an implementation detail, and the guarantee continues to only be the #[used(compiler)] behavior?

I want to guarantee the #[used(linker)] behavior as soon as we are confident that the this PR doesn't regress anything.

@lqd
Copy link
Member

lqd commented May 21, 2025

This would also allow re-enabling start-stop-gc with lld.

It would at least remove the main reason why we introduced -znostart-stop-gc, as this PR would of course fix linkme with lld.

@bjorn3
Copy link
Member Author

bjorn3 commented May 21, 2025

IIRC one of the issues was that SHF_RETAIN is a new flag. While only gold was actively miscompiling it at the time, there was still the issue that non-bleeding-edge linkers wouldn't actually support it, giving you different behavior depending on how new your linker is. I guess the situation here is somewhat better now.

SHF_GNU_RETAIN is a lot newer than I thought. It was only introduced in 2021 with binutils 2.36. I guess that means we can't make it a hard guarantee for now. We can stop passing -znostart-stop-gc for rust-lld however as we know the LLD version we ship is new enough to support it.

@bors
Copy link
Collaborator

bors commented May 25, 2025

☔ The latest upstream changes (presumably #141518) made this pull request unmergeable. Please resolve the merge conflicts.

@Noratrieb
Copy link
Member

Noratrieb commented May 29, 2025

I did some investigation on the history of SHF_GNU_RETAIN and its interactions with GCC.
It was proposed in 2020 on the linked mails in https://sourceware.org/pipermail/binutils/2020-November/113993.html.

Originally, the previously existing C-equivalent of #[used], __attribute__((used)) was changed to imply SHF_GNU_RETAIN (https://gcc.gnu.org/pipermail/gcc-patches/2020-October/555594.html, https://gcc.gnu.org/pipermail/gcc-patches/2020-November/557906.html). Additionally, __attribute__((retain)) was proposed to only apply SHF_GNU_RETAIN. Since SHF_GNU_RETAIN was a section flag (and linker GC happens on the section level), __attribute__((used)) was changed such that the symbol was put in a -fdata-sections-like new section with the flag applied.

It was then discovered that this behavior broke projects, like the Linux kernel (https://gcc.gnu.org/pipermail/gcc-patches/2021-February/565337.html). The reason for that was that they already used __attribute__((used)) but their linker scripts did not support the symbols ending up in these new sections. The general consensus on the mailing list appeared to have been that it would have been nice to make __attribute__((used)) apply SHF_GNU_RETAIN as well.

So what does that mean for Rust? Since we always use the equivalent of -fdata-sections (funnily enough there's -Zfunction-sections to disable -ffunction-sections but no equivalent of -fdata-sections) this breakage is not a concern for us. So this does indeed seem safe to go ahead.

I've opened #141750 to see whether it's possible to warn about the potential miscompiles with gold.

@Kobzol
Copy link
Member

Kobzol commented Jun 5, 2025

@nikic Do you have additional concerns regarding the SHF_RETAIN usage? With it, use-cases like linkme with #[used] would "just work" with a recent enough BFD (2.36+) and LLD (13+), and it would allow us to stop passing -znostart-stop-gc to LLD, which would slightly help with #140525.

I suppose that we don't need to concern ourselves with gold anymore, as with #141748 we warn about its usage, and it has been marked as deprecated.

@nikic
Copy link
Contributor

nikic commented Jun 5, 2025

I guess if we're mainly targeting the linkme use case, it should be fine, because the overlap of "enables start/stop GC by default" and "does not support SHF_RETAIN" should be empty (right?)

More widely, we may hit issues with ancient binutils versions used by the undead cross-rs project...

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine from my side.

@Kobzol
Copy link
Member

Kobzol commented Jun 5, 2025

because the overlap of "enables start/stop GC by default" and "does not support SHF_RETAIN" should be empty (right?)

I'm not 100% sure, but it seems like so. BFD and gold still use the conservative behavior, and LLD started using start/stop GC from version 13, where it also started supporting SHF_GNU_RETAIN, AFAIK.

#[used] currently is an alias for #[used(linker)] on all platforms
except ELF based ones where it is an alias for #[used(compiler)]. The
latter has surprising behavior and the LLVM LangRef explicitly states
that it "should only be used in rare circumstances, and should not be
exposed to source languages."

The reason #[used] still was an alias to #[used(compiler)] on ELF is
because the gold linker has issues with it. Luckily gold has been
deprecated with GCC 15 and seems to be unable to bootstrap rustc anyway.
As such we shouldn't really care about supporting gold.
@bors
Copy link
Collaborator

bors commented Jun 6, 2025

☀️ Test successful - checks-actions
Approved by: nikic
Pushing cf42371 to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Jun 6, 2025
@bors bors merged commit cf42371 into rust-lang:master Jun 6, 2025
11 checks passed
@rustbot rustbot added this to the 1.89.0 milestone Jun 6, 2025
Copy link
Contributor

github-actions bot commented Jun 6, 2025

What is this? This is an experimental post-merge analysis report that shows differences in test outcomes between the merged PR and its parent PR.

Comparing ccf3198 (parent) -> cf42371 (this PR)

Test differences

No test diffs found

Test dashboard

Run

cargo run --manifest-path src/ci/citool/Cargo.toml -- \
    test-dashboard cf423712b9e95e9f6ec84b1ecb3d125e55ac8d56 --output-dir test-dashboard

And then open test-dashboard/index.html in your browser to see an overview of all executed tests.

Job duration changes

  1. mingw-check-1: 2487.7s -> 1668.4s (-32.9%)
  2. dist-aarch64-apple: 6564.3s -> 5318.3s (-19.0%)
  3. x86_64-apple-1: 9584.7s -> 8002.0s (-16.5%)
  4. x86_64-gnu-debug: 6305.2s -> 5265.7s (-16.5%)
  5. i686-gnu-2: 6166.1s -> 5285.6s (-14.3%)
  6. aarch64-apple: 5456.8s -> 4692.3s (-14.0%)
  7. x86_64-apple-2: 5477.9s -> 4869.7s (-11.1%)
  8. i686-gnu-1: 8026.6s -> 7149.2s (-10.9%)
  9. dist-x86_64-apple: 10155.4s -> 9047.4s (-10.9%)
  10. arm-android: 5962.4s -> 5329.5s (-10.6%)
How to interpret the job duration changes?

Job durations can vary a lot, based on the actual runner instance
that executed the job, system noise, invalidated caches, etc. The table above is provided
mostly for t-infra members, for simpler debugging of potential CI slow-downs.

@rust-timer
Copy link
Collaborator

Finished benchmarking commit (cf42371): comparison URL.

Overall result: no relevant changes - no action needed

@rustbot label: -perf-regression

Instruction count

This benchmark run did not return any relevant results for this metric.

Max RSS (memory usage)

Results (primary -4.2%)

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
-4.2% [-4.2%, -4.2%] 1
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) -4.2% [-4.2%, -4.2%] 1

Cycles

Results (primary -2.4%)

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
-2.4% [-2.4%, -2.4%] 1
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) -2.4% [-2.4%, -2.4%] 1

Binary size

This benchmark run did not return any relevant results for this metric.

Bootstrap: 750.435s -> 750.748s (0.04%)
Artifact size: 371.90 MiB -> 371.85 MiB (-0.01%)

@bjorn3 bjorn3 deleted the elf_use_used_linker branch June 6, 2025 09:37
rust-bors bot added a commit that referenced this pull request Jul 8, 2025
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-*
bors added a commit that referenced this pull request Jul 8, 2025
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
bors added a commit that referenced this pull request Jul 8, 2025
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
ojeda added a commit to ojeda/linux that referenced this pull request Jul 12, 2025
Starting with Rust 1.89.0 (expected 2025-08-07), the Rust compiler fails
to build the `rusttest` target due to undefined references such as:

    kernel...-cgu.0:(.text....+0x116): undefined reference to
    `rust_helper_kunit_get_current_test'

Moreover, tooling like `modpost` gets confused:

    WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/gpu/drm/nova/nova.o
    ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/nova-core/nova_core.o

The reason behind both issues is that the Rust compiler will now [1] treat
`#[used]` as `#[used(linker)]` instead of `#[used(compiler)]` for our
targets. This means that the retain section flag (`R`, `SHF_GNU_RETAIN`)
will be used and that they will be marked as `unique` too, with different
IDs. In turn, that means we end up with undefined references that did
not get discarded and that multiple `.modinfo` sections are generated,
which confuse tooling like `modpost` because they only expect one.

Thus start using `#[used(compiler)]` to keep the previous behavior
and to be explicit about what we want. Sadly, it is an unstable feature
(`used_with_arg`) [2] -- we will talk to upstream Rust about it. The good
news is that it has been available for a long time (Rust >= 1.60) [3].

The changes should also be fine for previous Rust versions, since they
behave the same way as before [4].

Alternatively, we could use `#[no_mangle]` or `#[export_name = ...]`
since those still behave like `#[used(compiler)]`, but of course it is
not really what we want to express, and it requires other changes to
avoid symbol conflicts.

Cc: Björn Roy Baron <bjorn3_gh@protonmail.com>
Cc: David Wood <david@davidtw.co>
Cc: Wesley Wiser <wwiser@gmail.com>
Link: rust-lang/rust#140872 [1]
Link: rust-lang/rust#93798 [2]
Link: rust-lang/rust#91504 [3]
Link: https://godbolt.org/z/sxzWTMfzW [4]
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
ojeda added a commit to ojeda/linux that referenced this pull request Jul 12, 2025
… 1.89.0

Starting with Rust 1.89.0 (expected 2025-08-07), the Rust compiler fails
to build the `rusttest` target due to undefined references such as:

    kernel...-cgu.0:(.text....+0x116): undefined reference to
    `rust_helper_kunit_get_current_test'

Moreover, tooling like `modpost` gets confused:

    WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/gpu/drm/nova/nova.o
    ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/nova-core/nova_core.o

The reason behind both issues is that the Rust compiler will now [1]
treat `#[used]` as `#[used(linker)]` instead of `#[used(compiler)]`
for our targets. This means that the retain section flag (`R`,
`SHF_GNU_RETAIN`) will be used and that they will be marked as `unique`
too, with different IDs. In turn, that means we end up with undefined
references that did not get discarded in `rusttest` and that multiple
`.modinfo` sections are generated, which confuse tooling like `modpost`
because they only expect one.

Thus start using `#[used(compiler)]` to keep the previous behavior
and to be explicit about what we want. Sadly, it is an unstable feature
(`used_with_arg`) [2] -- we will talk to upstream Rust about it. The good
news is that it has been available for a long time (Rust >= 1.60) [3].

The changes should also be fine for previous Rust versions, since they
behave the same way as before [4].

Alternatively, we could use `#[no_mangle]` or `#[export_name = ...]`
since those still behave like `#[used(compiler)]`, but of course it is
not really what we want to express, and it requires other changes to
avoid symbol conflicts.

Cc: Björn Roy Baron <bjorn3_gh@protonmail.com>
Cc: David Wood <david@davidtw.co>
Cc: Wesley Wiser <wwiser@gmail.com>
Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs).
Link: rust-lang/rust#140872 [1]
Link: rust-lang/rust#93798 [2]
Link: rust-lang/rust#91504 [3]
Link: https://godbolt.org/z/sxzWTMfzW [4]
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
intel-lab-lkp pushed a commit to intel-lab-lkp/linux that referenced this pull request Jul 12, 2025
… 1.89.0

Starting with Rust 1.89.0 (expected 2025-08-07), the Rust compiler fails
to build the `rusttest` target due to undefined references such as:

    kernel...-cgu.0:(.text....+0x116): undefined reference to
    `rust_helper_kunit_get_current_test'

Moreover, tooling like `modpost` gets confused:

    WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/gpu/drm/nova/nova.o
    ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/nova-core/nova_core.o

The reason behind both issues is that the Rust compiler will now [1]
treat `#[used]` as `#[used(linker)]` instead of `#[used(compiler)]`
for our targets. This means that the retain section flag (`R`,
`SHF_GNU_RETAIN`) will be used and that they will be marked as `unique`
too, with different IDs. In turn, that means we end up with undefined
references that did not get discarded in `rusttest` and that multiple
`.modinfo` sections are generated, which confuse tooling like `modpost`
because they only expect one.

Thus start using `#[used(compiler)]` to keep the previous behavior
and to be explicit about what we want. Sadly, it is an unstable feature
(`used_with_arg`) [2] -- we will talk to upstream Rust about it. The good
news is that it has been available for a long time (Rust >= 1.60) [3].

The changes should also be fine for previous Rust versions, since they
behave the same way as before [4].

Alternatively, we could use `#[no_mangle]` or `#[export_name = ...]`
since those still behave like `#[used(compiler)]`, but of course it is
not really what we want to express, and it requires other changes to
avoid symbol conflicts.

Cc: Björn Roy Baron <bjorn3_gh@protonmail.com>
Cc: David Wood <david@davidtw.co>
Cc: Wesley Wiser <wwiser@gmail.com>
Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs).
Link: rust-lang/rust#140872 [1]
Link: rust-lang/rust#93798 [2]
Link: rust-lang/rust#91504 [3]
Link: https://godbolt.org/z/sxzWTMfzW [4]
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
ojeda added a commit to Rust-for-Linux/linux that referenced this pull request Jul 14, 2025
… 1.89.0

Starting with Rust 1.89.0 (expected 2025-08-07), the Rust compiler fails
to build the `rusttest` target due to undefined references such as:

    kernel...-cgu.0:(.text....+0x116): undefined reference to
    `rust_helper_kunit_get_current_test'

Moreover, tooling like `modpost` gets confused:

    WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/gpu/drm/nova/nova.o
    ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/nova-core/nova_core.o

The reason behind both issues is that the Rust compiler will now [1]
treat `#[used]` as `#[used(linker)]` instead of `#[used(compiler)]`
for our targets. This means that the retain section flag (`R`,
`SHF_GNU_RETAIN`) will be used and that they will be marked as `unique`
too, with different IDs. In turn, that means we end up with undefined
references that did not get discarded in `rusttest` and that multiple
`.modinfo` sections are generated, which confuse tooling like `modpost`
because they only expect one.

Thus start using `#[used(compiler)]` to keep the previous behavior
and to be explicit about what we want. Sadly, it is an unstable feature
(`used_with_arg`) [2] -- we will talk to upstream Rust about it. The good
news is that it has been available for a long time (Rust >= 1.60) [3].

The changes should also be fine for previous Rust versions, since they
behave the same way as before [4].

Alternatively, we could use `#[no_mangle]` or `#[export_name = ...]`
since those still behave like `#[used(compiler)]`, but of course it is
not really what we want to express, and it requires other changes to
avoid symbol conflicts.

Cc: Björn Roy Baron <bjorn3_gh@protonmail.com>
Cc: David Wood <david@davidtw.co>
Cc: Wesley Wiser <wwiser@gmail.com>
Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs).
Link: rust-lang/rust#140872 [1]
Link: rust-lang/rust#93798 [2]
Link: rust-lang/rust#91504 [3]
Link: https://godbolt.org/z/sxzWTMfzW [4]
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Link: https://lore.kernel.org/r/20250712160103.1244945-3-ojeda@kernel.org
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
ojeda added a commit to Rust-for-Linux/linux that referenced this pull request Jul 14, 2025
… 1.89.0

Starting with Rust 1.89.0 (expected 2025-08-07), the Rust compiler fails
to build the `rusttest` target due to undefined references such as:

    kernel...-cgu.0:(.text....+0x116): undefined reference to
    `rust_helper_kunit_get_current_test'

Moreover, tooling like `modpost` gets confused:

    WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/gpu/drm/nova/nova.o
    ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/nova-core/nova_core.o

The reason behind both issues is that the Rust compiler will now [1]
treat `#[used]` as `#[used(linker)]` instead of `#[used(compiler)]`
for our targets. This means that the retain section flag (`R`,
`SHF_GNU_RETAIN`) will be used and that they will be marked as `unique`
too, with different IDs. In turn, that means we end up with undefined
references that did not get discarded in `rusttest` and that multiple
`.modinfo` sections are generated, which confuse tooling like `modpost`
because they only expect one.

Thus start using `#[used(compiler)]` to keep the previous behavior
and to be explicit about what we want. Sadly, it is an unstable feature
(`used_with_arg`) [2] -- we will talk to upstream Rust about it. The good
news is that it has been available for a long time (Rust >= 1.60) [3].

The changes should also be fine for previous Rust versions, since they
behave the same way as before [4].

Alternatively, we could use `#[no_mangle]` or `#[export_name = ...]`
since those still behave like `#[used(compiler)]`, but of course it is
not really what we want to express, and it requires other changes to
avoid symbol conflicts.

Acked-by: Björn Roy Baron <bjorn3_gh@protonmail.com>
Cc: David Wood <david@davidtw.co>
Cc: Wesley Wiser <wwiser@gmail.com>
Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs).
Link: rust-lang/rust#140872 [1]
Link: rust-lang/rust#93798 [2]
Link: rust-lang/rust#91504 [3]
Link: https://godbolt.org/z/sxzWTMfzW [4]
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Link: https://lore.kernel.org/r/20250712160103.1244945-3-ojeda@kernel.org
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
ojeda added a commit to Rust-for-Linux/linux that referenced this pull request Jul 14, 2025
… 1.89.0

Starting with Rust 1.89.0 (expected 2025-08-07), the Rust compiler fails
to build the `rusttest` target due to undefined references such as:

    kernel...-cgu.0:(.text....+0x116): undefined reference to
    `rust_helper_kunit_get_current_test'

Moreover, tooling like `modpost` gets confused:

    WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/gpu/drm/nova/nova.o
    ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/nova-core/nova_core.o

The reason behind both issues is that the Rust compiler will now [1]
treat `#[used]` as `#[used(linker)]` instead of `#[used(compiler)]`
for our targets. This means that the retain section flag (`R`,
`SHF_GNU_RETAIN`) will be used and that they will be marked as `unique`
too, with different IDs. In turn, that means we end up with undefined
references that did not get discarded in `rusttest` and that multiple
`.modinfo` sections are generated, which confuse tooling like `modpost`
because they only expect one.

Thus start using `#[used(compiler)]` to keep the previous behavior
and to be explicit about what we want. Sadly, it is an unstable feature
(`used_with_arg`) [2] -- we will talk to upstream Rust about it. The good
news is that it has been available for a long time (Rust >= 1.60) [3].

The changes should also be fine for previous Rust versions, since they
behave the same way as before [4].

Alternatively, we could use `#[no_mangle]` or `#[export_name = ...]`
since those still behave like `#[used(compiler)]`, but of course it is
not really what we want to express, and it requires other changes to
avoid symbol conflicts.

Cc: David Wood <david@davidtw.co>
Cc: Wesley Wiser <wwiser@gmail.com>
Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs).
Link: rust-lang/rust#140872 [1]
Link: rust-lang/rust#93798 [2]
Link: rust-lang/rust#91504 [3]
Link: https://godbolt.org/z/sxzWTMfzW [4]
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Acked-by: Björn Roy Baron <bjorn3_gh@protonmail.com>
Link: https://lore.kernel.org/r/20250712160103.1244945-3-ojeda@kernel.org
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
ojeda added a commit to Rust-for-Linux/linux that referenced this pull request Jul 14, 2025
… 1.89.0

Starting with Rust 1.89.0 (expected 2025-08-07), the Rust compiler fails
to build the `rusttest` target due to undefined references such as:

    kernel...-cgu.0:(.text....+0x116): undefined reference to
    `rust_helper_kunit_get_current_test'

Moreover, tooling like `modpost` gets confused:

    WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/gpu/drm/nova/nova.o
    ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/nova-core/nova_core.o

The reason behind both issues is that the Rust compiler will now [1]
treat `#[used]` as `#[used(linker)]` instead of `#[used(compiler)]`
for our targets. This means that the retain section flag (`R`,
`SHF_GNU_RETAIN`) will be used and that they will be marked as `unique`
too, with different IDs. In turn, that means we end up with undefined
references that did not get discarded in `rusttest` and that multiple
`.modinfo` sections are generated, which confuse tooling like `modpost`
because they only expect one.

Thus start using `#[used(compiler)]` to keep the previous behavior
and to be explicit about what we want. Sadly, it is an unstable feature
(`used_with_arg`) [2] -- we will talk to upstream Rust about it. The good
news is that it has been available for a long time (Rust >= 1.60) [3].

The changes should also be fine for previous Rust versions, since they
behave the same way as before [4].

Alternatively, we could use `#[no_mangle]` or `#[export_name = ...]`
since those still behave like `#[used(compiler)]`, but of course it is
not really what we want to express, and it requires other changes to
avoid symbol conflicts.

Cc: David Wood <david@davidtw.co>
Cc: Wesley Wiser <wwiser@gmail.com>
Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs).
Link: rust-lang/rust#140872 [1]
Link: rust-lang/rust#93798 [2]
Link: rust-lang/rust#91504 [3]
Link: https://godbolt.org/z/sxzWTMfzW [4]
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Acked-by: Björn Roy Baron <bjorn3_gh@protonmail.com>
Link: https://lore.kernel.org/r/20250712160103.1244945-3-ojeda@kernel.org
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
ojeda added a commit to ojeda/linux that referenced this pull request Jul 21, 2025
… 1.89.0

Starting with Rust 1.89.0 (expected 2025-08-07), the Rust compiler fails
to build the `rusttest` target due to undefined references such as:

    kernel...-cgu.0:(.text....+0x116): undefined reference to
    `rust_helper_kunit_get_current_test'

Moreover, tooling like `modpost` gets confused:

    WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/gpu/drm/nova/nova.o
    ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/nova-core/nova_core.o

The reason behind both issues is that the Rust compiler will now [1]
treat `#[used]` as `#[used(linker)]` instead of `#[used(compiler)]`
for our targets. This means that the retain section flag (`R`,
`SHF_GNU_RETAIN`) will be used and that they will be marked as `unique`
too, with different IDs. In turn, that means we end up with undefined
references that did not get discarded in `rusttest` and that multiple
`.modinfo` sections are generated, which confuse tooling like `modpost`
because they only expect one.

Thus start using `#[used(compiler)]` to keep the previous behavior
and to be explicit about what we want. Sadly, it is an unstable feature
(`used_with_arg`) [2] -- we will talk to upstream Rust about it. The good
news is that it has been available for a long time (Rust >= 1.60) [3].

The changes should also be fine for previous Rust versions, since they
behave the same way as before [4].

Alternatively, we could use `#[no_mangle]` or `#[export_name = ...]`
since those still behave like `#[used(compiler)]`, but of course it is
not really what we want to express, and it requires other changes to
avoid symbol conflicts.

Cc: David Wood <david@davidtw.co>
Cc: Wesley Wiser <wwiser@gmail.com>
Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs).
Link: rust-lang/rust#140872 [1]
Link: rust-lang/rust#93798 [2]
Link: rust-lang/rust#91504 [3]
Link: https://godbolt.org/z/sxzWTMfzW [4]
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Acked-by: Björn Roy Baron <bjorn3_gh@protonmail.com>
Link: https://lore.kernel.org/r/20250712160103.1244945-3-ojeda@kernel.org
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
(cherry picked from commit 7498159)
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
github-actions bot pushed a commit to joseflrs/a4 that referenced this pull request Jul 22, 2025
… 1.89.0

commit 7498159 upstream.

Starting with Rust 1.89.0 (expected 2025-08-07), the Rust compiler fails
to build the `rusttest` target due to undefined references such as:

    kernel...-cgu.0:(.text....+0x116): undefined reference to
    `rust_helper_kunit_get_current_test'

Moreover, tooling like `modpost` gets confused:

    WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/gpu/drm/nova/nova.o
    ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/nova-core/nova_core.o

The reason behind both issues is that the Rust compiler will now [1]
treat `#[used]` as `#[used(linker)]` instead of `#[used(compiler)]`
for our targets. This means that the retain section flag (`R`,
`SHF_GNU_RETAIN`) will be used and that they will be marked as `unique`
too, with different IDs. In turn, that means we end up with undefined
references that did not get discarded in `rusttest` and that multiple
`.modinfo` sections are generated, which confuse tooling like `modpost`
because they only expect one.

Thus start using `#[used(compiler)]` to keep the previous behavior
and to be explicit about what we want. Sadly, it is an unstable feature
(`used_with_arg`) [2] -- we will talk to upstream Rust about it. The good
news is that it has been available for a long time (Rust >= 1.60) [3].

The changes should also be fine for previous Rust versions, since they
behave the same way as before [4].

Alternatively, we could use `#[no_mangle]` or `#[export_name = ...]`
since those still behave like `#[used(compiler)]`, but of course it is
not really what we want to express, and it requires other changes to
avoid symbol conflicts.

Cc: David Wood <david@davidtw.co>
Cc: Wesley Wiser <wwiser@gmail.com>
Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs).
Link: rust-lang/rust#140872 [1]
Link: rust-lang/rust#93798 [2]
Link: rust-lang/rust#91504 [3]
Link: https://godbolt.org/z/sxzWTMfzW [4]
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Acked-by: Björn Roy Baron <bjorn3_gh@protonmail.com>
Link: https://lore.kernel.org/r/20250712160103.1244945-3-ojeda@kernel.org
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
github-actions bot pushed a commit to joseflrs/a4 that referenced this pull request Jul 22, 2025
… 1.89.0

commit 7498159 upstream.

Starting with Rust 1.89.0 (expected 2025-08-07), the Rust compiler fails
to build the `rusttest` target due to undefined references such as:

    kernel...-cgu.0:(.text....+0x116): undefined reference to
    `rust_helper_kunit_get_current_test'

Moreover, tooling like `modpost` gets confused:

    WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/gpu/drm/nova/nova.o
    ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/nova-core/nova_core.o

The reason behind both issues is that the Rust compiler will now [1]
treat `#[used]` as `#[used(linker)]` instead of `#[used(compiler)]`
for our targets. This means that the retain section flag (`R`,
`SHF_GNU_RETAIN`) will be used and that they will be marked as `unique`
too, with different IDs. In turn, that means we end up with undefined
references that did not get discarded in `rusttest` and that multiple
`.modinfo` sections are generated, which confuse tooling like `modpost`
because they only expect one.

Thus start using `#[used(compiler)]` to keep the previous behavior
and to be explicit about what we want. Sadly, it is an unstable feature
(`used_with_arg`) [2] -- we will talk to upstream Rust about it. The good
news is that it has been available for a long time (Rust >= 1.60) [3].

The changes should also be fine for previous Rust versions, since they
behave the same way as before [4].

Alternatively, we could use `#[no_mangle]` or `#[export_name = ...]`
since those still behave like `#[used(compiler)]`, but of course it is
not really what we want to express, and it requires other changes to
avoid symbol conflicts.

Cc: David Wood <david@davidtw.co>
Cc: Wesley Wiser <wwiser@gmail.com>
Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs).
Link: rust-lang/rust#140872 [1]
Link: rust-lang/rust#93798 [2]
Link: rust-lang/rust#91504 [3]
Link: https://godbolt.org/z/sxzWTMfzW [4]
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Acked-by: Björn Roy Baron <bjorn3_gh@protonmail.com>
Link: https://lore.kernel.org/r/20250712160103.1244945-3-ojeda@kernel.org
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
chucklever pushed a commit to linux-nfs/linux-stable-rc that referenced this pull request Jul 22, 2025
… 1.89.0

commit 7498159 upstream.

Starting with Rust 1.89.0 (expected 2025-08-07), the Rust compiler fails
to build the `rusttest` target due to undefined references such as:

    kernel...-cgu.0:(.text....+0x116): undefined reference to
    `rust_helper_kunit_get_current_test'

Moreover, tooling like `modpost` gets confused:

    WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/gpu/drm/nova/nova.o
    ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/nova-core/nova_core.o

The reason behind both issues is that the Rust compiler will now [1]
treat `#[used]` as `#[used(linker)]` instead of `#[used(compiler)]`
for our targets. This means that the retain section flag (`R`,
`SHF_GNU_RETAIN`) will be used and that they will be marked as `unique`
too, with different IDs. In turn, that means we end up with undefined
references that did not get discarded in `rusttest` and that multiple
`.modinfo` sections are generated, which confuse tooling like `modpost`
because they only expect one.

Thus start using `#[used(compiler)]` to keep the previous behavior
and to be explicit about what we want. Sadly, it is an unstable feature
(`used_with_arg`) [2] -- we will talk to upstream Rust about it. The good
news is that it has been available for a long time (Rust >= 1.60) [3].

The changes should also be fine for previous Rust versions, since they
behave the same way as before [4].

Alternatively, we could use `#[no_mangle]` or `#[export_name = ...]`
since those still behave like `#[used(compiler)]`, but of course it is
not really what we want to express, and it requires other changes to
avoid symbol conflicts.

Cc: David Wood <david@davidtw.co>
Cc: Wesley Wiser <wwiser@gmail.com>
Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs).
Link: rust-lang/rust#140872 [1]
Link: rust-lang/rust#93798 [2]
Link: rust-lang/rust#91504 [3]
Link: https://godbolt.org/z/sxzWTMfzW [4]
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Acked-by: Björn Roy Baron <bjorn3_gh@protonmail.com>
Link: https://lore.kernel.org/r/20250712160103.1244945-3-ojeda@kernel.org
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
chucklever pushed a commit to linux-nfs/linux-stable-rc that referenced this pull request Jul 22, 2025
… 1.89.0

commit 7498159 upstream.

Starting with Rust 1.89.0 (expected 2025-08-07), the Rust compiler fails
to build the `rusttest` target due to undefined references such as:

    kernel...-cgu.0:(.text....+0x116): undefined reference to
    `rust_helper_kunit_get_current_test'

Moreover, tooling like `modpost` gets confused:

    WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/gpu/drm/nova/nova.o
    ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/nova-core/nova_core.o

The reason behind both issues is that the Rust compiler will now [1]
treat `#[used]` as `#[used(linker)]` instead of `#[used(compiler)]`
for our targets. This means that the retain section flag (`R`,
`SHF_GNU_RETAIN`) will be used and that they will be marked as `unique`
too, with different IDs. In turn, that means we end up with undefined
references that did not get discarded in `rusttest` and that multiple
`.modinfo` sections are generated, which confuse tooling like `modpost`
because they only expect one.

Thus start using `#[used(compiler)]` to keep the previous behavior
and to be explicit about what we want. Sadly, it is an unstable feature
(`used_with_arg`) [2] -- we will talk to upstream Rust about it. The good
news is that it has been available for a long time (Rust >= 1.60) [3].

The changes should also be fine for previous Rust versions, since they
behave the same way as before [4].

Alternatively, we could use `#[no_mangle]` or `#[export_name = ...]`
since those still behave like `#[used(compiler)]`, but of course it is
not really what we want to express, and it requires other changes to
avoid symbol conflicts.

Cc: David Wood <david@davidtw.co>
Cc: Wesley Wiser <wwiser@gmail.com>
Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs).
Link: rust-lang/rust#140872 [1]
Link: rust-lang/rust#93798 [2]
Link: rust-lang/rust#91504 [3]
Link: https://godbolt.org/z/sxzWTMfzW [4]
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Acked-by: Björn Roy Baron <bjorn3_gh@protonmail.com>
Link: https://lore.kernel.org/r/20250712160103.1244945-3-ojeda@kernel.org
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
github-actions bot pushed a commit to joseflrs/a4 that referenced this pull request Jul 22, 2025
… 1.89.0

commit 7498159 upstream.

Starting with Rust 1.89.0 (expected 2025-08-07), the Rust compiler fails
to build the `rusttest` target due to undefined references such as:

    kernel...-cgu.0:(.text....+0x116): undefined reference to
    `rust_helper_kunit_get_current_test'

Moreover, tooling like `modpost` gets confused:

    WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/gpu/drm/nova/nova.o
    ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/nova-core/nova_core.o

The reason behind both issues is that the Rust compiler will now [1]
treat `#[used]` as `#[used(linker)]` instead of `#[used(compiler)]`
for our targets. This means that the retain section flag (`R`,
`SHF_GNU_RETAIN`) will be used and that they will be marked as `unique`
too, with different IDs. In turn, that means we end up with undefined
references that did not get discarded in `rusttest` and that multiple
`.modinfo` sections are generated, which confuse tooling like `modpost`
because they only expect one.

Thus start using `#[used(compiler)]` to keep the previous behavior
and to be explicit about what we want. Sadly, it is an unstable feature
(`used_with_arg`) [2] -- we will talk to upstream Rust about it. The good
news is that it has been available for a long time (Rust >= 1.60) [3].

The changes should also be fine for previous Rust versions, since they
behave the same way as before [4].

Alternatively, we could use `#[no_mangle]` or `#[export_name = ...]`
since those still behave like `#[used(compiler)]`, but of course it is
not really what we want to express, and it requires other changes to
avoid symbol conflicts.

Cc: David Wood <david@davidtw.co>
Cc: Wesley Wiser <wwiser@gmail.com>
Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs).
Link: rust-lang/rust#140872 [1]
Link: rust-lang/rust#93798 [2]
Link: rust-lang/rust#91504 [3]
Link: https://godbolt.org/z/sxzWTMfzW [4]
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Acked-by: Björn Roy Baron <bjorn3_gh@protonmail.com>
Link: https://lore.kernel.org/r/20250712160103.1244945-3-ojeda@kernel.org
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
github-actions bot pushed a commit to joseflrs/a4 that referenced this pull request Jul 22, 2025
… 1.89.0

commit 7498159 upstream.

Starting with Rust 1.89.0 (expected 2025-08-07), the Rust compiler fails
to build the `rusttest` target due to undefined references such as:

    kernel...-cgu.0:(.text....+0x116): undefined reference to
    `rust_helper_kunit_get_current_test'

Moreover, tooling like `modpost` gets confused:

    WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/gpu/drm/nova/nova.o
    ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/nova-core/nova_core.o

The reason behind both issues is that the Rust compiler will now [1]
treat `#[used]` as `#[used(linker)]` instead of `#[used(compiler)]`
for our targets. This means that the retain section flag (`R`,
`SHF_GNU_RETAIN`) will be used and that they will be marked as `unique`
too, with different IDs. In turn, that means we end up with undefined
references that did not get discarded in `rusttest` and that multiple
`.modinfo` sections are generated, which confuse tooling like `modpost`
because they only expect one.

Thus start using `#[used(compiler)]` to keep the previous behavior
and to be explicit about what we want. Sadly, it is an unstable feature
(`used_with_arg`) [2] -- we will talk to upstream Rust about it. The good
news is that it has been available for a long time (Rust >= 1.60) [3].

The changes should also be fine for previous Rust versions, since they
behave the same way as before [4].

Alternatively, we could use `#[no_mangle]` or `#[export_name = ...]`
since those still behave like `#[used(compiler)]`, but of course it is
not really what we want to express, and it requires other changes to
avoid symbol conflicts.

Cc: David Wood <david@davidtw.co>
Cc: Wesley Wiser <wwiser@gmail.com>
Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs).
Link: rust-lang/rust#140872 [1]
Link: rust-lang/rust#93798 [2]
Link: rust-lang/rust#91504 [3]
Link: https://godbolt.org/z/sxzWTMfzW [4]
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Acked-by: Björn Roy Baron <bjorn3_gh@protonmail.com>
Link: https://lore.kernel.org/r/20250712160103.1244945-3-ojeda@kernel.org
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
firmware-kpd bot pushed a commit to linux-kdevops/linux-firmware-kpd that referenced this pull request Jul 23, 2025
… 1.89.0

Starting with Rust 1.89.0 (expected 2025-08-07), the Rust compiler fails
to build the `rusttest` target due to undefined references such as:

    kernel...-cgu.0:(.text....+0x116): undefined reference to
    `rust_helper_kunit_get_current_test'

Moreover, tooling like `modpost` gets confused:

    WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/gpu/drm/nova/nova.o
    ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/nova-core/nova_core.o

The reason behind both issues is that the Rust compiler will now [1]
treat `#[used]` as `#[used(linker)]` instead of `#[used(compiler)]`
for our targets. This means that the retain section flag (`R`,
`SHF_GNU_RETAIN`) will be used and that they will be marked as `unique`
too, with different IDs. In turn, that means we end up with undefined
references that did not get discarded in `rusttest` and that multiple
`.modinfo` sections are generated, which confuse tooling like `modpost`
because they only expect one.

Thus start using `#[used(compiler)]` to keep the previous behavior
and to be explicit about what we want. Sadly, it is an unstable feature
(`used_with_arg`) [2] -- we will talk to upstream Rust about it. The good
news is that it has been available for a long time (Rust >= 1.60) [3].

The changes should also be fine for previous Rust versions, since they
behave the same way as before [4].

Alternatively, we could use `#[no_mangle]` or `#[export_name = ...]`
since those still behave like `#[used(compiler)]`, but of course it is
not really what we want to express, and it requires other changes to
avoid symbol conflicts.

Cc: David Wood <david@davidtw.co>
Cc: Wesley Wiser <wwiser@gmail.com>
Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs).
Link: rust-lang/rust#140872 [1]
Link: rust-lang/rust#93798 [2]
Link: rust-lang/rust#91504 [3]
Link: https://godbolt.org/z/sxzWTMfzW [4]
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Acked-by: Björn Roy Baron <bjorn3_gh@protonmail.com>
Link: https://lore.kernel.org/r/20250712160103.1244945-3-ojeda@kernel.org
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
(cherry picked from commit 7498159)
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
Whissi pushed a commit to Whissi/linux-stable that referenced this pull request Jul 24, 2025
… 1.89.0

commit 7498159 upstream.

Starting with Rust 1.89.0 (expected 2025-08-07), the Rust compiler fails
to build the `rusttest` target due to undefined references such as:

    kernel...-cgu.0:(.text....+0x116): undefined reference to
    `rust_helper_kunit_get_current_test'

Moreover, tooling like `modpost` gets confused:

    WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/gpu/drm/nova/nova.o
    ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/nova-core/nova_core.o

The reason behind both issues is that the Rust compiler will now [1]
treat `#[used]` as `#[used(linker)]` instead of `#[used(compiler)]`
for our targets. This means that the retain section flag (`R`,
`SHF_GNU_RETAIN`) will be used and that they will be marked as `unique`
too, with different IDs. In turn, that means we end up with undefined
references that did not get discarded in `rusttest` and that multiple
`.modinfo` sections are generated, which confuse tooling like `modpost`
because they only expect one.

Thus start using `#[used(compiler)]` to keep the previous behavior
and to be explicit about what we want. Sadly, it is an unstable feature
(`used_with_arg`) [2] -- we will talk to upstream Rust about it. The good
news is that it has been available for a long time (Rust >= 1.60) [3].

The changes should also be fine for previous Rust versions, since they
behave the same way as before [4].

Alternatively, we could use `#[no_mangle]` or `#[export_name = ...]`
since those still behave like `#[used(compiler)]`, but of course it is
not really what we want to express, and it requires other changes to
avoid symbol conflicts.

Cc: David Wood <david@davidtw.co>
Cc: Wesley Wiser <wwiser@gmail.com>
Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs).
Link: rust-lang/rust#140872 [1]
Link: rust-lang/rust#93798 [2]
Link: rust-lang/rust#91504 [3]
Link: https://godbolt.org/z/sxzWTMfzW [4]
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Acked-by: Björn Roy Baron <bjorn3_gh@protonmail.com>
Link: https://lore.kernel.org/r/20250712160103.1244945-3-ojeda@kernel.org
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
gregkh pushed a commit to gregkh/linux that referenced this pull request Jul 24, 2025
… 1.89.0

commit 7498159 upstream.

Starting with Rust 1.89.0 (expected 2025-08-07), the Rust compiler fails
to build the `rusttest` target due to undefined references such as:

    kernel...-cgu.0:(.text....+0x116): undefined reference to
    `rust_helper_kunit_get_current_test'

Moreover, tooling like `modpost` gets confused:

    WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/gpu/drm/nova/nova.o
    ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/nova-core/nova_core.o

The reason behind both issues is that the Rust compiler will now [1]
treat `#[used]` as `#[used(linker)]` instead of `#[used(compiler)]`
for our targets. This means that the retain section flag (`R`,
`SHF_GNU_RETAIN`) will be used and that they will be marked as `unique`
too, with different IDs. In turn, that means we end up with undefined
references that did not get discarded in `rusttest` and that multiple
`.modinfo` sections are generated, which confuse tooling like `modpost`
because they only expect one.

Thus start using `#[used(compiler)]` to keep the previous behavior
and to be explicit about what we want. Sadly, it is an unstable feature
(`used_with_arg`) [2] -- we will talk to upstream Rust about it. The good
news is that it has been available for a long time (Rust >= 1.60) [3].

The changes should also be fine for previous Rust versions, since they
behave the same way as before [4].

Alternatively, we could use `#[no_mangle]` or `#[export_name = ...]`
since those still behave like `#[used(compiler)]`, but of course it is
not really what we want to express, and it requires other changes to
avoid symbol conflicts.

Cc: David Wood <david@davidtw.co>
Cc: Wesley Wiser <wwiser@gmail.com>
Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs).
Link: rust-lang/rust#140872 [1]
Link: rust-lang/rust#93798 [2]
Link: rust-lang/rust#91504 [3]
Link: https://godbolt.org/z/sxzWTMfzW [4]
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Acked-by: Björn Roy Baron <bjorn3_gh@protonmail.com>
Link: https://lore.kernel.org/r/20250712160103.1244945-3-ojeda@kernel.org
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
opsiff pushed a commit to opsiff/UOS-kernel that referenced this pull request Jul 27, 2025
… 1.89.0

commit 7498159226772d66f150dd406be462d75964a366 upstream.

Starting with Rust 1.89.0 (expected 2025-08-07), the Rust compiler fails
to build the `rusttest` target due to undefined references such as:

    kernel...-cgu.0:(.text....+0x116): undefined reference to
    `rust_helper_kunit_get_current_test'

Moreover, tooling like `modpost` gets confused:

    WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/gpu/drm/nova/nova.o
    ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/nova-core/nova_core.o

The reason behind both issues is that the Rust compiler will now [1]
treat `#[used]` as `#[used(linker)]` instead of `#[used(compiler)]`
for our targets. This means that the retain section flag (`R`,
`SHF_GNU_RETAIN`) will be used and that they will be marked as `unique`
too, with different IDs. In turn, that means we end up with undefined
references that did not get discarded in `rusttest` and that multiple
`.modinfo` sections are generated, which confuse tooling like `modpost`
because they only expect one.

Thus start using `#[used(compiler)]` to keep the previous behavior
and to be explicit about what we want. Sadly, it is an unstable feature
(`used_with_arg`) [2] -- we will talk to upstream Rust about it. The good
news is that it has been available for a long time (Rust >= 1.60) [3].

The changes should also be fine for previous Rust versions, since they
behave the same way as before [4].

Alternatively, we could use `#[no_mangle]` or `#[export_name = ...]`
since those still behave like `#[used(compiler)]`, but of course it is
not really what we want to express, and it requires other changes to
avoid symbol conflicts.

Cc: David Wood <david@davidtw.co>
Cc: Wesley Wiser <wwiser@gmail.com>
Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs).
Link: rust-lang/rust#140872 [1]
Link: rust-lang/rust#93798 [2]
Link: rust-lang/rust#91504 [3]
Link: https://godbolt.org/z/sxzWTMfzW [4]
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Acked-by: Björn Roy Baron <bjorn3_gh@protonmail.com>
Link: https://lore.kernel.org/r/20250712160103.1244945-3-ojeda@kernel.org
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
(cherry picked from commit d9ebd928288bb82df8efeb3a34f2cd31883f440e)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-attributes Area: Attributes (`#[…]`, `#![…]`) A-linkage Area: linking into static, shared libraries and binaries A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. F-used_with_arg `#![feature(used_with_arg)]` merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

#[link_section] does not work in libraries on release
9 participants