adhoc executable patching on nixos
’one weird trick’ for getting that ELF executable someone just handed you working soonβ’
Create a shell.nix
file that looks like:
let pkgs = import <nixpkgs> {}; in pkgs.mkShell { nativeBuildInputs = (with pkgs; [ gcc # example libraries you might want: zlib SDL2 ]); }
Launch the nix-shell, patch the linker so you can call the executable and see what’s up:
; nix-shell shell.nix ; patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" /path/to/executable
Strategies to find out what you might need:
; ldd /path/to/executable ; strace /path/to/executable
Update the shell.nix
file with the dependencies/linked libraries your executable might need, and then patch the executable from the derived nix-shell based on that:
; nix-shell ./shell.nix ; # you may want to peek at new_rpath here or mangle NIX_LDFLAGS yourself ; new_rpath=$(echo "$NIX_LDFLAGS" | tr ' ' $'\n' | grep "^-L" | sed -E 's/^-L/:/' | tr -d $'\n') ; patchelf --set-rpath "$new_rpath" /path/to/executable
Then you should be able to run the executable by calling it normally (in or out of a nix-shell context).
That’s all well and good for right now, but what if we lose the nix store references we just patched in! Plus, we can’t exactly carry this around. Let’s note how to do the above as a build definition (the example here is a dynamically-linked release of babashka):
babashka = stdenv.mkDerivation rec { name = "babashka"; # this is the way to coerce a string into a path # src = /. + "/home/neeasade/temps/2020-04-20_07:21:05/bb"; # showing a few different ways: # src = /. + "/home/neeasade/temps/2020-04-20_07:21:05/babashka-0.0.88-linux-amd64.zip"; src = (fetchurl { url = "https://github.com/borkdude/babashka/releases/download/v0.1.3/babashka-0.1.3-linux-amd64.zip"; sha256 = "0nldq063a1sfk0qnkd37dpw8jq43p4divn4j4qiif6dy1qz9xdcq"; }); unpackPhase = "unzip $src"; # if src is a path to the executable: # unpackPhase = "cp $src bb"; # note: the chmod is only needed when using direct path to local executable patchPhase = '' chmod u+w ./bb patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" ./bb new_rpath=$(echo "$NIX_LDFLAGS" | tr ' ' $'\n' | grep "^-L" | sed -E 's/^-L/:/' | tr -d $'\n') patchelf --set-rpath "$new_rpath" ./bb ''; dontBuild = true; installPhase = '' mkdir -p $out/bin cp bb $out/bin ''; nativeBuildInputs = (with pkgs; [ gcc-unwrapped.lib zlib unzip ]); };
And bam! now we can integrate our sins into our package definitions.
autopatchelf, which appears to try and do our first attempt automatically.
There is also the very cool looking Another solution has come up: This popped up on my feed today and is also a cool solution to this problem: