mirror of
https://github.com/NixOS/nixpkgs.git
synced 2026-10-30 16:10:36 +00:00
To decide whether a library directory is in the store, and so belongs in
the rpath, ld-wrapper canonicalizes its path with `realpath -s`, and
then, while it is outside the store, follows it if it is a symlink. But
the linker searches the directory the kernel resolves the path to, and
the kernel follows each symlink in the path, including those of
ancestor directories, before going on to the next component, so `..`
applies to the target of a symlink. So ld-wrapper got these wrong:
- A symlink into the store, as in -L/build/link with /build/link
pointing to /nix/store/...-foo/lib: after following it, ld-wrapper set
dir to the literal string "dir2" rather than "$dir2", so it never
added /nix/store/...-foo/lib to the rpath. It would also have
resolved a relative target against the current directory, rather than
the directory containing the symlink.
- A symlink into the store from an ancestor directory, as in
-L/build/link/lib with /build/link pointing to /nix/store/...-foo: the
path is not a symlink itself, so ld-wrapper treated it as outside the
store.
- A `..` after a symlink, as in -L/build/link/../lib with /build/link
pointing to /nix/store/...-foo/bin: `realpath -s` removed link/..
without following link, so ld-wrapper looked at /build/lib instead of
/nix/store/...-foo/lib. The same goes for a symlink in the store,
where the result can be a different store path than the one the
linker searched.
Instead, resolve the path one component at a time, with a new
resolveLibDir function, following each symlink as the kernel does. But
in the store, still keep a symlink that is not followed by `..` as it is,
since resolving symlinks between store paths would refer to their
targets instead of the store paths given (#454199). And never resolve
the store or its ancestors, as `realpath -s` did not, so that the store
is still recognized when /nix is a symlink, which Nix allows with
NIX_IGNORE_SYMLINK_STORE. Like the linker, and as `realpath -s` did,
take a relative path, as in `ld -L lib`, from the working directory.
As a result, ld-wrapper also no longer runs `realpath` for every library
directory, which it has since #472652, slowing down every link
(#545973). It runs `readlink` only to follow a symlink. Linking an
empty C program now starts 7 processes instead of 15.
Test all of these cases, including the tests from #566486 for symlinks
with absolute and relative targets, and test that ld-wrapper keeps a
symlink between store paths in the rpath, that it recognizes a store
whose parent is a symlink, and that it resolves relative directories
from the working directory.
Co-authored-by: Robert Hensing <robert@roberthensing.nl>
Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Anders Kaseorg <andersk@mit.edu>
(cherry picked from commit 88c2064d4b)