The File Exists, So Why ENOENT?#
A dynamically linked ELF file needs not only the executable itself but also the interpreter recorded in the ELF. Ordinary x86_64 Linux distributions often place the glibc loader at /lib64/ld-linux-x86-64.so.2; packaged NixOS programs usually refer directly to a loader in /nix/store. When an unadapted downloaded binary cannot find its hard-coded loader path, execve returns ENOENT, and the shell may report No such file or directory.
The following historical example output illustrates this behavior; it is not a record from this machine:
$ ./masterpdfeditor5
bash: ./masterpdfeditor5: No such file or directory
$ ls -la ./masterpdfeditor5
-rwxr-xr-x 1 user users 27160344 Jul 4 16:22 ./masterpdfeditor5From System Calls to the ELF Interpreter
strace -f ./masterpdfeditor5
file ./masterpdfeditor5
patchelf --print-interpreter ./masterpdfeditor5A typical diagnostic sequence looks like this, with addresses, build IDs, and store hashes omitted:
execve("./masterpdfeditor5", ["./masterpdfeditor5"], ...) = -1 ENOENT (No such file or directory)
strace: exec: No such file or directory
masterpdfeditor5: ELF 64-bit LSB executable, x86-64, dynamically linked,
interpreter /lib64/ld-linux-x86-64.so.2, strippedfile is a separately packaged tool, not part of binutils. strace shows system calls and return values; file and patchelf help distinguish problems with file permissions, architecture, interpreters, and dependent libraries. A missing loader path is only one cause of ENOENT; a script shebang pointing to a nonexistent interpreter can produce a similar error.
Compare this with a packaged system program:
patchelf --print-interpreter /run/current-system/sw/bin/lsIts loader is often /nix/store/<hash>-glibc-<version>/lib/ld-linux-x86-64.so.2. This is a difference in NixOS's path layout; NixOS itself is also Linux.
The Packaging-Time Solution: autoPatchelfHook#
For closed-source software with fixed release archives, autoPatchelfHook is a Nixpkgs build hook that locates binary dependencies, rewrites the ELF interpreter, and sets library search paths during packaging. The appropriate dependencies must still be supplied in buildInputs; the hook does not conjure up downloads of missing libraries.
The historical notes use Master PDF Editor to illustrate the rewritten RPATH:
nix-shell -p masterpdfeditor patchelf
command -v masterpdfeditor5
# 对真正的 ELF 文件检查;外层可能是启动 wrapper
patchelf --print-rpath /nix/store/<hash>-masterpdfeditor-<version>/bin/.masterpdfeditor5-wrappedPackage names, versions, and wrapped filenames depend on the Nixpkgs revision in use. The key point of the historical output is that RPATH contains multiple Nix store library directories:
/nix/store/<hash>-sane-backends-<version>/lib:
/nix/store/<hash>-zlib-<version>/lib:
/nix/store/<hash>-qtsvg-5.15/lib:
/nix/store/<hash>-qtbase-5.15/lib:
/nix/store/<hash>-qtdeclarative-5.15/lib:
/nix/store/<hash>-libglvnd-<version>/lib:
/nix/store/<hash>-gcc-<version>-lib/libThe line breaks above are only for readability; the actual RPATH is one colon-separated line. Qt5 was a dependency of this historical package, which does not mean all current versions still use Qt5.
When Is Patching Every Time Impractical?
- Executables downloaded by editor extensions or tools such as npm and pip must be patched again after updates.
- Java JARs or other archives may contain binaries that are not extracted until runtime.
- Games or proprietary software may verify binary integrity and refuse to start after modification.
- Very large software, such as FPGA IDEs, can be inconvenient to copy into the store and then process.
nix-ld's Shim Mechanism#
nix-ld is placed at the path used by ordinary distributions' loaders, then delegates to the real dynamic loader in the Nix store. It preserves the contents of unpatched files and does not require copying the entire software package into the store.
NIX_LD specifies the real loader; NIX_LD_LIBRARY_PATH specifies additional library directories, separated by colons, not commas. The shim supplies library search paths to the loader. Upstream also supports architecture-specific variables such as NIX_LD_x86_64_linux; the suffix replaces hyphens in the system identifier with underscores.
System Module Configuration
{ pkgs, ... }: {
programs.nix-ld.enable = true;
programs.nix-ld.libraries = with pkgs; [
stdenv.cc.cc
zlib
fuse3
icu
nss
openssl
curl
expat
# 按应用缺失依赖继续添加
];
}Run the target binary after the module takes effect. Merely writing the configuration to a file or building the system does not mean the shim is enabled. The dependency list above is a starting point, not a guarantee that every commercial application will run directly.
The original article was written in 2022 and discussed library configuration in the then-upcoming 23.05 release. The release preview is presented here as historical context. The original author's extended configuration is available in Mic92's nix-ld module.
Project-Local Environment Variables
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
NIX_LD = pkgs.stdenv.cc.bintools.dynamicLinker;
NIX_LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [
pkgs.stdenv.cc.cc
pkgs.openssl
];
}If the shim is present but the loader configuration is missing, the error may explicitly state that NIX_LD has not been configured. Check the configuration according to the messages from your installed version.
The Boundary with Python Virtual Environments
Nixpkgs Python is already linked to a loader in the store; setting NIX_LD_LIBRARY_PATH does not automatically route it through nix-ld. If a PyPI extension module lacks libraries, prepare the correct system libraries and runtime environment locally for the project. Setting LD_LIBRARY_PATH globally may inject incorrect versions into programs that are already packaged. For specific Python/Qt troubleshooting, see Python Environment Configuration and Qt Troubleshooting.
Choosing Between steam-run and Packaging#
For occasional use of unadapted software, consider the FHS environment provided by steam-run:
steam-run ./my-appThe command requires steam-run to be available already. It provides a compatibility environment whose scope differs from nix-ld; when packaging, autoPatchelfHook or other Nixpkgs build mechanisms are usually preferred.
For everyday software such as QQ, WeChat, and VS Code, first look for an existing package in the Nixpkgs package search, then choose a compatibility approach based on its dependencies. nix-ld addresses dynamic-loader and library-path problems; it does not automatically fix missing resources, kernel features, every hard-coded /usr/bin path, or other ABI differences. Hard-coded script paths are a separate topic; see envfs for more.
Sources and Scope#
This article combines basic configuration notes with the original nix-ld article published by Jörg Thalheim and Jill Thornhill on 2022-12-31. It removes duplicated Chinese/English passages while preserving the full technical progression through system calls, ELF, RPATH, use cases, and configuration. Commands and output are explanatory examples, not results verified on this machine.
References & further reading
Adapted from technical notes. Examples use generic names.