¿Cómo usar las bibliotecas instaladas por nix en tiempo de ejecución?

8

Estoy usando el nix"modo de usuario único" en un sistema en el que no soy la raíz (ver más abajo para una descripción de mi configuración de nix).

Quería ejecutar rápidamente uno de mis archivos binarios que está vinculado dinámicamente con una biblioteca que está ausente en el sistema.

Entonces, instalé la biblioteca con nix:

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

Pero el enlazador todavía no encuentra la biblioteca:

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

Mira, está presente en el sistema de archivos:

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

¿Qué hago para que las bibliotecas instaladas por nixsean "visibles"?

Probablemente, la secuencia de comandos de usuario a la instalación estándar de la nixmodifica .bash_profilepara añadir su bin/dentro PATH, pero no hace algo similar para las bibliotecas.

Mi configuración de nix:

Lo único que le pedí a la raíz que hiciera por mí fue: de lo mkdir -m 0755 /nix && chown ivan /nixcontrario, he seguido el procedimiento estándar de instalación de nix simple. Entonces ahora puedo usar programas personalizados de paquetes nix. No podría hacer esto bien sin ninguna ayuda de la raíz, es decir, sin /nix/, porque /nix/no estaba disponible para mí; Por supuesto, podría usar otro directorio, pero los paquetes binarios precompilados no serían válidos y todos los paquetes tendrían que reconstruirse, de acuerdo con la documentación de nix. En mi caso, fue más sencillo preguntar /nix/por mí.

Otra cosa que he hecho es agregar a ~/.bash_profile:

export NIX_CONF_DIR=/nix/etc/nix

para que yo pueda editar nix.conf. (De lo /etc/contrario, se suponía que estaba controlado por la raíz . Lo hice porque quería build-max-jobsy la build-coresconfiguración en él).

imz - Ivan Zakharyaschev
fuente
1
Nunca he oído hablar nix-env, por no mencionar nix.conf. ¿Qué sistema operativo es este? Además, ¿qué nixsignifican referencias repetidas ? Solo he escuchado que se usa como abreviatura Unix, pero parece que lo está usando en un contexto más específico.
Faheem Mitha
3
@FaheemMitha Pensé que la etiqueta tenía alguna descripción, así que no necesito explicar esto en la publicación. Pero aparentemente la etiqueta no tiene descripción. Oh, bueno, entonces tengo que poner algunos enlaces. nixes un administrador de paquetes moderno , y nixOS es una distribución, y Hydra es un sistema para reconstruir constantemente paquetes de nix, y nixOps es una herramienta para administrar una infraestructura (una red de varios hosts) declarativamente, y disNix para administrar un conjunto de servicios declarativamente (encima de una infraestructura). guixes una descendencia de GNU nix, con una distribución (promovida como 100% libre IIC
imz - Ivan Zakharyaschev
1
Veo. Gracias por la información. No he oído hablar de ninguno de ellos excepto guix.
Faheem Mitha

Respuestas:

7

TL; DR

La solución de trabajo está utilizando patchelf(si tiene que lidiar con versiones de glibc que no coinciden: en el sistema host y con el que se han vinculado las bibliotecas nix), vea la segunda mitad de mi historia.

Intentando el enfoque habitual

Intentando usar LD_LIBRARY_PATH

Bueno, he configurado una variable de entorno para esto en ~/.bash_profile:

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

¡pero eso no es todo!

Ahora hay problemas al vincular con diferentes versiones de 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)
$ 

Clasificando 2 versiones de glibc

El error más sorprendente aquí es:

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)

porque nixdebe haber instalado la versión de la glibccual es utilizada por su libgmp!

Y de hecho, el glibcde nixestá ahí:

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

Probablemente, glibcno estaba disponible para el usuario, así que cuando ejecuté mi binario, el sistema glibcse cargó primero. Prueba:

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

Ok, también podemos intentar hacer glibcvisible para el usuario:

$ nix-env -i glibc

Entonces todo está mal:

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

Por lo tanto, parece que no es un trabajo fácil si desea cargar bibliotecas nixcuando ejecuta sus propios binarios ...

