Como usar bibliotecas instaladas pelo nix no tempo de execução?

8

Estou usando nix no "modo de usuário único" em um sistema no qual não sou a raiz (veja abaixo uma descrição da configuração do meu nix).

Eu queria executar rapidamente um dos meus binários, que é dinamicamente vinculado a uma biblioteca que está ausente no sistema.

Então, eu instalei a biblioteca com nix :

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

Mas a biblioteca ainda não foi encontrada pelo vinculador:

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

Olha, está presente no sistema de arquivos:

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

O que eu faço para que as bibliotecas instaladas por nix sejam "visíveis"?

Provavelmente, o script de instalação de usuário padrão de nix modifica .bash_profile para adicionar seu bin/ a PATH , mas não faz algo análogo para bibliotecas.

Configuração do meu nix:

A única coisa que pedi para o root fazer por mim foi: mkdir -m 0755 /nix && chown ivan /nix , caso contrário, segui o procedimento de instalação do nix simples padrão. Então agora eu posso usar programas personalizados de pacotes nix. Eu não poderia fazer isso muito bem sem a ajuda da raiz, ou seja, sem /nix/ , porque /nix/ não estava disponível para mim; Eu poderia, claro, usar outro diretório, mas os pacotes binários pré-construídos não seriam válidos e todos os pacotes teriam que ser reconstruídos, de acordo com a documentação do nix. No meu caso, era mais simples pedir /nix/ para mim.

Outra coisa que fiz foi adicionar ~/.bash_profile :

export NIX_CONF_DIR=/nix/etc/nix

para que eu possa editar nix.conf . (Ele deveria estar no /etc/ controlado pela raiz. Eu fiz isso porque queria build-max-jobs e build-cores configurações nele.)

    
por imz -- Ivan Zakharyaschev 17.03.2015 / 14:35

2 respostas

6

TL; DR

A solução de trabalho está usando patchelf (se você tiver que lidar com versões glibc não correspondentes: no sistema host e com as bibliotecas nix vinculadas), veja a segunda metade da minha história.

Tentando a abordagem usual

Tentando usar o LD_LIBRARY_PATH

Bem, eu configurei uma variável de ambiente para isso em ~/.bash_profile :

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

mas isso não é tudo!

Agora existem problemas com links para versões diferentes 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)
$ 

Classificando duas versões do glibc

O erro mais surpreendente aqui é:

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 nix deve ter instalado a versão de glibc que é usada por seu libgmp !

E, de fato, o glibc de nix está lá:

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

Provavelmente, glibc não estava disponível para o usuário, portanto, quando executei meu binário, o glibc do sistema foi carregado primeiro. Prova:

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

Ok, podemos tentar tornar glibc visível para o usuário também:

$ nix-env -i glibc

Então tudo 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
$ 

Portanto, parece não ser uma tarefa fácil se você quiser carregar bibliotecas de nix ao executar seus próprios binários ...

Por enquanto, estou comentando

export LD_LIBRARY_PATH="$NIX_LINK"/lib

e fazendo na sessão de shell:

$ unset LD_LIBRARY_PATH
$ export LD_LIBRARY_PATH

Precisa pensar mais. (Leia sobre __vdso_time: modo inválido para dlopen () : espera-se que outro glibc in LD_LIBRARY_PATH falhe, porque o seu ld-linux-x86-64.so.2 não corresponderá ao seu libc.so.6 . Ter várias versões do glibc em um único sistema é possível, mas um pouco complicado, conforme explicado nesta resposta.

A solução necessária: patchelf

Assim, o caminho para o vinculador dinâmico é codificado no binário. E o linker dinâmico usado é do sistema (do host glibc), não do nix. E como o vinculador dinâmico não corresponde ao glibc que queremos e precisamos usar, ele não funciona.

Uma solução simples e funcional é fixada .

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

Depois disso, funciona. Você ainda precisa mexer com LD_LIBRARY_PATH .

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

Se - como no meu caso imperfeito - algumas das bibliotecas são retiradas do nix, mas algumas são tiradas do sistema host (porque eu não as instalei com nix-env -i ), você tem que especifique o caminho para as bibliotecas nix e para as bibliotecas do sistema host em LD_LIBRARY_PATH (substitui completamente o caminho de pesquisa padrão).

etapa adicional: patchelf para o caminho de pesquisa da biblioteca

(da página patchelf )

Da mesma forma, você pode alterar o RPATH , o caminho de pesquisa do vinculador incorporado em bibliotecas executáveis e dinâmicas:

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

Isso faz com que o vinculador dinâmico pesquise /opt/my-libs/lib e /foo/lib nas bibliotecas compartilhadas necessárias ao programa. É claro que você também pode definir a variável de ambiente LD_LIBRARY_PATH , mas isso geralmente é inconveniente, pois exige um script de wrapper para configurar o ambiente.

    
por 17.03.2015 / 15:41
2

Além do "modo de usuário único" do Nix, eu ofereço uma resposta para os usuários NixOS . Você geralmente não pode executar arquivos binários no NixOS.

Se você instalar pacotes localmente usando nix-env -i , todos os seus arquivos .so serão armazenados em ~/.nix-profile/lib/ .

Se você instalar pacotes globalmente especificando-os em /etc/nixos/configuration.nix , seus arquivos .so correspondentes podem ser encontrados em /nix/var/nix/profiles/system/sw/lib/ . Mais corretamente, apenas links simbólicos para arquivos correspondentes em algum lugar em /nix/store/ estão nesse diretório.

Portanto, se você instalar pacotes globalmente, a solução de Ivan Zakharyaschev se tornará:

$ 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 o primeiro comando funcionar, você terá que instalar glibc globalmente. Você também pode modificar o segundo comando se tiver pacotes instalados tanto globalmente quanto por usuário:

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

Pode ser que o arquivo .so necessário não esteja instalado no sistema, então você terá um erro como:

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

Não sei como encontrar um pacote correspondente para um arquivo ausente em geral, mas você pode pesquisar no Google o nome do arquivo .so e instalar o pacote correspondente e tentar executar o executável com um LD_LIBRARY_PATH personalizado novamente.

    
por 29.12.2015 / 22:03