Fetching the path to the linux dynamic loader
Closed this issue · 3 comments
Dynamically loading functions at runtime requires to have the dynamic loader path in the ELF file (at least on Linux).
In an ELF file, it is done by adding a .interp
section which points to the path of the dynamic loader. While this can be hardcoded as /lib64/ld-linux-x86-64.so.2
for example, this does not work on all Linux distributions (like NixOS). On NixOS, there is a tool called patchelf
which is in charge of updating hardcoded paths such as this one.
While this works, there may be better alternatives, like:
- using a shell command to fetch the file (e.g.
find / -path '*ld-linux*.so.? -type f
This is very slow (it has to traverse the entire filesystem) and does not necessarily works great (you can have multiple entries, or not handle different versions, etc) - hardcoding the path unless a flag like
--dynamic-loader=path/to/ld-linux.so.N
is specified
This should work, unless the path specified is invalid - automatically retrieving the path through for example an environment variable
NSTAR_DYN_LOADER
which would be set on installation (requiring some way to fetch the path anyway, at some point)
One of the downside of having the --dynamic-loader
option is that if there are any breaking change between major versions of glibc, it won't really handle them properly.
A downside of having an environment variable for this is that the compiler depends entirely on the users' environment which may not be a great thing to depend on. You also have to fetch the path to the dynamic loader at some point, and there doesn't seem to really be one working for all distributions (or at least I haven't found one).
The (probably) best alternative would be to hardcode the path to the dynamic loader, and, on NixOS at least, use patchelf
with the hook, or use the --dynamic-loader
(automatically set to ${pkgs.glibc}/bin/ld-linux-<arch>.so.N
) in a wrapper.
Seems like gcc
follows the --dynamic-linker
approach to the problem, with a default argument /lib64/ld-linux-x86-64.so.2
or /lib/ld-linux.so.2
(for either x86 or x64, I don't know for other architectures).
I believe this is the correct way to go, this way we can just wrap the executable on NixOS (the only problem would be how to fetch the dynamic linker consistently, depending on x86 or x64, we could set it to ${pkgs.glibc}/lib/ld-linux-x86-64.so.2
or ${pkgs.glibc}/lib/ld-linux.so.2
I believe -- maybe even replacing the .2
with the major version of ${pkgs.glibc}
).
Default paths used by gcc
:
- https://github.com/gcc-mirror/gcc/blob/887515acd27e49c176395ab76d5826959d89cb9b/gcc/config/i386/linux.h#L23
/lib/ld-linux.so.2
(for x86) - https://github.com/gcc-mirror/gcc/blob/887515acd27e49c176395ab76d5826959d89cb9b/gcc/config/i386/linux64.h#L31
/lib/ld-linux-x86-64.so.2
(for x64)
These paths are then patched in the Nix derivation here : https://github.com/NixOS/nixpkgs/blob/6b47c89f66ee9d3b9896c231e0dcaee8697d91e1/pkgs/development/compilers/gcc/10/default.nix#L123-L131 (mostly just adding the prefix to ${pkgs.glibc}
right before the #define
d value)
In fact, because we are only generating relocatable ELF object files, and linking them to the C-runtime, ld
already does this for us, which saves us some time here.