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

8

Estou usando o nix"modo de usuário único" em um sistema em que não sou o root (veja abaixo uma descrição da minha configuração do nix).

Eu queria executar rapidamente um dos meus binários que está 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, ele 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 faço para que as bibliotecas instaladas por nixsejam "visíveis"?

Provavelmente, o script de instalação padrão do usuário nixmodifica .bash_profilepara adicionar o bin/arquivo PATH, mas não faz algo análogo às bibliotecas.

Minha configuração nix:

A única coisa que pedi para o root fazer por mim foi: mkdir -m 0755 /nix && chown ivan /nixcaso contrário, segui o procedimento de instalação simples padrão do nix. Então agora eu posso usar programas personalizados dos pacotes nix. Eu não poderia fazer isso muito bem sem nenhuma ajuda da raiz, ou seja, sem /nix/, porque /nix/não estava disponível para mim; É claro que eu poderia usar outro diretório, mas os pacotes binários pré-criados 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/por mim.

Outra coisa que fiz foi adicionar a ~/.bash_profile:

export NIX_CONF_DIR=/nix/etc/nix

para que eu possa editar nix.conf. (Era para ser controlado pela raiz, /etc/caso contrário. Fiz isso porque queria build-max-jobse build-coresconfigurações nele.)

imz - Ivan Zakharyaschev
fonte
1
Eu nunca ouvi falar nix-env, para não mencionar nix.conf. Que SO é esse? Além disso, o que você repetiu referências para nixsignificar? Eu só ouvi isso como abreviação Unix, mas parece que você está usando em um contexto mais específico.
Faheem Mitha 31/03
3
@FaheemMitha Pensei que a tag tivesse alguma descrição, então não preciso explicar isso no post. Mas, aparentemente, a tag não tem descrição. Oh, bem, então eu tenho que colocar alguns links. nixé um gerenciador de pacotes moderno e o nixOS é uma distribuição, e o Hydra é um sistema para reconstruir constantemente pacotes nix, e o nixOps é uma ferramenta para gerenciar declaradamente uma infraestrutura (uma rede de vários hosts) e o disNix para gerenciar declarativamente um conjunto de serviços (em cima de uma infraestrutura). guixé um nix
filho do
1
Eu vejo. Obrigado pela informação. Eu ouvi falar de nenhum deles, exceto guix.
Faheem Mitha

Respostas:

7

TL; DR

A solução de trabalho está sendo usada patchelf(se você tiver que lidar com versões glibc não correspondentes: no sistema host e nas quais uma nix libs foi vinculada), consulte a segunda metade da minha história.

Tentando a abordagem usual

Tentando usar 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, há problemas ao vincular com diferentes versões do 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 2 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 nixdeve ter instalado a versão glibcusada pelo seu libgmp!

E, de fato, o glibcde nixlá está:

$ 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, glibcnão estava disponível para o usuário; portanto, quando executei meu binário, o sistema glibcfoi carregado primeiro. Prova:

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

Ok, também podemos tentar tornar glibcvisível o usuário:

$ nix-env -i glibc

Então tudo está ruim:

$ 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ê deseja carregar bibliotecas a partir da nixexecução de seus próprios binários ...

Por enquanto, estou comentando

export LD_LIBRARY_PATH="$NIX_LINK"/lib

e fazendo na sessão 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 glibcparticipe LD_LIBRARY_PATHfalhe, porque o seu ld-linux-x86-64.so.2não corresponderá ao seu libc.so.6. É possível ter várias versões do glibc em um único sistema, mas um pouco complicado, conforme explicado nesta resposta.)

A solução necessária: patchelf

Portanto, o caminho para o vinculador dinâmico é codificado no binário. E o vinculador dinâmico que está sendo 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 é remendar .

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

Depois disso, funciona. Você ainda precisa mexer com isso 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 retiradas do sistema host (porque ainda não as instalei nix-env -i), você deve especificar o caminho para as bibliotecas do nix, e para as libs do seu sistema host LD_LIBRARY_PATH(substitui completamente o caminho de pesquisa padrão).

etapa adicional: corrigir o caminho de pesquisa da biblioteca

(a partir da patchelfpágina)

Da mesma forma, você pode alterar o RPATHcaminho de pesquisa do vinculador incorporado aos executáveis ​​e bibliotecas dinâmicas:

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

Isso faz com que o vinculador dinâmico de pesquisa em /opt/my-libs/libe /foo/libpara as bibliotecas compartilhadas necessárias para programa. Obviamente, você também pode definir a variável de ambiente LD_LIBRARY_PATH, mas isso costuma ser inconveniente, pois requer um script de wrapper para configurar o ambiente.

imz - Ivan Zakharyaschev
fonte
3

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

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

Se você instalar pacotes globalmente especificando-os /etc/nixos/configuration.nix, seus .soarquivos correspondentes poderão ser encontrados em /nix/var/nix/profiles/system/sw/lib/. Mais corretamente, apenas links simbólicos para arquivos correspondentes em algum lugar /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 que o primeiro comando funcione, você precisará instalar glibcglobalmente. Você também pode modificar o segundo comando se tiver pacotes instalados globalmente e por usuário:

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

Pode ser que o .soarquivo necessário simplesmente 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 tenho certeza de como encontrar um pacote correspondente para um arquivo ausente em geral, mas você pode pesquisar no google o nome do .soarquivo, instalar o pacote correspondente e tentar executar o executável com um costume LD_LIBRARY_PATHnovamente.

Mirzhan Irkegulov
fonte