Since 1.90 (#140525) rustc links x86_64-unknown-linux-gnu binaries with the self-contained rust-lld. On NixOS this silently changes the output of any crate that links a system shared library: the binary no longer gets a RUNPATH, and it fails to start.
Background
NixOS has no /usr/lib. The cc on PATH is a nixpkgs wrapper whose companion ld wrapper appends -rpath <dir> for each -L directory in /nix/store that provides a linked -l library (ld-wrapper.sh). That is how every binary linked there finds its libraries at run time.
rust-lld is not reached through that ld wrapper, so no RUNPATH is written. The cc wrapper still passes -dynamic-linker for the Nix store glibc, so the binary is not a "foreign" one either, and NixOS's nix-ld fallback does not apply. The result links cleanly and dies at startup.
Reproduction
NixOS 26.05, rustup stable rustc 1.95.0 (59807616e 2026-04-14), LLVM 22.1.2, nixpkgs gcc-wrapper-15.2.0 as cc. Outside any nix-shell, with PKG_CONFIG_PATH pointing at the store's openssl/zlib/sqlite .pc directories:
[dependencies]
openssl-sys = "=0.9.117"
libz-sys = { version = "=1.1.29", default-features = false, features = ["libc"] }
libsqlite3-sys = "=0.38.2"
fn main() {
unsafe {
openssl_sys::init();
let v = std::ffi::CStr::from_ptr(openssl_sys::OpenSSL_version(0));
let z = std::ffi::CStr::from_ptr(libz_sys::zlibVersion());
let s = std::ffi::CStr::from_ptr(libsqlite3_sys::sqlite3_libversion());
println!("openssl={v:?} zlib={z:?} sqlite={s:?}");
}
}
cargo build
readelf -p .comment target/debug/app | grep Linker # Linker: LLD 22.1.2
readelf -d target/debug/app | grep RUNPATH # (nothing)
ldd target/debug/app # libssl.so.3 => not found, libcrypto.so.3 => not found, libsqlite3.so => not found
./target/debug/app # error while loading shared libraries: libsqlite3.so ... (exit 127)
The same build through the same cc, opting out of the self-contained linker:
RUSTFLAGS="-Clinker-features=-lld -Clink-self-contained=-linker" cargo build
readelf -d target/debug/app | grep RUNPATH # [/nix/store/…-sqlite-3.51.2/lib:/nix/store/…-openssl-3.6.3/lib:…]
./target/debug/app # openssl="OpenSSL 3.6.3 …" zlib="1.3.2" sqlite="3.51.2"
| Linker |
RUNPATH |
ldd not found |
Run |
| rust-lld (default) |
none |
3 |
exit 127 |
| GNU ld via the nixpkgs wrapper |
store library dirs |
0 |
exit 0 |
The same happens with clang-sys (libclang.so.21.1: cannot open shared object file), and with tools that inspect the result: a Python wheel backend that runs ldd to bundle OpenSSL failed because ldd reported it missing.
What would help
Any one of these:
- Document the NixOS interaction next to the lld default and its opt-out, since the failure shows up at run time rather than link time.
- When
cc is a nixpkgs wrapper (e.g. it has a nix-support/ directory beside it), don't pick the self-contained linker, or warn that RUNPATH handling is lost.
- Provide a hook for linker-wrapper rpath semantics when rust-lld replaces the system
ld.
Workarounds (verified)
-Clinker-features=-lld -Clink-self-contained=-linker restores the wrapper's behaviour.
- Put
-rpath <dir> in the cc wrapper's cc-ldflags, which the cc wrapper passes to whatever linker it calls, rust-lld included. That is what this machine does now: zackees/nixos@91d74c1
Related: NixOS/nixpkgs#24744 (lld has no nixpkgs wrapper).
Since 1.90 (#140525) rustc links
x86_64-unknown-linux-gnubinaries with the self-contained rust-lld. On NixOS this silently changes the output of any crate that links a system shared library: the binary no longer gets aRUNPATH, and it fails to start.Background
NixOS has no
/usr/lib. ThecconPATHis a nixpkgs wrapper whose companionldwrapper appends-rpath <dir>for each-Ldirectory in/nix/storethat provides a linked-llibrary (ld-wrapper.sh). That is how every binary linked there finds its libraries at run time.rust-lld is not reached through that
ldwrapper, so noRUNPATHis written. Theccwrapper still passes-dynamic-linkerfor the Nix store glibc, so the binary is not a "foreign" one either, and NixOS'snix-ldfallback does not apply. The result links cleanly and dies at startup.Reproduction
NixOS 26.05, rustup stable
rustc 1.95.0 (59807616e 2026-04-14), LLVM 22.1.2, nixpkgsgcc-wrapper-15.2.0ascc. Outside any nix-shell, withPKG_CONFIG_PATHpointing at the store's openssl/zlib/sqlite.pcdirectories:The same build through the same
cc, opting out of the self-contained linker:lddnot foundThe same happens with
clang-sys(libclang.so.21.1: cannot open shared object file), and with tools that inspect the result: a Python wheel backend that runslddto bundle OpenSSL failed becauselddreported it missing.What would help
Any one of these:
ccis a nixpkgs wrapper (e.g. it has anix-support/directory beside it), don't pick the self-contained linker, or warn thatRUNPATHhandling is lost.ld.Workarounds (verified)
-Clinker-features=-lld -Clink-self-contained=-linkerrestores the wrapper's behaviour.-rpath <dir>in the cc wrapper'scc-ldflags, which the cc wrapper passes to whatever linker it calls, rust-lld included. That is what this machine does now: zackees/nixos@91d74c1Related: NixOS/nixpkgs#24744 (lld has no nixpkgs wrapper).