Skip to content

add fallback mechanism if environment variables are unset #53

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 32 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ Also read this [blog post](https://blog.thalheim.io/2022/12/31/nix-ld-a-clean-so
to get the explaination in full detail. A summary is below:

Precompiled binaries that were not created for NixOS usually have a so-called
link-loader hardcoded into them. On Linux/x86_64 this is for example
`/lib64/ld-linux-x86-64.so.2`. for glibc. NixOS, on the other hand, usually has
link-loader hardcoded into them. On Linux/x86_64 this is for example
`/lib64/ld-linux-x86-64.so.2`. for glibc. NixOS, on the other hand, usually has
its dynamic linker in the glibc package in the Nix store and therefore cannot
run these binaries. Nix-ld provides a shim layer for these types of binaries. It
is installed in the same location where other Linux distributions install their
Expand All @@ -42,7 +42,7 @@ libraries that the executable needs to run.
nix-ld is part of nixpkgs since NixOS 22.05. There one can enable it with the following
nixos setting:

``` nix
```nix
{
programs.nix-ld.enable = true;
}
Expand All @@ -62,10 +62,14 @@ $ sudo nix-channel --update
imports = [
<nix-ld/modules/nix-ld.nix>
];
# The module in this repository defines a new module under (programs.nix-ld.dev) instead of (programs.nix-ld)
# to not collide with the nixpkgs version.
programs.nix-ld.dev.enable = true;
}
```

### With nix flake

### With nix flake

Add the following lines to `/etc/nixos/flake.nix`. Replace `myhostname` with the
actual hostname of your system.
Expand All @@ -77,24 +81,27 @@ actual hostname of your system.
inputs.nix-ld.url = "github:Mic92/nix-ld";
# this line assume that you also have nixpkgs as an input
inputs.nix-ld.inputs.nixpkgs.follows = "nixpkgs";

outputs = { nix-ld, nixpkgs, ... }: {
# replace `myhostname` with your actual hostname
nixosConfigurations.myhostname = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
# ... add this line to the rest of your configuration modules
nix-ld.nixosModules.nix-ld

# The module in this repository defines a new module under (programs.nix-ld.dev) instead of (programs.nix-ld)
# to not collide with the nixpkgs version.
{ programs.nix-ld.dev.enable = true; }
];
};
};
}
```


## Usage

After setting up the nix-ld symlink as described above, one needs to set
After setting up the nix-ld symlink as described above, one needs to set
`NIX_LD` and `NIX_LD_LIBRARY_PATH` to run executables. For example, this can
be done with a `shell.nix` in a nix-shell like this:

Expand Down Expand Up @@ -123,6 +130,21 @@ variables set.
To figure out what libraries a program needs, you can use `ldd` on the binary or
set the `LD_DEBUG=libs` environment variable.

## Default Configuration for nix-ld

In some scenarios, certain build systems or programs might ignore environment variables,
which could disrupt the functioning of nix-ld.

To counteract this, nix-ld implements a fallback mechanism.
If the `NIX_LD` environment variable is not set,
nix-ld will verify the existence of `/run/current-system/sw/share/nix-ld/lib/ld.so`.
If this file exists, it will be used, alongside `/run/current-system/sw/share/nix-ld/lib`.

This behavior essentially defaults back to the NixOS configuration for nix-ld.
In terms of library paths, it will default to using the paths specified in `programs.nix-ld.libraries`.
This ensures that nix-ld can function effectively, even when its configuration
is not explicitly defined through the `NIX_LD` environment variable.

## Known Issues

### LD_LIBRARY_PATH is inherited by child processes
Expand Down Expand Up @@ -154,14 +176,14 @@ with nix-ld will break the system.
### My python/nodejs/ruby/$interpreter libraries do not find the libraries configured by nix-ld

Nix-ld is only used by unpatched executables that use the link loader at `/lib`
or `/lib64`. If you use for example python from nixpkgs than it will not pick
up `NIX_LD_LIBRARY_PATH` and `NIX_LD` since these types of binaries are
or `/lib64`. If you use for example python from nixpkgs than it will not pick
up `NIX_LD_LIBRARY_PATH` and `NIX_LD` since these types of binaries are
configured to use a glibc from the nix store. If you encounter these cases i.e.
when you are trying to use python packages installed in a virtualenv than you
need to set `LD_LIBRARY_PATH` directly. You can also create yourself a wrapper
like this:

