Não consegue encontrar .so no mesmo diretório que o executável?

45

Eu tenho um executável que precisa vincular libtest.sodinamicamente, então eu os coloco no mesmo diretório e, em seguida:

cd path_to_dir
./binary

Mas entendi:

error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory

Como ele não consegue encontrar o libtest.soque já está no mesmo diretório que o próprio executável?

linuxer
fonte

Respostas:

25

O carregador não verifica o diretório atual para objetos compartilhados a menos que seja explicitamente dirigida a via $LD_LIBRARY_PATH. Veja a ld.so(8)página de manual para mais detalhes.

Ignacio Vazquez-Abrams
fonte
echo $LD_LIBRARY_PATHestá vazio na minha máquina :(
linuxer
Geralmente é.
Ignacio Vazquez-Abrams
2
Ele especifica diretórios adicionais para o carregador procurar bibliotecas.
Ignacio Vazquez-Abrams
1
Os caminhos no * nix são separados por dois pontos ( :), não um ponto e vírgula.
Ignacio Vazquez-Abrams
3
LD_LIBRARY_PATH geralmente é uma má escolha na produção. É bom para hacks rápidos, e coisas como ajudar os binários desinstalados a encontrar suas bibliotecas compartilhadas ao executar testes de unidade (pense em ./configure; make; make check). Ao criar seu binário, você pode colocar sua biblioteca em um local padrão (listado em /etc/ld.so.conf) ou passar o sinalizador -R para o vinculador para que o binário saiba onde procurar.
automatthias
57

Embora você possa definir LD_LIBRARY_PATH para permitir que o vinculador dinâmico saiba onde procurar, há opções melhores. Você pode colocar sua biblioteca compartilhada em um dos locais padrão, consulte /etc/ld.so.conf(no Linux) e /usr/bin/crle(no Solaris) para obter a lista desses locais

Você pode passar -R <path>para o vinculador ao criar seu binário, que será adicionado <path>à lista de diretórios verificados para sua biblioteca compartilhada. Aqui está um exemplo. Primeiro, mostrando o problema:

libtest.h:

void hello_world(void);

libtest.c:

#include <stdio.h>
void hello_world(void) {
  printf("Hello world, I'm a library!\n");
}

hello.c:

#include "libtest.h"
int main(int argc, char **argv) {
  hello_world();
}

Makefile (as guias devem ser usadas):

all: hello
hello: libtest.so.0
%.o: %.c
        $(CC) $(CFLAGS) -fPIC -c -o $@ $<
libtest.so.0.0.1: libtest.o
        $(CC) -shared -Wl,-soname,libtest.so.0 -o libtest.so.0.0.1 libtest.o
libtest.so.0: libtest.so.0.0.1
        ln -s $< $@
clean:
        rm -f hello libtest.o hello.o libtest.so.0.0.1 libtest.so.0

Vamos executá-lo:

$ make
cc  -fPIC -c -o libtest.o libtest.c
cc -shared -Wl,-soname,libtest.so.0 -o libtest.so.0.0.1 libtest.o
ln -s libtest.so.0.0.1 libtest.so.0
cc     hello.c libtest.so.0   -o hello
$ ./hello 
./hello: error while loading shared libraries: libtest.so.0: cannot open shared object file: No such file or directory

Como corrigi-lo? Adicione -R <path>aos sinalizadores do vinculador (aqui, configurando LDFLAGS).

$ make clean
(...)
$ make LDFLAGS="-Wl,-R -Wl,/home/maciej/src/tmp"
(...)
cc   -Wl,-R -Wl,/home/maciej/src/tmp  hello.c libtest.so.0   -o hello
$ ./hello 
Hello world, I'm a library!

Olhando para o binário, você pode ver que ele precisa libtest.so.0:

$ objdump -p hello | grep NEEDED
  NEEDED               libtest.so.0
  NEEDED               libc.so.6

O binário procurará suas bibliotecas, além dos locais padrão, no diretório especificado:

$ objdump -p hello | grep RPATH
  RPATH                /home/maciej/src/tmp

Se você deseja que o binário procure no diretório atual, pode configurar o RPATH para $ORIGIN. Isso é um pouco complicado, porque você precisa garantir que o cifrão não seja interpretado pelo make. Aqui está uma maneira de fazer isso:

$ make CFLAGS="-fPIC" LDFLAGS="-Wl,-rpath '-Wl,\$\$ORIGIN'"
$ objdump -p hello | grep RPATH
  RPATH                $ORIGIN
$ ./hello 
Hello world, I'm a library!
automatthias
fonte
1
Se não estiver usando make, como ao chamar manualmente g++, tente -Wl,-rpath='$ORIGIN'(observe as aspas simples) para impedir a $ORIGINexpansão para uma sequência vazia.
Morpork
14

Para carregar os objetos compartilhados do mesmo diretório que o executável, basta executar:

$ LD_LIBRARY_PATH=. ./binary

Nota: Não modificará a variável LD_LIBRARY_PATH do seu sistema. A alteração afeta apenas a isso e somente a execução do seu programa.

SwanS
fonte
4

Para quem ainda luta sem resposta, encontrei uma com a seguinte sugestão:

Você pode tentar atualizar o ld.so.cache usando: sudo ldconfig -v

Trabalhou para mim.

Ian Frisbie
fonte
Trabalhou para mim também.
Joel
3

Para qualquer pessoa que use o CMake para sua compilação, você pode definir CMAKE_EXE_LINKER_FLAGSo seguinte:

set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath='$ORIGIN'")

Isso propaga adequadamente os sinalizadores do vinculador para todos os tipos de compilação (por exemplo, Debug, Release, etc ...) para procurar arquivos .so no diretório de trabalho atual primeiro.

Michael Goldshteyn
fonte
0

O vinculador dinâmico decidirá onde procurar por bibliotecas. No caso do Linux, o vinculador dinâmico geralmente é GNU ld.so(ou uma alternativa que normalmente se comportará idêntica por motivos de compatibilidade).

Para citações da Wikipedia:

O vinculador dinâmico da Biblioteca GNU C procura bibliotecas compartilhadas nos seguintes locais:

  1. Os caminhos (separados por dois pontos) no DT_RPATHatributo de seção dinâmica do binário, se presente, e o DT_RUNPATHatributo não existe.
  2. Os caminhos (separados por dois pontos) na variável de ambiente LD_LIBRARY_PATH, a menos que o executável seja a setuid/ setgidbinário, caso em que é ignorado. LD_LIBRARY_PATHpode ser substituído chamando o vinculador dinâmico com a opção --library-path (por exemplo, /lib/ld-linux.so.2 --library-path $ HOME / mylibs myprogram).
  3. Os caminhos (separados por dois pontos) no DT_RUNPATHatributo de seção dinâmica do binário, se presente.
  4. Pesquisa baseada no arquivo de cache ldconfig (geralmente localizado em /etc/ld.so.cache) que contém uma lista compilada de bibliotecas candidatas encontradas anteriormente no caminho de biblioteca aumentada (definido por /etc/ld.so.conf). Se, no entanto, o binário -z nodefaultlibestiver vinculado à opção vinculador, as bibliotecas nos caminhos padrão da biblioteca serão ignoradas.
  5. No caminho padrão confiável /libe, em seguida /usr/lib. Se o binário foi vinculado à opção -z nodefaultlib linker, esta etapa será ignorada.

Fonte: https://en.wikipedia.org/wiki/Rpath

Mecki
fonte