I am trying to create a derivation to compile my Rust project with static linking to glibc. This works in cargo-land by adding RUSTFLAGS="-C target-feature=+crt-static".
In my derivation (which is in a flake), I have packages.default using pkgs.rustPlatform.buildRustPackage.
I set it up how I usually do for other projects and it works as long as I don’t add the flag above.
However, when using the above flag and during linking, I get the following errors:
> /nix/store/74y3751gsixaz9797ib0hp7c658sp1y5-binutils-2.40/bin/ld: cannot find -lrt: No such file or directory
> /nix/store/74y3751gsixaz9797ib0hp7c658sp1y5-binutils-2.40/bin/ld: cannot find -lpthread: No such file or directory
> /nix/store/74y3751gsixaz9797ib0hp7c658sp1y5-binutils-2.40/bin/ld: cannot find -lm: No such file or directory
> /nix/store/74y3751gsixaz9797ib0hp7c658sp1y5-binutils-2.40/bin/ld: cannot find -ldl: No such file or directory
> /nix/store/74y3751gsixaz9797ib0hp7c658sp1y5-binutils-2.40/bin/ld: cannot find -lc: No such file or directory
> /nix/store/74y3751gsixaz9797ib0hp7c658sp1y5-binutils-2.40/bin/ld: cannot find -lc: No such file or directory
> collect2: error: ld returned 1 exit status
I tried to do the following:
Add pkg-config to nativeBuildInputs (this is true for all cases)
Add rustPlatform.bindgenHook to nativeBuildInputs
Add clang and/or libclang to nativeBuildInputs and/or buildInputs
Add glibc or glibc.static to nativeBuildInputs and/or buildInputs
Add override { stdenv = pkgs.clangStdenv; } to buildRustPackage
Add LD_LIBRARY_PATH = lib.makeLibraryPath [ pkgs.clang pkgs.libclang pkgs.glibc.static ] to the inputs of buildRustPackage.
Ah that’s a good point! I guess the binary appears to be statically linked but will still call out to glibc and would not run on a machine with an older glibc? @beeb did you test that?
I have almost 0 darwin experience, so I’m not sure what’s happening in your stdenv.
I guess the binary appears to be statically linked but will still call out to glibc and would not run on a machine with an older glibc?
I don’t think so. If you managed to build this from Darwin, it’s likely static for real. My huntch would be Darwin’s default libc can be statically linked to Rust programs, unlike Glibc. Maybe somebody reading this more familiar with Darwin could enlighten us.
$ otool -L result/bin/rust-nix-static
result/bin/rust-nix-static:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1292.60.1)
/nix/store/02jndc2wwsfigf12wv53k8zwyfvwl9gf-libiconv-50/lib/libiconv.dylib (compatibility version 7.0.0, current version 7.0.0)
The standard library in general strives to support both statically linked and dynamically linked C runtimes for targets as appropriate.
[…]
The linkage of the C runtime is configured to respect the crt-static target feature.
[…]
Targets which do not support switching between linkage of the C runtime will ignore this flag. It’s recommended to inspect the resulting binary to ensure that it’s linked as you would expect after the compiler succeeds.
So, actually,
might be incorrect. It could’ve just silently ignored the flag and created a dynamically linked binary anyway.
Nixpkgs seems to set this flag by default when you’re using musl, but I can’t see anything that would specifically cause the error message you’ve shown.
I nudged a colleague in the meantime, who reminded me that the Darwin ABI is not stable (like all BSDs). So this behavior makes a lot of sense: the ABI is unstable, a statically-linked libc would only be compatible with a single kernel version.
anw, TIL
The default Linux stdenv is using Glibc, not Musl. The original snippet was trying to statically link Glibc, hence the error.
You can for sure statically link glibc and I did it using cargo build with the appropriate compiler flags on target x86_64-unknown-linux-gnu.
$ file ./myexecutable
./myexecutable: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), static-pie linked, for GNU/Linux 3.10.0, stripped
$ ldd ./myexecutable
statically linked
The executable works fine in all environments where I tested it, while the dynamically linked binary was not working due to a glibc version mismatch or something else, can’t remember right now.
So my question still stands, what am I missing in my flake to make this work? I had issues using MUSL with one of the crates I was using so I think I need to link against glibc.
Oh sorry, yeah that seems legit. I tried it now on my machine and can 100% reproduce this. Regular build works fine and yields a static binary, nix build works without the envvar, but crashes with the same message you observed.
In addition to your attempted solutions, I also tried explicitly setting the linker with CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER to both gcc and clang’s ld, and switching the build to nearsk, but to no success.
I feel like there’s some issue with the way the way the command line arguments are built up. They might be in the wrong order, so ld confuses some flags for filepaths? I’m really out of my comfort zone here, so just a shot in the dark.
I tried running nix develop -i .#packages.x86_64-linux.default and then run the stages separately, but that seemed to work mostly fine, so it’s not a good representation of the process.
It’s also a little surprising that the build doesn’t use the .cargo/config.toml file, Nix does copy it into the build directory during the unpackPhase.
It’s also a little surprising that the build doesn’t use the .cargo/config.toml file, Nix does copy it into the build directory during the unpackPhase.
From what I read, this function doesn’t use cargo and instead calls to rustc directly, so that could explain why the config file is not used.
I have a machine where I can’t compile with cargo build --release (presumably because I’m missing the glibc system-wide) and I noticed that I can fix it if I add pkgs.glibc.static to my dev shell prior to launching cargo. So that’s another data point, it seems it should be able to link properly if pkgs.glibc.static is present, but doesn’t do it from within the buildRustPackage function.