``` nix
```nix
(pkgs.writeShellScriptBin "python" ''
export LD_LIBRARY_PATH=$NIX_LD_LIBRARY_PATH
exec ${pkgs.python3}/bin/python "$@"
Expand Down
30 changes: 24 additions & 6 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

78 changes: 72 additions & 6 deletions modules/nix-ld.nix
Original file line number Diff line number Diff line change
@@ -1,9 +1,75 @@
{pkgs, lib, ...}:
{ pkgs, lib, config, ... }:
let
cfg = config.programs.nix-ld.dev;

nix-ld-libraries = pkgs.buildEnv {
name = "lb-library-path";
pathsToLink = [ "/lib" ];
paths = map lib.getLib cfg.libraries;
# TODO make glibc here configurable?
postBuild = ''
ln -s "$(cat '${pkgs.stdenv.cc}/nix-support/dynamic-linker')" $out/share/nix-ld/lib/ld.so
'';
extraPrefix = "/share/nix-ld";
ignoreCollisions = true;
};

# We currently take all libraries from systemd and nix as the default.
# Is there a better list?
baseLibraries = with pkgs; [
zlib
zstd
stdenv.cc.cc
curl
openssl
attr
libssh
bzip2
libxml2
acl
libsodium
util-linux
xz
systemd
];
in
{
config = {
programs.nix-ld.enable = true;
} // lib.optionalAttrs (lib.versionAtLeast (lib.versions.majorMinor lib.version) "23.05") {
# 22.11 users won't actually use nix-ld from this repo but at least it does not break their configuration.
programs.nix-ld.package = pkgs.callPackage ../nix-ld.nix {};
meta.maintainers = [ lib.maintainers.mic92 ];


options.programs.nix-ld.dev = {
enable = lib.mkEnableOption (lib.mdDoc ''nix-ld, Documentation: <https://github.com/Mic92/nix-ld>'');
package = lib.mkOption {
type = lib.types.package;
description = lib.mdDoc "The package to be used for nix-ld.";
default = pkgs.callPackage ../default.nix {};
};
libraries = lib.mkOption {
type = lib.types.listOf lib.types.package;
description = lib.mdDoc "Libraries that automatically become available to all programs. The default set includes common libraries.";
default = baseLibraries;
defaultText = lib.literalExpression "baseLibraries derived from systemd and nix dependencies.";
};
};

config = lib.mkIf config.programs.nix-ld.dev.enable {
assertions = [{
assertion = !config.programs.nix-ld.enable;
message = ''
nix-ld.dev cannot be enabled at the same time as nix-ld.
'';
}];

systemd.tmpfiles.packages = [ cfg.package ];

environment.systemPackages = [ nix-ld-libraries ];

environment.pathsToLink = [ "/share/nix-ld" ];

environment.variables = {
NIX_LD = "/run/current-system/sw/share/nix-ld/lib/ld.so";
NIX_LD_LIBRARY_PATH = "/run/current-system/sw/share/nix-ld/lib";
};
};
}

2 changes: 1 addition & 1 deletion nixos-example.nix
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{pkgs, ...}: {
imports = [./modules/nix-ld.nix];
programs.nix-ld.enable = true;
programs.nix-ld.dev.enable = true;
environment.systemPackages = [
(pkgs.runCommand "patched-hello" {} ''
install -D -m755 ${pkgs.hello}/bin/hello $out/bin/hello
Expand Down
5 changes: 4 additions & 1 deletion nixos-test.nix
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ in {
testScript = ''
start_all()
machine.succeed("hello")
machine.succeed("ls -la /run/current-system/sw/share/nix-ld/lib/ld.so >&2")
machine.succeed("$(< ${nix-ld}/nix-support/ldpath) --version")
machine.succeed("$(< ${nix-ld}/nix-support/ldpath) $(which hello)")

# test fallback if NIX_LD is not set
machine.succeed("unset NIX_LD; unset NIX_LD_LIBRARY_PATH; $(< ${nix-ld}/nix-support/ldpath) $(which hello)")
'';
} {
inherit pkgs;
Expand Down
19 changes: 17 additions & 2 deletions src/nix-ld.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ typedef Elf64_Phdr Phdr;
#error unsupported word width
#endif

#define DEFAULT_NIX_LD "/run/current-system/sw/share/nix-ld/lib/ld.so"
#define DEFAULT_NIX_LD_LIBRARY_PATH "/run/current-system/sw/share/nix-ld/lib"

typedef struct {
void *addr;
size_t size;
Expand Down Expand Up @@ -382,6 +385,11 @@ static void* get_at_base(size_t *auxv) {
return NULL;
}

#define R_OK 4
int access(const char *pathname, int mode) {
return my_syscall2(__NR_access, (long)pathname, mode);
}

int main(int argc, char** argv, char** envp) {
size_t *auxv;
for (auxv = (size_t *)envp; *auxv; auxv++) {
Expand All @@ -391,8 +399,15 @@ int main(int argc, char** argv, char** envp) {
struct ld_ctx ctx = init_ld_ctx(argc, argv, envp, auxv);

if (!ctx.nix_ld) {
log_error(&ctx, "You are trying to run an unpatched binary on nixos, but you have not configured NIX_LD or NIX_LD_" NIX_SYSTEM ". See https://github.com/Mic92/nix-ld for more details");
return 1;
// fallback to default ld.so
if (access(DEFAULT_NIX_LD, R_OK) == 0) {
ctx.nix_ld = DEFAULT_NIX_LD;
// if no NIX_LD is set we also don't trust NIX_LD_LIBRARY_PATH since it may point to a different libc
ctx.nix_lib_path_prefix = DEFAULT_NIX_LD_LIBRARY_PATH;
} else {
log_error(&ctx, "You are trying to run an unpatched binary on nixos, but you have not configured NIX_LD or NIX_LD_" NIX_SYSTEM ". See https://github.com/Mic92/nix-ld for more details");
return 1;
}
}

if (!ctx.page_size) {
Expand Down