Nie możesz znaleźć pliku .so w tym samym katalogu co plik wykonywalny?

45

Mam plik wykonywalny, który musi łączyć się libtest.sodynamicznie, więc umieszczam je w tym samym katalogu, a następnie:

cd path_to_dir
./binary

Ale dostałem to:

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

Jak może nie być w stanie znaleźć, libtest.soktóry jest już w tym samym katalogu, co sam plik wykonywalny?

linuxer
źródło

Odpowiedzi:

25

Moduł ładujący nigdy nie sprawdza bieżącego katalogu pod kątem współdzielonych obiektów, chyba że jest to wyraźnie wskazane za pośrednictwem $LD_LIBRARY_PATH. Zobacz ld.so(8)stronę podręcznika, aby uzyskać więcej informacji.

Ignacio Vazquez-Abrams
źródło
echo $LD_LIBRARY_PATHjest pusty na mojej maszynie :(
linuxer
Zwykle tak jest.
Ignacio Vazquez-Abrams,
2
Określa dodatkowe katalogi dla modułu ładującego do wyszukiwania bibliotek.
Ignacio Vazquez-Abrams,
1
Ścieżki w * nix są oddzielone dwukropkiem ( :), a nie średnikiem.
Ignacio Vazquez-Abrams
3
LD_LIBRARY_PATH jest ogólnie złym wyborem w produkcji. Jest dobry do szybkich włamań i takich rzeczy, jak pomoc odinstalowanym plikom binarnym w znajdowaniu współdzielonych bibliotek podczas uruchamiania testów jednostkowych (pomyśl ./configure; make; make check). Podczas budowania pliku binarnego możesz umieścić bibliotekę w standardowej lokalizacji (wymienionej na /etc/ld.so.conf) lub przekazać flagę -R linkerowi, aby poinformował plik binarny, gdzie szukać.
automatthias
57

Chociaż możesz ustawić LD_LIBRARY_PATH, aby dynamiczny linker wiedział, gdzie szukać, istnieją lepsze opcje. Możesz umieścić udostępnioną bibliotekę w jednym ze standardowych miejsc, zobacz /etc/ld.so.conf(w systemie Linux) i /usr/bin/crle(w systemie Solaris) listę tych miejsc

Możesz przejść -R <path>do linkera podczas budowania pliku binarnego, który doda <path>do listy katalogów skanowanych w celu udostępnienia biblioteki. Oto przykład. Po pierwsze, pokazując problem:

libtest.h:

void hello_world(void);

libtest.c:

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

cześć, c:

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

Makefile (należy użyć tabulatorów):

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

Uruchommy to:

$ 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

Jak to naprawić? Dodaj -R <path>do flag linkera (tutaj, ustawiając 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!

Patrząc na plik binarny, widać, że potrzebuje libtest.so.0:

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

Plik binarny będzie szukał swoich bibliotek, oprócz standardowych miejsc, w określonym katalogu:

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

Jeśli chcesz, aby plik binarny wyglądał w bieżącym katalogu, możesz ustawić RPATH na $ORIGIN. Jest to nieco trudne, ponieważ musisz upewnić się, że znak dolara nie jest interpretowany przez markę. Oto jeden ze sposobów, aby to zrobić:

$ make CFLAGS="-fPIC" LDFLAGS="-Wl,-rpath '-Wl,\$\$ORIGIN'"
$ objdump -p hello | grep RPATH
  RPATH                $ORIGIN
$ ./hello 
Hello world, I'm a library!
automatów
źródło
1
Jeśli nie używasz make, na przykład podczas ręcznego wywoływania g++, spróbuj -Wl,-rpath='$ORIGIN'(zwróć uwagę na pojedyncze cudzysłowy), aby zapobiec $ORIGINrozwijaniu do pustego ciągu.
Morpork,
14

Aby załadować współdzielone obiekty z tego samego katalogu co plik wykonywalny, po prostu wykonaj:

$ LD_LIBRARY_PATH=. ./binary

Uwaga: Nie zmodyfikuje zmiennej LD_LIBRARY_PATH twojego systemu. Zmiana wpływa tylko na to i tylko na wykonanie twojego programu.

Łabędzie
źródło
4

Dla każdego, kto wciąż zmaga się z odpowiedzią, znalazłem ją z następującą sugestią:

Możesz spróbować zaktualizować plik ld.so.cache, używając: sudo ldconfig -v

Pracował dla mnie.

Ian Frisbie
źródło
Pracował również dla mnie.
Joel
3

Dla każdego, kto używa CMake do ich kompilacji, możesz ustawić CMAKE_EXE_LINKER_FLAGSnastępujące opcje:

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

Spowoduje to prawidłowe propagowanie flag linkera dla wszystkich typów kompilacji (np. Debugowania, wydania itp.), Aby najpierw wyszukać pliki .so w bieżącym katalogu roboczym.

Michael Goldshteyn
źródło
0

Dynamiczny linker zdecyduje, gdzie szukać bibliotek. W przypadku Linuksa linker dynamiczny zwykle jest GNU ld.so(lub alternatywą, która zwykle zachowuje się identycznie ze względu na kompatybilność.

Cytaty z Wikipedii:

Dynamiczny linker biblioteki GNU C szuka bibliotek współdzielonych w następujących lokalizacjach:

  1. Ścieżki (oddzielone dwukropkami) w DT_RPATHatrybucie sekcji dynamicznej pliku binarnego, jeśli występuje, a DT_RUNPATHatrybut nie istnieje.
  2. Ścieżki (oddzielone dwukropkami) w zmiennej środowiskowej LD_LIBRARY_PATH, chyba że plik wykonywalny jest setuid/ setgidbinarny, w którym to przypadku jest ignorowany. LD_LIBRARY_PATHmożna go zastąpić, wywołując dynamiczny linker z opcją --library-path (np. /lib/ld-linux.so.2 --library-path $ HOME / mylibs myprogram).
  3. Ścieżki (oddzielone dwukropkami) w DT_RUNPATHatrybucie sekcji dynamicznej pliku binarnego, jeśli jest obecny.
  4. Wyszukiwanie na podstawie pliku pamięci podręcznej ldconfig (często znajdującego się pod /etc/ld.so.cache), który zawiera skompilowaną listę bibliotek kandydujących wcześniej znalezionych w rozszerzonej ścieżce biblioteki (ustawionej przez /etc/ld.so.conf). Jeśli jednak plik binarny został połączony z -z nodefaultlibopcją linkera, biblioteki w domyślnych ścieżkach bibliotek są pomijane.
  5. W zaufanej domyślnej ścieżce /lib, a następnie /usr/lib. Jeśli plik binarny został połączony z opcją linkera -z nodefaultlib, ten krok zostanie pominięty.

Źródło: https://en.wikipedia.org/wiki/Rpath

Mecki
źródło