Skip to content

Include self-contained -L in native-static-libs - #159145

Open
alyssais wants to merge 1 commit into
rust-lang:mainfrom
alyssais:native-static-libs
Open

Include self-contained -L in native-static-libs#159145
alyssais wants to merge 1 commit into
rust-lang:mainfrom
alyssais:native-static-libs

Conversation

@alyssais

@alyssaisalyssais commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

View all comments

(I suggest reviewing with whitespace ignored.)

rustc uses heuristics to determine whether the self-contained path should be on the linker search path. It's important to get these right, because if self-contained is improperly missing from the search path, linking will fail (as seen here), but if it's improperly present, a broken executable can be produced due to e.g. linking two different libcs (as seen here and here). All of this works reasonably well when rustc is doing the linking, but when linking is done externally, there's a problem: there's no way for a build tool to know whether rustc would include the self-contained path or not. Without a way to find this out from rustc, the only way for a build tool to correctly link with a staticlib produced by rustc is to exactly reimplement rustc's heuristics. Not ideal.

The existing rustc --print native-static-libs option exists to enable build tools to find out what linker flags should be used when linking a rustc-produced staticlib. Without including the argument to add the self-contained directory to the search path when necessary, though, it doesn't meet its intended purpose — build tools relying on native-static-libs will still sometimes do the wrong thing. Fixing this will make it possible for build tools to link the correct self-contained libraries when necessary, without each build tool having to match rustc's heuristics itself. This will enable fixing real problems encountered with Meson, where either self-contained libraries were not found, or self-contained libraries were incorrectly linked.

@rustbotrustbot added 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 Jul 11, 2026
@rustbot

Copy link
Copy Markdown
Collaborator

r? @TaKO8Ki

rustbot has assigned @TaKO8Ki.
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

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: codegen, compiler
  • codegen, compiler expanded to 75 candidates
  • Random selection from 18 candidates

rustc uses heuristics to determine whether the self-contained path
should be on the linker search path. It's important to get these
right, because if self-contained is improperly missing from the search
path, linking will fail (as seen in [1]), but if it's improperly
present, a broken executable can be produced due to e.g. linking two
different libcs (as seen in [2][3]). All of this works reasonably
well when rustc is doing the linking, but when linking is done
externally, there's a problem: there's no way for a build tool to know
whether rustc would include the self-contained path or not. Without a
way to find this out from rustc, the only way for a build tool to
correctly link with a staticlib produced by rustc is to exactly
reimplement rustc's heuristics. Not ideal.
The existing rustc --print native-static-libs option exists to enable
build tools to find out what linker flags should be used when linking
a rustc-produced staticlib. Without including the argument to add the
self-contained directory to the search path when necessary, though, it
doesn't meet its intended purpose — build tools relying on
`native-static-libs` will still sometimes do the wrong thing. Fixing
this will make it possible for build tools to link the correct
self-contained libraries when necessary, without each build tool
having to match rustc's heuristics itself. This will enable fixing
real problems encountered with Meson, where either self-contained
libraries were not found, or self-contained libraries were incorrectly
linked.
[1]: mesonbuild/meson#15216
[2]: https://bugs.gentoo.org/970166
[3]: https://bugs.gentoo.org/967728
@alyssais
alyssaisforce-pushed the native-static-libs branch from 0d8d502 to 6d07079CompareJuly 11, 2026 18:30
@bjorn3

Copy link
Copy Markdown
Member

If you are building a staticlib, I wouldn't expect it to contain self-contained artifacts. Whatever executable/dylib you link it into already has to either provide those artifacts for itself or get them from the system even if the rust staticlib wasn't linked in. The self-contained artifacts consists of a linker, libraries and object files (think crt*.o) a regular C toolchain for the target would ship. The self-contained artifacts allow you to link without needing a C toolchain for the target. For Meson I would expect a C toolchain to already be available for the C code that gets link too. Or is the use case for this building pure Rust applications with Meson?

@alyssais

Copy link
Copy Markdown
ContributorAuthor

