Env variable $LD_LIBRARY_PATH is a runtime alternative to patchelf --set-rpath.
Is there something similar for pathelf --set-interpreter ?
The case is executables inside jar files downloaded from Maven.
For example GitHub - os72/protoc-jar: Protocol Buffers protobuf compiler - multi-platform executable protoc JAR and API has executables with interpreter set to /lib64/ld-linux-x86-64.so.2 so they fail to run on NixOS. patchelfing would be weird here, in that case the files under $HOME/.m2/ will have nix store paths, which could be garbage collected on the next upgrade.
I’d rather patch kernel or glibc to make such executables work than would make a cron job updating executables inside jars to the current valid interpreter.
It was tempting to use statifier to fix executables inside jars (I am mostly using a custom Maven resolver/downloader, so there is a single point for such a postprocessing), but unfortunately statifier fails to process those binaries.
Also ld-linux-x86-64.so.2 does not appear under /run/current-system so there is no “stable” path like /run/current-system/sw/lib64/ld-linux-x86-64.so.2 which could substitute /lib64/ld-linux-x86-64.so.2
Turning the question the other way around, why isn’t NixOS more compatible with the other Linux distributions?
I believe the answer is mostly historical. NixOS used to not have sandboxed builds and so the best way to catch missing build dependencies was to have them on custom paths.
Having to patchelf every executable makes it quite difficult to use NixOS when downloading custom binaries from the internet, npm, rubygems, python wheels. It adds a lot of friction to new users.
So I started playing with the idea of making NixOS compatible with the other distros. I haven’t gotten very far for now, this is how my nixos module looks like:
# Make NixOS compatible
#
# TODO: symlink /lib64/ld-linux-x86-64.so.2 to
# /run/current-system/sw/lib/ld-linux-x86_64.so.2
#
# TODO: symlink /usr/lib to /run/current-system/sw/lib
#
# TODO: symlink /bin/bash to /run/current-system/sw/bin/bash
{ pkgs, lib, ... }:
{
environment.systemPackages = [
pkgs.glibc.out
# Common /usr/lib dependencies
pkgs.zlib.out
];
# FIXME: something else is overriding that setting
# error: The unique option `environment.sessionVariables.LD_LIBRARY_PATH' is defined multiple times,
environment.sessionVariables.LD_LIBRARY_PATH =
lib.mkForce [
"/run/opengl-driver/lib"
"/run/current-system/sw/lib"
];
}
I believe the answer is mostly historical. NixOS used to not have sandboxed builds and so the best way to catch missing build dependencies was to have them on custom paths.
Not having to care if some programs need different versions of the same library is also useful in runtime
Having to patchelf every executable makes it quite difficult to use NixOS when downloading custom binaries from the internet, npm, rubygems, python wheels. It adds a lot of friction to new users.
So basically you want the default environment to be a FHSUserEnv ?
Another tool that might be useful in that space: autopatchelf <bin_path> wrapper script that would lookup all the missing .so files in the nix index and patch the binary automagically. It would probably work 99% of the time.
Then use the boot.binfmt facility to replace all the binary executions to first go through autopatchelf :-p
If it’s not turned on by default, I wouldn’t see much problems; though having binfmt tweakable in the sandbox is probably a good idea, considering otherwise it’s an impurity.
Although their rpath is empty, they somehow manage to find libpthread.so.0, libm.so.6 and libc.so.6 in nix store (I do not have systemwide $LD_LIBRARY_PATH as @zimbatm and set | grep glibc returns no result)
No, it is not relevant to Nix build process.
It is about how to run on NixOS unmodified Linux binaries when is it difficult to patch them, when they are distributed via Maven or another weird way.
I’ve had the hunch for a while that nix starts breaking down when having to deal with any sort of “packed” binaries (e.g. dependencies are calculated by looking at what hashes can be found in a result). I think it would be good to improve compatibility in ways that don’t involve modifying executables.
Though I guess the fallback continues to be FHSUserEnv or using a VM. Better seamless integration with FHSUserEnv might also be interesting?
You can run executables with your interpreter of choice by passing the executable as an argument to ld-linux. Like this:
./my-ld-linux.so.2 path/to/exe ...
This means you’d have to patch the source of the calling library instead of the executable so it might not be a better solution… But this is the runtime alternative to using --set-interpreter.