First I am not really comfortable with ELF and all this library linking process. But after having played a bit with Nix/NixOS, there is something I still don't really understand with the way graphic drivers are linked into binaries.
One of the designs of Nix/NixOS is that the OpenGL driver cannot be part of the reproducible environment because it will depend on the target hardware.
Most Nix programs find OpenGL drivers into "/run/opengl-driver/lib".
In the case of a NixOS system, drivers are downloaded and installed at this path and then the LD_LIBRARY_PATH
can be defined globally in the configuration using hardware.opengl.setLdLibraryPath = true
(see https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/hardware/opengl.nix#L166).
However, in the case of a non-NixOS operating system, I can notice that programs still reference this /run/opengl-driver/lib
path while LD_LIBRARY_PATH
is not defined anywhere. And so my question is: why does a binary still search drivers into /run/opengl-driver/lib on a non-NixOS system?
My example here is compton. Its derivation in Nixpkgs is at the following address.
I can see using strace that the program tries to load the libGLX_mesa.so
library at runtime.
$ strace -e openat $(readlink -f $(which compton)) 2>&1 | grep -E ".*opengl.*.so"
openat(AT_FDCWD, "/run/opengl-driver/lib/tls/haswell/x86_64/libGLX_mesa.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/run/opengl-driver/lib/tls/haswell/libGLX_mesa.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/run/opengl-driver/lib/tls/x86_64/libGLX_mesa.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/run/opengl-driver/lib/tls/libGLX_mesa.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/run/opengl-driver/lib/haswell/x86_64/libGLX_mesa.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/run/opengl-driver/lib/haswell/libGLX_mesa.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/run/opengl-driver/lib/x86_64/libGLX_mesa.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/run/opengl-driver/lib/libGLX_mesa.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
Indeed, using ldd
reveals that libGLX_mesa.so
is not dynamically linked.
$ ldd $(readlink -f $(which compton)) | grep mesa | wc -l
0
So my guess is that this library is loaded at runtime maybe using dlopen
. I cannot find any reference of dlopen
in https://github.com/chjj/compton but maybe it is done indirectly from one of the libs linked by compton.
If my guess is true, I have found that dlopen
was able to find the desired library searching into paths defined in the LD_LIBRARY_PATH
environment variable (when the path is a leaf name).
So my next move was to analyse the environment variables available to compton at runtime using one of the two commands below.
$ strace -v -s 10000 -e execve $(readlink -f $(which compton)) 2>&1 | grep LD_LIBRARY_PATH | wc -l
0
$ ltrace -e getenv $(readlink -f $(which compton)) 2>&1 | grep LD_LIBRARY_PATH | wc -l
0
But this LD_LIBRARY_PATH
does not seem to be defined. And I am kind of stuck in my analysis here.
So to recap, my questions are:
- How is this opengl path
/run/opengl-driver/lib
injected into compton? - What would be the standard way of adding this library path to programs requiring graphic drivers in Nix?
- And last question not really related to this analysis but: if wanting to make compton work on a non-NixOS system, would it be enough to manually link opengl libraries from
/usr/lib/x86_64-linux-gnu/
to/run/opengl-drivers/lib/
?