如何在运行时使用 nix 安装的库?

imz*_*hev 9 path dynamic-linking not-root-user nix altlinux

nix在我不是 root 的系统中以“单用户模式”使用(有关我的 nix 设置的描述,请参见下文)。

我想快速运行我的一个二进制文件,它与系统中不存在的库动态链接。

所以,我已经安装了库nix

$ nix-env -qa 'gmp'
gmp-4.3.2
gmp-5.1.3
$ nix-env -i gmp-5.1.3
Run Code Online (Sandbox Code Playgroud)

但是链接器仍然找不到该库:

$ ldd -r ../valencies 
../valencies: /lib64/libc.so.6: version `GLIBC_2.15' not found (required by ../valencies)
../valencies: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by ../valencies)
    linux-vdso.so.1 =>  (0x00007fffbbf28000)
    /usr/local/lib/libsnoopy.so (0x00007f4dcfbdc000)
    libgmp.so.10 => not found
    libffi.so.5 => /usr/lib64/libffi.so.5 (0x00007f4dcf9cc000)
    libm.so.6 => /lib64/libm.so.6 (0x00007f4dcf748000)
    librt.so.1 => /lib64/librt.so.1 (0x00007f4dcf540000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00007f4dcf33c000)
    libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f4dcf11f000)
    libc.so.6 => /lib64/libc.so.6 (0x00007f4dced8b000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f4dcfde7000)
undefined symbol: __gmpz_gcd    (../valencies)
undefined symbol: __gmpn_cmp    (../valencies)
undefined symbol: __gmpz_mul    (../valencies)
undefined symbol: __gmpz_fdiv_r (../valencies)
undefined symbol: __gmpz_fdiv_q_2exp    (../valencies)
undefined symbol: __gmpz_com    (../valencies)
undefined symbol: __gmpn_gcd_1  (../valencies)
undefined symbol: __gmpz_sub    (../valencies)
symbol memcpy, version GLIBC_2.14 not defined in file libc.so.6 with link time reference    (../valencies)
undefined symbol: __gmpz_fdiv_q (../valencies)
undefined symbol: __gmpz_fdiv_qr    (../valencies)
undefined symbol: __gmpz_add    (../valencies)
undefined symbol: __gmpz_init   (../valencies)
undefined symbol: __gmpz_ior    (../valencies)
undefined symbol: __gmpz_mul_2exp   (../valencies)
undefined symbol: __gmpz_xor    (../valencies)
undefined symbol: __gmpz_and    (../valencies)
symbol __fdelt_chk, version GLIBC_2.15 not defined in file libc.so.6 with link time reference   (../valencies)
undefined symbol: __gmpz_tdiv_qr    (../valencies)
undefined symbol: __gmp_set_memory_functions    (../valencies)
undefined symbol: __gmpz_tdiv_q (../valencies)
undefined symbol: __gmpz_divexact   (../valencies)
undefined symbol: __gmpz_tdiv_r (../valencies)
$ 
Run Code Online (Sandbox Code Playgroud)

看,它存在于文件系统中:

$ find / -name 'libgmp.so.10' 2>/dev/null 
/nix/store/mnmzq0qbrvw6dv1k2vj3cwz9ffdh05zr-user-environment/lib/libgmp.so.10
/nix/store/fnww2w81hv5v3dl9gsb7p4llb7z7krzd-gmp-5.1.3/lib/libgmp.so.10
$ 
Run Code Online (Sandbox Code Playgroud)

我该怎么做才能使安装的库nix“可见”?

可能,标准的用户安装脚本nix修改.bash_profile以将其添加bin/到 中PATH,但不会对库执行类似的操作。

我的 nix 设置:

我要求 root 为我做的唯一一件事是:mkdir -m 0755 /nix && chown ivan /nix,否则我将遵循标准的简单 nix 安装程序。所以现在我可以使用 nix 包中的自定义程序。如果没有根的任何帮助,我就不能很好地做到这一点,即没有/nix/, 因为/nix/对我来说不可用;我当然可以使用另一个目录,但是根据 nix 文档,预构建的二进制包将无效,并且必须重新构建所有包。就我而言,要求/nix/我更简单。

我所做的另一件事是添加到~/.bash_profile

export NIX_CONF_DIR=/nix/etc/nix
Run Code Online (Sandbox Code Playgroud)

以便我可以编辑nix.conf. (/etc/否则它应该在根控制中。我这样做是因为我想要build-max-jobsbuild-cores在其中进行设置。)

imz*_*hev 8

TL; 博士

正在使用的工作解决方案patchelf(如果您必须处理不匹配的 glibc 版本:在主机系统中并且已经链接了一个 nix 库),请参阅我的故事的后半部分。

尝试通常的方法

尝试使用 LD_LIBRARY_PATH

好吧,我已经为此设置了一个环境变量~/.bash_profile

NIX_LINK=/home/ivan/.nix-profile
export LD_LIBRARY_PATH="$NIX_LINK"/lib
Run Code Online (Sandbox Code Playgroud)

但这还不是全部!

现在与不同版本的链接存在问题libc

$ ldd -r ../valencies 
../valencies: /lib64/libc.so.6: version `GLIBC_2.15' not found (required by ../valencies)
../valencies: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by ../valencies)
../valencies: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by /home/ivan/.nix-profile/lib/libgmp.so.10)
    linux-vdso.so.1 =>  (0x00007fff365ff000)
    /usr/local/lib/libsnoopy.so (0x00007f56c72e6000)
    libgmp.so.10 => /home/ivan/.nix-profile/lib/libgmp.so.10 (0x00007f56c7063000)
    libffi.so.5 => /usr/lib64/libffi.so.5 (0x00007f56c6e54000)
    libm.so.6 => /lib64/libm.so.6 (0x00007f56c6bd0000)
    librt.so.1 => /lib64/librt.so.1 (0x00007f56c69c7000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00007f56c67c3000)
    libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f56c65a6000)
    libc.so.6 => /lib64/libc.so.6 (0x00007f56c6211000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f56c74f1000)
