Running binaries without FHS and patchELF

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à.

1 Like

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

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

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.

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.