Running binaries without FHS and patchELF


#1

First, join the dark side by adding these lines to your configuration.nix:

  environment.extraInit = with pkgs; let loader = "ld-linux-x86-64.so.2"; in ''
    export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/run/current-system/sw/lib"
    ln -fs ${pkgs.glibc}/lib/${loader} /lib/${loader}
  '';

This will link the the loader into /lib/ld-linux-x86-64.so.2. With the power it gives you no longer need FHS or patchELF to run a binary.

Though you still need to set the environment. So create a nix file with something like this:

with import <nixpkgs> {};

stdenv.mkDerivation rec {
  name = "<env-name>";

  nativeBuildInputs = [
    pulseaudio freetype xorg.libX11
    xorg.libXcomposite xorg.libXcursor xorg.libXdamage
    xorg.libXext xorg.libXfixes xorg.libXi xorg.libXrandr xorg.libXrender
    xorg.libXtst xorg.libxcb xorg.xcbutilkeysyms xorg.libXxf86vm libglvnd
  ];

  LD_LIBRARY_PATH = builtins.foldl'
    (a: b: "${a}:${b}/lib") "/run/opengl-driver/lib" nativeBuildInputs;
}

Run the file with nix-shell and you’ll get into a shell where you can just run the binary.

You can run the binary instead of entering into the shell by adding shellHook = <command> into the nix file. And you can move the nix file into $HOME/.local/nix-shell/, create $HOME/.local/bin/<name> with exec nix-shell $HOME/.local/nix-shell/<filename>.nix in it, add $HOME/.local/bin/ to the $PATH and voilà.


#2

This might cause problems when incompatible libraries are added to the library path.


#3

But libraries from nativeBuildInputs are not really incompatible? And AFAIK these libraries load other libraries using absolute paths.


#4

I mean that the rpath of an executable will have a lower priority then LD_LIBRARY_PATH, which might be a problem if you try to run an executable that is linked against an older library, because LD_LIBRARY_PATH would force the newer version.


#5

http://man7.org/linux/man-pages/man8/ld.so.8.html

If a shared object dependency does not contain a slash, then it is
searched for in the following order:

  • Using the directories specified in the DT_RPATH dynamic section
    attribute of the binary if present and DT_RUNPATH attribute does
    not exist. Use of DT_RPATH is deprecated.
  • Using the environment variable LD_LIBRARY_PATH, unless the
    executable is being run in secure-execution mode (see below), in
    which case this variable is ignored.
  • Using the directories specified in the DT_RUNPATH dynamic section
    attribute of the binary if present. Such directories are searched
    only to find those objects required by DT_NEEDED (direct
    dependencies) entries and do not apply to those objects’ children,
    which must themselves have their own DT_RUNPATH entries. This is
    unlike DT_RPATH, which is applied to searches for all children in
    the dependency tree.
  • From the cache file /etc/ld.so.cache, which contains a compiled
    list of candidate shared objects previously found in the augmented
    library path. If, however, the binary was linked with the -z
    nodeflib linker option, shared objects in the default paths are
    skipped. Shared objects installed in hardware capability
    directories (see below) are preferred to other shared objects.
  • In the default path /lib, and then /usr/lib. (On some 64-bit
    architectures, the default paths for 64-bit shared objects are
    /lib64, and then /usr/lib64.) If the binary was linked with the
    -z nodeflib linker option, this step is skipped.

So if nix indeed sets RPATH instead of RUNPATH, everything should be fine.

UPDATE:

This is unlike DT_RPATH, which is applied to searches for all children in the dependency tree.

Oh. So nix indeed should set RUNPATH. All right, there’s indeed a problem with using LD_LIBRARY_PATH.