Path

如何在執行時使用 nix 安裝的庫?

  • November 10, 2016

nix在我不是 root 的系統中使用“單使用者模式”(有關我的 nix 設置的描述,請參見下文)。

我想快速執行我的一個二進製文件,它與系統中不存在的庫動態連結。

所以,我已經安裝了這個庫nix

$ nix-env -qa 'gmp'
gmp-4.3.2
gmp-5.1.3
$ nix-env -i gmp-5.1.3

但是連結器仍然找不到該庫:

$ 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)
$ 

看,它存在於文件系統中:

$ 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
$ 

我該怎麼做才能使安裝的庫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

以便我可以編輯nix.conf. (否則它應該在根控制下/etc/。我這樣做是因為我想在build-max-jobs其中build-cores設置。)

TL; 博士

工作解決方案正在使用patchelf(如果您必須處理不匹配的 glibc 版本:在主機系統和一個 nix 庫已連結),請參閱我故事的後半部分。

嘗試正常方法

嘗試使用 LD_LIBRARY_PATH

好吧,我已經為此設置了一個環境變數~/.bash_profile

NIX_LINK=/home/ivan/.nix-profile
export LD_LIBRARY_PATH="$NIX_LINK"/lib

但這還不是全部!

現在與不同版本的連結存在問題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)
$ 

整理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)

因為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
$ 

可能glibc對使用者不可用,所以當我執行我的二進製文件時,系統glibc首先被載入。證明:

$ ls ~/.nix-profile/lib/*libc*
ls: cannot access /home/ivan/.nix-profile/lib/*libc*: No such file or directory
$ 

好的,我們也可以嘗試讓glibc使用者可見:

$ nix-env -i glibc

然後一切都很糟糕:

$ 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
$ 

nix因此,如果您想在執行自己的二進製文件時載入庫,這似乎不是一件容易的事……

現在,我正在評論

export LD_LIBRARY_PATH="$NIX_LINK"/lib

並在 shell 會話中執行:

$ unset LD_LIBRARY_PATH
$ export LD_LIBRARY_PATH

需要多想。(閱讀關於__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

之後,它的工作原理。不過,您仍然需要擺弄LD_LIBRARY_PATH

$ LD_LIBRARY_PATH=/home/ivan/.nix-profile/lib:/lib64/:/usr/lib64/ ../valencies

如果 - 就像我不完美的情況一樣 - 一些庫是從 nix 中獲取的,但有些是從主機系統中獲取的(因為我沒有安裝它們nix-env -i),你必須指定 nix 庫的路徑,並添加到您的主機系統庫中LD_LIBRARY_PATH(它完全覆蓋了預設搜尋路徑)。

附加步驟:庫搜尋路徑的 patchelf

(從patchelf頁面)

同樣,您可以更改RPATH嵌入到執行檔和動態庫中的連結器搜尋路徑:

patchelf --set-rpath /opt/my-libs/lib:/foo/lib program

這會導致動態連結器在程序所需的共享庫中/opt/my-libs/lib進行搜尋。/foo/lib當然,您也可以設置環境變數LD_LIBRARY_PATH,但這通常很不方便,因為它需要一個包裝腳本來設置環境。

引用自:https://unix.stackexchange.com/questions/190719