You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Don't leak non-exported symbols from staticlibs #104707
When compiling a cdylib only #[no_mangle] symbols are exported. #[rustc_std_internal_symbol] and mangled symbols are not exported. This prevents symbol conflicts and avoids overriding symbols in ways that causes UB when using a rust cdylib in a rust program. For staticlibs however all symbols leak out of the staticlib. Causing symbol overrides that are potentially UB and symbols conflicts. For example when statically linking spidermonkey, you will get symbol conflicts if the rust version you compile your program with doesn't match the one the rust parts of spidermonkey were compiled with.
FTR, I'm unsure there is a way to limit exported symbols from a staticlib, without both limiting the number of CGUs within the crate to 1 and preventing any upstream crates from using the #[global_allocator]. There is further limited ways to prevent upstream crate symbols from being exported. My Original Post was made with that in mind.
Maybe partial linking would work? If not we will at least need to version all #[rustc_std_internal_symbol]s for each rustc version to prevent UB if different rustc versions implement them in backwards incompatible ways. And maybe we can edit every object file to rename symbols to be unique? By the way note that for cdylib and staticlib all rust crate dependencies are included directly in the cdylib/staticlib. No dynamic linking supported.
IDK whether all linkers support partial linking (in particular, I don't know about microsoft's link.exe). Though, personally, I don't particularily want to touch ld -r with a 10' pole anyways if I don't have to. The versioning would work, if done within the mangling (for mangled symbols).
By the way note that for cdylib and staticlib all rust crate dependencies are included directly in the cdylib/staticlib. No dynamic linking supported.
I plan to do it slightly differently when possible - for staticlibs resolve the dependencies and produce a links.o "object" that is just a linker script. For cdylibs, just link as normal and when dynamically linking, add DT_RPATH (to the stdlib directory)+DT_NEEDED as needed.
for staticlibs resolve the dependencies and produce a links.o "object" that is just a linker script.
That is a backward incompatible change for rustc
For cdylibs, just link as normal and when dynamically linking, add DT_RPATH (to the stdlib directory)+DT_NEEDED as needed.
That will share and thus leak the global state of libstd across the cdylib boundary. This among other things will break the mitigation of #102721 to prevent catching foreign rust panics.
On Tue, Nov 22, 2022 at 07:28 bjorn3 ***@***.***> wrote:
for staticlibs resolve the dependencies and produce a links.o "object"
that is just a linker script.
That is a backward incompatible change for rustc
rustc currently just emits the library directly into the archive, right?
I'm not particularly sure what the difference here is, except in terms of
file size, and the fact that using libstd.so.0.1 is an option in addition
to libstd.rlib.0.1.
For cdylibs, just link as normal and when dynamically linking, add
DT_RPATH (to the stdlib directory)+DT_NEEDED as needed.
That will share and thus leak the global state of libstd across the cdylib
boundary.
That is true, though I'm unsure how it is avoidable - even when statically
linking the symbols from libstd et. al need to have default visibility so
that when linking into a dylib, the symbols can be used via the dylib. It
would not make a difference on ELF.
rustc currently just emits the library directly into the archive, right?
I'm not particularly sure what the difference here is, except in terms of
file size, and the fact that using libstd.so.0.1 is an option in addition
to libstd.rlib.0.1.
The difference is that currently you can ship a staticlib as a standalone file and expect linking to succeed, but with your proposal you also need all dependencies to be available at exactly the same place.
That is true, though I'm unsure how it is avoidable - even when statically
linking the symbols from libstd et. al need to have default visibility so
that when linking into a dylib, the symbols can be used via the dylib. It
would not make a difference on ELF.
When linking a cdylib, libstd is statically linked and none of it's symbols are exported from the cdylib. When linking a rust dylib, sharing state is just fine. In fact you aren't allowed to duplicate crates in that case.
The difference is that currently you can ship a staticlib as a standalone file and expect linking to succeed, but with your proposal you also need all dependencies to be available at exactly the same place.
Fair enough, though the result is potentially shipping GiB for an API surface that should take MiB.
When linking a cdylib, libstd is statically linked and none of it's symbols are exported from the cdylib. When linking a rust dylib, sharing state is just fine. In fact you aren't allowed to duplicate crates in that case.
On ELF I'm unsure how this would be achieved. Symbol visibilty is controlled when the symbol is defined (in the object file), and I just send everything on to ld (post processing? What is that? I only know "add head and tail libraries to link line"). ELF shared objects don't have an "Export List", the dynamic symbol list is just built from the static symbol list usually excluding internal and hidden symbols. Every GLOBAL or WEAK symbol in the list with PROTECTED or DEFAULT visibility can be imported from the cdylib and every symbol with DEFAULT visibilty can additonally be replaced. These are functions of the link editor producing the files and the dynamic linker-loader resolving runtime relocations, and are far from under the control of rust as a language or any particular implementation.
Rustc passes a version script to the linker specifying exactly which symbols to export and making it to hide everything else. This is but one of the reasons rustc is in change of invoking the linker.
Rustc passes a version script to the linker specifying exactly which symbols to export and making it to hide everything else. This is but one of the reasons rustc is in change of invoking the linker.
Ah. My problem is that I have to support link editors that are like "Version Script? What is this? Expected PHDRS, MEMORY, or SECTIONS"
Linker scripts and versions scripts are different. Linker scripts tell what should be put where in the linked artifact. Version scripts only list which symbols are exported and which aren't amd optionally provide a version for the purpose of symbol versioning. The format of version scripts is trivial in comparison to linker scripts. See
I am aware of version scripts. The simplicity is not the problem. The problem is if I'm faced with a link editor that doesn't support them, which I cannot always assume.
Hmm... I'm actually not sure. Some quick research on autoconf says only GNU ld + solaris LD (and lld supports it, as will lcld). I guess older platforms may have none of the above, but IDK how old you have to get. I'm sure given enough time I could find a counterexample, but I don't want to look rn.
We are shipping a pre-compiled Rust library that some of our clients want to use again from Rust. The leaked symbols were causing issues, so we tried @zeozeozeo's tool (https://github.com/zeozeozeo/staticlib-fucker) to hide them.
As we are shipping our library across so many different targets, we needed to come up with a cross platform solution. After working on it for nearly a year and shipping all of our libraries patched with this tool, we are confident that it works well across all of our targets.
So we decided to open source it!
Please use of it and let us know if you hit any limitations or bugs 🙂
Since #155338 we have the unstable -Zstaticlib-hide-internal-symbols option to prevent exporting symbols from dylibs (ELF and Mach-O only) and since #156950 we have -Zstaticlib-rename-internal-symbols to rename symbols to prevent conflicts (ELF and Mach-O currently, #160679 will add PE/COFF support).
Reacted by Cherry, Weihang Lo, teor, Diana, Oreste Bernardi and Jackson Coxson
Reacted by Stephan Eckes, Kevin Nakamura, Diana, Oreste Bernardi and Ian Cullinan
Activity
bjorn3 commentedon Nov 22, 2022
cc @chorman0773
chorman0773 commentedon Nov 22, 2022
FTR, I'm unsure there is a way to limit exported symbols from a staticlib, without both limiting the number of CGUs within the crate to 1 and preventing any upstream crates from using the
#[global_allocator]. There is further limited ways to prevent upstream crate symbols from being exported. My Original Post was made with that in mind.bjorn3 commentedon Nov 22, 2022
Maybe partial linking would work? If not we will at least need to version all
#[rustc_std_internal_symbol]s for each rustc version to prevent UB if different rustc versions implement them in backwards incompatible ways. And maybe we can edit every object file to rename symbols to be unique? By the way note that for cdylib and staticlib all rust crate dependencies are included directly in the cdylib/staticlib. No dynamic linking supported.chorman0773 commentedon Nov 22, 2022
IDK whether all linkers support partial linking (in particular, I don't know about microsoft's link.exe). Though, personally, I don't particularily want to touch
ld -rwith a 10' pole anyways if I don't have to. The versioning would work, if done within the mangling (for mangled symbols).I plan to do it slightly differently when possible - for staticlibs resolve the dependencies and produce a
links.o"object" that is just a linker script. For cdylibs, just link as normal and when dynamically linking, add DT_RPATH (to the stdlib directory)+DT_NEEDED as needed.bjorn3 commentedon Nov 22, 2022
That is a backward incompatible change for rustc
That will share and thus leak the global state of libstd across the cdylib boundary. This among other things will break the mitigation of #102721 to prevent catching foreign rust panics.
chorman0773 commentedon Nov 22, 2022
bjorn3 commentedon Nov 22, 2022
The difference is that currently you can ship a staticlib as a standalone file and expect linking to succeed, but with your proposal you also need all dependencies to be available at exactly the same place.
When linking a cdylib, libstd is statically linked and none of it's symbols are exported from the cdylib. When linking a rust dylib, sharing state is just fine. In fact you aren't allowed to duplicate crates in that case.
chorman0773 commentedon Nov 22, 2022
Fair enough, though the result is potentially shipping GiB for an API surface that should take MiB.
On ELF I'm unsure how this would be achieved. Symbol visibilty is controlled when the symbol is defined (in the object file), and I just send everything on to ld (post processing? What is that? I only know "add head and tail libraries to link line"). ELF shared objects don't have an "Export List", the dynamic symbol list is just built from the static symbol list usually excluding internal and hidden symbols. Every GLOBAL or WEAK symbol in the list with PROTECTED or DEFAULT visibility can be imported from the cdylib and every symbol with DEFAULT visibilty can additonally be replaced. These are functions of the link editor producing the files and the dynamic linker-loader resolving runtime relocations, and are far from under the control of rust as a language or any particular implementation.
bjorn3 commentedon Nov 22, 2022
Rustc passes a version script to the linker specifying exactly which symbols to export and making it to hide everything else. This is but one of the reasons rustc is in change of invoking the linker.
chorman0773 commentedon Nov 22, 2022
Ah. My problem is that I have to support link editors that are like "Version Script? What is this? Expected PHDRS, MEMORY, or SECTIONS"
bjorn3 commentedon Nov 22, 2022
Linker scripts and versions scripts are different. Linker scripts tell what should be put where in the linked artifact. Version scripts only list which symbols are exported and which aren't amd optionally provide a version for the purpose of symbol versioning. The format of version scripts is trivial in comparison to linker scripts. See
rust/compiler/rustc_codegen_ssa/src/back/linker.rs
Lines 666 to 724 in b7463e8
chorman0773 commentedon Nov 22, 2022
I am aware of version scripts. The simplicity is not the problem. The problem is if I'm faced with a link editor that doesn't support them, which I cannot always assume.
bjorn3 commentedon Nov 22, 2022
Which linker doesn't support it? AFAIK every platform targeted by rustc has a linker supporting them or some other way to hide symbols.
chorman0773 commentedon Nov 22, 2022
Hmm... I'm actually not sure. Some quick research on autoconf says only GNU ld + solaris LD (and lld supports it, as will lcld). I guess older platforms may have none of the above, but IDK how old you have to get. I'm sure given enough time I could find a counterexample, but I don't want to look rn.
52 remaining items
refactor(android): split C++ into wzp-native cdylib, loaded at runtime
phase 2(android): port Oboe C++ bridge + audio FFI into wzp-native
Rollup merge of rust-lang#155338 - cezarbbb:staticlib-symbol-hygiene,…
Rollup merge of rust-lang#155338 - cezarbbb:staticlib-symbol-hygiene,…
Rollup merge of rust-lang#155338 - cezarbbb:staticlib-symbol-hygiene,…
Rollup merge of rust-lang#155338 - cezarbbb:staticlib-symbol-hygiene,…
Unrolled build for #155338
Rollup merge of #155338 - cezarbbb:staticlib-symbol-hygiene, r=petroc…
Rollup merge of #155338 - cezarbbb:staticlib-symbol-hygiene, r=petroc…
steckes commentedon Sep 15, 2026
We are shipping a pre-compiled Rust library that some of our clients want to use again from Rust. The leaked symbols were causing issues, so we tried @zeozeozeo's tool (https://github.com/zeozeozeo/staticlib-fucker) to hide them.
As we are shipping our library across so many different targets, we needed to come up with a cross platform solution. After working on it for nearly a year and shipping all of our libraries patched with this tool, we are confident that it works well across all of our targets.
So we decided to open source it!
Please use of it and let us know if you hit any limitations or bugs 🙂
https://github.com/ai-coustics/lib-patcher
decentral1se commentedon Sep 15, 2026
Amazing @steckes, you're all heroes. I will be sure to test this out in the coming days 🙏
apiraino commentedon Sep 15, 2026
rust-mod action: some messages were temporarily marked as possible adverstisement. Feel free to get in contact for a review.
bjorn3 commentedon Sep 15, 2026
Since #155338 we have the unstable
-Zstaticlib-hide-internal-symbolsoption to prevent exporting symbols from dylibs (ELF and Mach-O only) and since #156950 we have-Zstaticlib-rename-internal-symbolsto rename symbols to prevent conflicts (ELF and Mach-O currently, #160679 will add PE/COFF support).steckes commentedon Sep 15, 2026
Yes and that is is amazing, can't wait for this to not be an issue anymore! :)
decentral1se commentedon Oct 11, 2026
I tested #104707 (comment) and it solved the problems I am facing: https://git.coopcloud.tech/decentral1se/p2panda-loro-go
Thank you all very much!