rust-lang / rust-lang/rust

Consider documenting: rust-lld default (1.90+) disables LTO for cc-rs consumers using GCC -flto -ffat-lto-objects

Open Beginner friendly
#159,355 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-docs needs-triage
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

Location (URL)

https://blog.rust-lang.org/2025/09/01/rust-lld-on-1.90.0-stable/

Summary

Just to preface, I'm not sure whether this is the right place/tag with which to file. It seems like I'm running into expected behavior that I just don't see documented anywhere, but I'd like to check with the broader community first. If this is already known/documented somewhere upstream that I missed, a pointer would be great. Happy to move to a different repo/tag if there's a better home.

Issue

We recently bumped MSRV on the s2n-tls Rust bindings from 1.89 to 1.91. Afterwards, every TLS handshake benchmark in our daily CI regressed by ~2-4% (~20 µs/handshake).

Bisecting inside the MSRV bump window pointed at the toolchain bump itself as the trigger, and once I ran with rust-lld explicitly, I was able to pinpoint the cause of the regression cleanly.

Root cause

This seems to happen because s2n-tls-sys's build.rs compiles the vendored libs2n C code with -flto -ffat-lto-objects when the C compiler is GCC-like. GCC produces fat objects with the compiled function code in .gnu.lto_* (GIMPLE bytecode) sections. Under bfd, collect2 auto-loads GCC's liblto_plugin.so, which consumes the GIMPLE and performs cross-file LTO. Under LLD (the x86_64-unknown-linux-gnu default as of Rust 1.90), the plugin protocol isn't implemented and, per LLD's own docs, LLD's LTO story seems primarily scoped to LLVM bitcode from Clang. The .gnu.lto_* sections get silently discarded, only the native-code fallback in the fat objects is linked, and the build succeeds with no warning. However, the resulting binary is measurably slower.

The behavior itself is a straightforward consequence of rust-lld's design and LLD's LTO scope. But I couldn't find a note anywhere connecting "1.90 lld default" to "cc-rs + GCC + fat-LTO consumers silently lose their LTO."

Minimal Repro

// foo.c
int foo(int x) { return x * 2; }
int foo_impl(int x) { return foo(x) + 1; }

// main.c
extern int foo_impl(int);
int main() { return foo_impl(1); }

gcc -c -O2 -flto -ffat-lto-objects foo.c main.c
ar rcs libfoo.a foo.o main.o

# Path A: bfd + liblto_plugin.so
gcc -O2 -flto -o out_bfd main.o foo.o
# objdump: main = mov $0x3; ret        (LTO folded foo_impl(1) → 3)

# Path B: rustc → rust-lld on the same archive (what cc-rs consumers actually get)
# Wrapper crate links libfoo.a via `cargo:rustc-link-lib=static=foo`,
# built on 1.90+ x86_64-unknown-linux-gnu with default linker.
# objdump: main = jmp foo_impl         (no LTO; native fallback only)

Possible Outcomes

  • If this is already documented somewhere I missed could someone point me at it?
  • If it's expected but undocumented and worth noting a note in the 1.90 blog post under known incompatibilities, the release notes, or the platform support docs would help the next person hit this once rather than try and root cause it. The blog already recommends RUSTFLAGS="-Clinker-features=-lld" as the general opt-out; the ask is just to acknowledge this specific class of pattern as one of the expected cases.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read the linked Rust 1.90 rust-lld announcement, then compare RELEASES.md and the platform support documentation as possible locations. Document the interaction between rust-lld, GCC -flto -ffat-lto-objects archives, and cc-rs consumers, including the RUSTFLAGS opt-out; done means the expected limitation and workaround are clearly discoverable.

Written by the indexing model from the issue text.

Assessment

Tech stack
c, rust
Domain
build-system, documentation
Issue type
Documentation
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.