The staticlib in this case does not contain/bundle artifacts, but it might expect to link with e.g. rustc's self-contained libunwind. If I build a staticlib with rustc's +crt-static x86_64-unknown-linux-musl target, it's expected to be ultimately linked with libunwind even though the musl C toolchain probably doesn't provide that by default. In a mixed Rust+C program, if rustc does the final link, it will link with the self-contained libunwind. Presumably, since the libunwind requirement is coming from the Rust part, that same libunwind (from rustc's self-contained directory) should be used when the C toolchain happens to be doing the final link.

@alyssais

Copy link
Copy Markdown
ContributorAuthor

(Building pure Rust applications with Meson works fine, because then rustc does the final link.)

@bjorn3

Copy link
Copy Markdown
Member

The standard library needs either libunwind or libgcc (libgcc_eh.a or libgcc_s.so) as unwinder. It is important that only one unwinder ends up getting linked into the entire program to ensure dynamic registration of unwind tables will always be done against the same unwinder as the one that does the actual unwinding. If your C toolchain comes with libgcc instead of libunwind you should probably link against that instead. If your C toolchain provides neither, is it not capable of compiling C++ code?

@alyssais

alyssais commented Jul 12, 2026

Copy link
Copy Markdown
ContributorAuthor

I believe it provides libgcc_eh.a, but per bootstrap.example.toml, rustc will only ever use libunwind for musl +crt-static, not libgcc_eh.

rust/bootstrap.example.toml

Lines 1075 to 1077 in f0b782b

# the behavior is depend on the libc. On musl target, 'no' and 'in-tree' both
# means static link to the in-tree build of llvm libunwind, and 'system' means
# static link to `libunwind.a` provided by system. Due to the limitation of glibc,

@alyssais

Copy link
Copy Markdown
ContributorAuthor

(And if I am supposed to link against libgcc_eh anyway, that means I can't trust the output of native-static-libs, which includes -lunwind, even though it may not exist outside of self-contained.)

@TaKO8Ki

Copy link
Copy Markdown
Member

r? compiler

@rustbotrustbot assigned JohnTitor and unassigned TaKO8KiJul 21, 2026
@JohnTitor

Copy link
Copy Markdown
Member

@rustbot reroll

@rustbotrustbot assigned chenyukang and unassigned JohnTitorAug 10, 2026
@chenyukang

Copy link
Copy Markdown
Member

@rustbot reroll

@rustbotrustbot assigned adwinwhite and unassigned chenyukangAug 24, 2026
@adwinwhite

Copy link
Copy Markdown
Contributor

@rustbot reroll

@rustbotrustbot assigned mu001999 and unassigned adwinwhiteAug 25, 2026
@mu001999

Copy link
Copy Markdown
Member

@rustbot reroll

@rustbotrustbot assigned jieyouxu and unassigned mu001999Aug 25, 2026
@saethlin

Copy link
Copy Markdown
Member

r? me
cc @bjorn3 you're the better reviewer but I'm willing to give this a go to move it forward

@rustbotrustbot assigned saethlin and unassigned jieyouxuAug 26, 2026
@mati865

Copy link
Copy Markdown
Member

(And if I am supposed to link against libgcc_eh anyway, that means I can't trust the output of native-static-libs, which includes -lunwind, even though it may not exist outside of self-contained.)

Wouldn't the solution here be to link libunwind only if self-contained mode is active?
+crt-static by itself shouldn't dictate the unwind library.

@bjorn3

Copy link
Copy Markdown
Member

Wouldn't the solution here be to link libunwind only if self-contained mode is active?

If we didn't explicitly link libunwind, no unwinder would be linked at all.

@mati865

Copy link
Copy Markdown
Member

If we are not in self-contained mode the user has to supply Musl objects and libs, either by linking with {gcc,clang}-musl or {gcc,musl} on Musl based distro. In that case, linkable libgcc should be present in the system.
For self-contained mode, we always link libunwind as that's what we provide.

I forgot that we handle unwind library linking via library/unwind/src/lib.rs which complicates this approach a lot.

@bjorn3

Copy link
Copy Markdown
Member

If we are not in self-contained mode the user has to supply Musl objects and libs, either by linking with {gcc,clang}-musl or {gcc,musl} on Musl based distro. In that case, linkable libgcc should be present in the system.

If the user statically links the executable, linking libgcc is not enough. libgcc_eh also needs to be linked. And even if that may be the default of gcc and clang, we explicitly disable linking to default libraries on most targets.

@mati865

Copy link
Copy Markdown
Member

If the user statically links the executable, linking libgcc is not enough. libgcc_eh also needs to be linked.

My bad there, I thought of libgcc unwind library (libgcc{_eh,_s}) when writing libgcc.

And even if that may be the default of gcc and clang, we explicitly disable linking to default libraries on most targets.

Yeah, but then we explicitly link the unwind library.

So, ideally for musl we'd link:

+crt-static -crt-static
link-self-contained=on -Lself-contained/ -lunwind -Lself-contained/ -lunwind
link-self-contained=off -lgcc_eh -lgcc_s

@bjorn3

Copy link
Copy Markdown
Member

We don't know if the host toolchain uses libgcc or compiler-rt, right? In the latter case libunwind would still be used I think.

@mati865

Copy link
Copy Markdown
Member

We don't know if the host toolchain uses libgcc or compiler-rt, right? In the latter case libunwind would still be used I think.

Builtins and unwinder are two distinct things. First one is handled by --rtlib in Clang (GCC supports only libgcc), second one is handled by --unwindlib in Clang (again, GCC supports only libgcc_). But yeah, we don't know what the host toolchain uses, although libgcc_ is de facto dominant standard on Linux.

Perhaps we could use target-feature akin to system-llvm-libunwind std feature to control the unwinding library...

@bjorn3

Copy link
Copy Markdown
Member

Builtins and unwinder are two distinct things.

libgcc_s contains both. So you can't use libunwind when dynamically linking libgcc.

@mati865

mati865 commented Aug 26, 2026

Copy link
Copy Markdown
Member

libgcc_s contains both.

Well, yes it's a linker script that links libgcc_s.so.1 and libgcc.a. However, you can still use compiler-rt builtins by putting them first in the linker invocation. Different builtins don't conflict each other.

So you can't use libunwind when dynamically linking libgcc.

I don't understand. You cannot use libuwind when linking either libgcc unwinder, be it libgcc_s or libgcc_eh. Still you can use LLVM's libunwind.a together with libgcc.a.

EDIT: fixed typo.

@bjorn3

Copy link
Copy Markdown
Member

libgcc_s.so.1 for me does export part of the symbols of libgcc.a, so that is why I assumed they would conflict.

So you can't use libunwind when dynamically linking libgcc.

I don't understand. You cannot use libuwind when linker either libgcc unwinder, be it libgcc_s or libgcc_eh. Still you can use LLVM's libunwind.a together with libgcc.a.

If you statically link libgcc.a you have the option to use libunwind instead of libgcc_eh. If you dynamically link libgcc_s.so (which I assumed would be providing the compiler builtins instead of libgcc.a), you don't have the option to use libunwind.

@mati865

Copy link
Copy Markdown
Member

I think I see where the confusion is coming from. You could say that symbols from libgcc.a (or compiler-rt) are always linked statically, no matter what you pass to the linker because there is no .dynsym there.

So, to rephrase it. You (almost) always need the unwinder (shared: libgcc_s.so, libunwind.so or static: libgcc_eh.a, libunwind.a) and the builtins (libgcc.a or libclang_rt.builtins-<arch>.a). You can use combine them as you want as long as only a single unwinder is used. Although, there are edge cases like libgcc_s.

If we had target modifier to control the unwinder, we could let the user decide which one to use (unless I'm missing something). Rather than forcing one whether linking Musl statically or dynamically. But that's a much more complex solution.

Coming back to the solution proposed by this PR, native-static-libs predates link-self-contained a lot: #43067 So, it's easy to say why it doesn't include search dirs for it.
The idea isn't bad, but my concern is whether we are fine with exposing our self-contained contents (perhaps we are?).

Regarding https://bugs.gentoo.org/967728 I suppose self-contained makes little sense when building Rust as a system package. We need to include libunwind.a because of how we always link it for Musl when linking statically (again that problem would be gone if we could change unwinder library), but crt objects and Musl library should be rather linked from the system.

I think mesonbuild/meson#15216 is an unfortunate workaround for our shortcomings in this area, because AFAIK the files layout is an implementation detail and therefore not guaranteed.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-reviewStatus: Awaiting review from the assignee but also interested parties.T-compilerRelevant 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.

12 participants

@alyssais@rustbot@bjorn3@TaKO8Ki@JohnTitor@chenyukang@adwinwhite@mu001999@saethlin@mati865@petrochenkov@jieyouxu