Por ahora, estoy comentando

export LD_LIBRARY_PATH="$NIX_LINK"/lib

y haciendo en la sesión de shell:

$ unset LD_LIBRARY_PATH
$ export LD_LIBRARY_PATH

Necesito pensar más. (Lea acerca de __vdso_time: modo no válido para dlopen () : se espera que se bloquee otro glibcen LD_LIBRARY_PATH, porque ld-linux-x86-64.so.2no coincidirá con su libc.so.6. Tener múltiples versiones de glibc en un solo sistema es posible, pero un poco complicado, como se explica en esta respuesta).

La solución necesaria: parchelf

Entonces, la ruta al enlazador dinámico está codificada en el binario. Y el enlazador dinámico que se usa es del sistema (del host glibc), no de nix. Y debido a que el enlazador dinámico no coincide con el glibc que queremos y necesitamos usar, no funciona.

Una solución simple y funcional es parchelf .

patchelf --set-interpreter /home/ivan/.nix-profile/lib/ld-linux-x86-64.so.2 ../valencies

Después de eso, funciona. Sin embargo, aún debes jugar con ellos LD_LIBRARY_PATH.

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

Si, como en mi caso imperfecto, algunas de las bibliotecas se toman de nix, pero algunas se toman del sistema host (porque no las he instalado nix-env -i), debe especificar tanto la ruta a las librerías de nix, y en las bibliotecas de su sistema host LD_LIBRARY_PATH(anula por completo la ruta de búsqueda predeterminada).

paso adicional: patchelf para la ruta de búsqueda de la biblioteca

(de la patchelfpágina)

Del mismo modo, puede cambiar la RPATHruta de búsqueda del enlazador incrustada en ejecutables y bibliotecas dinámicas:

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

Esto hace que el vinculador dinámico busque /opt/my-libs/liby busque /foo/liblas bibliotecas compartidas que necesita el programa. Por supuesto, también puede establecer la variable de entorno LD_LIBRARY_PATH, pero eso a menudo es inconveniente ya que requiere un script de contenedor para configurar el entorno.

imz - Ivan Zakharyaschev
fuente
3

Además del "modo de usuario único" de Nix, proporciono una respuesta para los usuarios de NixOS . Por lo general , no puede ejecutar archivos binarios en NixOS.

Si instala paquetes utilizando localmente nix-env -i, todos sus .soarchivos se almacenan en ~/.nix-profile/lib/.

Si instala paquetes globalmente al especificarlos /etc/nixos/configuration.nix, .sopuede encontrar sus archivos correspondientes en /nix/var/nix/profiles/system/sw/lib/. Más correctamente, solo los enlaces simbólicos a los archivos correspondientes en algún lugar /nix/store/están en ese directorio.

Entonces, si instala paquetes globalmente, la solución de Ivan Zakharyaschev se convierte en:

$ patchelf --set-interpreter /nix/var/nix/profiles/system/sw/lib/ld-linux-x86-64.so.2 ./YOUREXECUTABLE
$ LD_LIBRARY_PATH=/nix/var/nix/profiles/system/sw/lib ./YOUREXECUTABLE

Para que el primer comando funcione, deberá instalarlo glibcglobalmente. También puede modificar el segundo comando si tiene paquetes instalados tanto globalmente como por usuario:

$ LD_LIBRARY_PATH=/home/YOURUSERNAME/.nix-profile/lib:/nix/var/nix/profiles/system/sw/lib ./YOUREXECUTABLE

Es posible que el .soarchivo necesario simplemente no esté instalado en el sistema, por lo que tendrá un error como:

./YOUREXECUTABLE: error while loading shared libraries: libX11.so.6: cannot open shared object file: No such file or directory

No estoy seguro de cómo encontrar un paquete correspondiente para un archivo que falta en general, pero puede buscar en Google el nombre del .soarchivo e instalar el paquete correspondiente e intentar ejecutar su archivo ejecutable con un personalizado LD_LIBRARY_PATHnuevamente.

Mirzhan Irkegulov
fuente