symbol memcpy, version GLIBC_2.14 not defined in file libc.so.6 with link time reference    (/home/ivan/.nix-profile/lib/libgmp.so.10)
symbol memcpy, version GLIBC_2.14 not defined in file libc.so.6 with link time reference    (../valencies)
symbol __fdelt_chk, version GLIBC_2.15 not defined in file libc.so.6 with link time reference   (../valencies)
$ 
Run Code Online (Sandbox Code Playgroud)

整理出2个版本的glibc

这里最令人惊讶的错误是:

symbol memcpy, version GLIBC_2.14 not defined in file libc.so.6 with link time reference    (/home/ivan/.nix-profile/lib/libgmp.so.10)
Run Code Online (Sandbox Code Playgroud)

因为nix一定是安装了glibc它所使用的版本libgmp

事实上,glibc来自nix那里:

$ ldd -r /home/ivan/.nix-profile/lib/libgmp.so.10
    linux-vdso.so.1 =>  (0x00007fff0f1ff000)
    /usr/local/lib/libsnoopy.so (0x00007f06e9919000)
    libc.so.6 => /nix/store/93zfs0zzndi7pkjkjxawlafdj8m90kg5-glibc-2.20/lib/libc.so.6 (0x00007f06e957c000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00007f06e9371000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f06e9da7000)
symbol _dl_find_dso_for_object, version GLIBC_PRIVATE not defined in file ld-linux-x86-64.so.2 with link time reference (/nix/store/93zfs0zzndi7pkjkjxawlafdj8m90kg5-glibc-2.20/lib/libc.so.6)
/home/ivan/.nix-profile/lib/libgmp.so.10: error while loading shared libraries: __vdso_time: invalid mode for dlopen(): Invalid argument
$ 
Run Code Online (Sandbox Code Playgroud)

可能glibc对用户不可用,所以当我运行我的二进制文件时,glibc首先加载系统的。证明:

$ ls ~/.nix-profile/lib/*libc*
ls: cannot access /home/ivan/.nix-profile/lib/*libc*: No such file or directory
$ 
Run Code Online (Sandbox Code Playgroud)

好的,我们也可以尝试让glibc用户可见:

$ nix-env -i glibc
Run Code Online (Sandbox Code Playgroud)

那么一切都不好:

$ ldd -r ../valencies 
/bin/bash: error while loading shared libraries: __vdso_time: invalid mode for dlopen(): Invalid argument
$ /bin/echo ok
/bin/echo: error while loading shared libraries: __vdso_time: invalid mode for dlopen(): Invalid argument
$ 
Run Code Online (Sandbox Code Playgroud)

因此,如果您想nix在运行自己的二进制文件时加载库,这似乎不是一件容易的事......

现在,我正在评论

export LD_LIBRARY_PATH="$NIX_LINK"/lib
Run Code Online (Sandbox Code Playgroud)

并在 shell 会话中执行:

$ unset LD_LIBRARY_PATH
$ export LD_LIBRARY_PATH
Run Code Online (Sandbox Code Playgroud)

需要多想想。(阅读__vdso_time: invalid mode for dlopen():有另一个glibcinLD_LIBRARY_PATH预计会崩溃,因为你ld-linux-x86-64.so.2不会匹配你的libc.so.6。在单个系统上有多个版本的 glibc 是可能的,但有点棘手,如本答案所述。)

需要的解决方案:patchelf

因此,动态链接器的路径在二进制文件中是硬编码的。并且正在使用的动态链接器来自系统(来自主机 glibc),而不是来自 nix。并且因为动态链接器与我们想要和需要使用的 glibc 不匹配,所以它不起作用。

一个简单而有效的解决方案是patchelf

patchelf --set-interpreter /home/ivan/.nix-profile/lib/ld-linux-x86-64.so.2 ../valencies
Run Code Online (Sandbox Code Playgroud)

之后,它的工作原理。你仍然需要摆弄LD_LIBRARY_PATH

$ LD_LIBRARY_PATH=/home/ivan/.nix-profile/lib:/lib64/:/usr/lib64/ ../valencies
Run Code Online (Sandbox Code Playgroud)

如果——就像在我不完美的情况下——一些库是从 nix 中获取的,但有些库是从主机系统中获取的(因为我没有安装它们nix-env -i),你必须同时指定 nix 库的路径,和您的主机系统库LD_LIBRARY_PATH(它完全覆盖默认搜索路径)。

附加步骤:库搜索路径的 patchelf

(来自patchelf页面)

同样,您可以更改RPATH嵌入到可执行文件和动态库中的链接器搜索路径:

patchelf --set-rpath /opt/my-libs/lib:/foo/lib program
Run Code Online (Sandbox Code Playgroud)

这导致动态链接到搜索/opt/my-libs/lib/foo/lib用于通过程序所需的共享库。当然,您也可以设置环境变量LD_LIBRARY_PATH,但这通常不方便,因为它需要一个包装脚本来设置环境。