Skip to content

rust-lld default on x86_64-unknown-linux-gnu produces binaries without RUNPATH on NixOS #162781

Description

@zackees

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).

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-linkageArea: linking into static, shared libraries and binariesC-discussionCategory: Discussion or questions that doesn't represent real issues.O-NixOSOperating system: NixOS, https://nixos.org/

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions