2010-11-05 14 views
25

Rozważmy mamy następującą sytuację:Zrozumienie, jak dynamiczne łączenie działa w systemie UNIX

  • program nazwany program która zależy dynamicznie na libfoo.so
  • libfoo.so że zależy na niczym (dobrze, to zależy od libstdc++ i rzeczy, ale Chyba możemy to pominąć)

program działa idealnie.

Nagle, libfoo zmienia kody, a niektóre funkcje obecnie używają wewnętrznie func_bar() funkcji udostępnianej przez inną bibliotekę libbar.so.

libfoo.so jest przekompilowany i teraz zależy od libbar.so. program pozostaje niezmieniony, nadal zależy tylko od numeru libfoo.so.

Teraz, gdy wykonuję program, skarży się, że nie może znaleźć func_bar().

Oto moje pytania:

  • libfoo.so interfejs nie zmienił, tylko jego realizacja. Dlaczego program ma do wyraźnie link z libbar.so?
  • Czy drzewo zależności nie jest rekursywne? Wydaje mi się, że od libfoo.so zależy od libbar.so, libbar.so zostałby automatycznie dodany do listy zależności program, bez ponownej kompilacji. Jednak ldd program pokazuje, że tak nie jest.

Wydaje się dziwne, że jeden musi ponownej kompilacji (RELINK) każdego binarnego, że za każdym razem zależy od jakiejś biblioteki, które zmieniają zależności bibliotecznych. Jakie mam rozwiązania, aby temu zapobiec?

+0

Czy możesz umieścić minimalną konfigurację, która powiela problem? Komentarze vitauta następujące po poście wydają się opisywać ten sam proces i nie dochodzi do tego samego problemu. Może jeśli postawisz proste kroki, możemy pomóc w odpowiedzi na pytanie? –

Odpowiedz

17

Problem pojawia się, gdy nie połączono libfoo.so z libbar. Podczas kompilowania pliku wykonywalnego domyślnie linker nie pozwala pozostawić niezdefiniowanych odniesień. Jednak podczas kompilowania biblioteki współużytkowanej, będzie ona wymagać, aby były spełnione w czasie łączenia. Jest tak, że libfoo może korzystać z funkcji eksportowanych przez samą program - podczas próby uruchomienia dynamiczny linker oczekuje, że func_bar() zostanie dostarczone przez . Problemem jest zilustrowany tak:

(foo.c jest selfcontained)

export LD_RUN_PATH=`pwd` 
gcc -Wall -shared foo.c -o libfoo.so 
gcc -Wall -L. p.c -lfoo -o p 

W tym momencie ./p przebiega prawidłowo, jak można by oczekiwać. Następnie tworzymy libbar.so i modyfikować foo.c go używać:

gcc -Wall -shared bar.c -o libbar.so 
gcc -Wall -shared foo.c -o libfoo.so 

W tym momencie ./p daje błąd opisania. Jeśli sprawdzimy ldd libfoo.so, zauważymy, że ma ona wartość , a nie zależność od libbar.so - jest to błąd. Aby rozwiązać ten problem, musimy połączyć libfoo.so poprawnie:

gcc -Wall -L. -lbar -shared foo.c -o libfoo.so 

W tym momencie ./p znowu działa prawidłowo, a ldd libfoo.so pokazuje zależność libbar.so.

+0

Miałeś rację: nie łączyłem się z 'libbar.so'. Teraz działa jak urok! Wielkie dzięki ! – ereOn

2

Nie podałeś żadnych informacji systemowych, czy używasz glibc? Jeśli tak, co jest wyjście z tej komendy:

LD_DEBUG = Program Files

Sprawdź również "How to write shared (ELF) libraries" (pdf) (czy używasz glibc lub nie)

1

Twój program nie musi łączyć się z libbar.so.

Myślę, że przyczyną problemu jest brak określenia libbar.so jako zależności od libfoo.so podczas budowania późniejszej wersji. Nie jestem pewien, co system budowania używasz ale w CMake można to zrobić w następujący sposób:

add_library(bar SHARED bar.c) 

add_library(foo SHARED foo.c) 
target_link_libraries(foo bar) 

add_executable(program program.c) 
target_link_libraries(program foo) 

Jak widać program jest związany tylko z foo (libfoo.so) i foo tylko bar (libbar.so).

Może się zdarzyć, że nie można znaleźć libbar.so. Spróbuj podać ścieżkę do swojego katalogu w zmiennej środowiskowej LD_LIBRARY_PATH.

+1

Pytanie jest, co CMake robi za kulisami, może być wystarczająco inteligentne, aby zbudować odpowiednie polecenie łącza dla programu –

+0

@Peer Stritzinger: Dobre pytanie. Pomyślałem o tym i zrobiłem kilka eksperymentów (budowanie 'programu' podczas gdy' foo' nie miał łącza do 'bar', a następnie budowanie' foo' i 'bar', ale nie' program') i działało. Więc magia ze strony cmake wydaje się mało prawdopodobna w tym przypadku, chociaż jest naprawdę sprytna =). – vitaut

+1

Czy to naprawdę odpowiada na pytanie? Oczywiście połączenie 'libbar' z' libfoo' i * then * łączenie z 'program' zacznie działać, niezależnie od systemu kompilacji. Jednakże, jeśli 'program' jest połączony z' libfoo', a następnie 'libfoo' jest aktualizowany w celu połączenia z' libbar', to może nie być prawdą. Jeśli twój skrypt CMake, jasne jest, że 'program' łączy się z' libfoo', jeśli 'libfoo' zmieni połączenie z' libbar' bez żadnej magii. –

8

W Fedorze łączenie dynamiczne jest wykonywane przez ld-linux.so.2. Dynamiczny linker używa /etc/ld.so.cache i /etc/ld.so.preload, aby znaleźć pliki biblioteki.

Uruchom ldconfig, aby poinformować system, w którym libfoo powinien szukać libbar.

ldconfig wygląda w/lib,/usr/lib i dowolnym katalogu wymienionym w /etc/ld.so.conf. Możesz sprawdzić, które biblioteki program używa z LDD.

Więcej szczegółów jest dostępnych na stronach podręcznika dla każdego polecenia.

Oto przykład aplikacji korzystającej z bibliotek współdzielonych.
Program.cc

#include "foo.h" 
#include <iostream> 

int main(int argc, char *argv[]) 
{ 
    for (int i = 0; i < argc; ++i) { 
     std::cout << func_foo(argv[i]) << std::endl; 
    } 
} 

Foo.h

#ifndef FOO_H 
#define FOO_H 
#include <string> 
std::string func_foo(std::string const &); 
#endif 

foo.cc

#include "foo.h" 

std::string func_foo(std::string const &arg) 
{ 
    return arg + "|" + __func__; 
} 

bar.h

#ifndef BAR_H 
#define BAR_H 
#include <string> 
std::string func_bar(); 
#endif 

bar.cc

#include "bar.h" 

std::string func_bar() 
{ 
    return __func__; 
} 

Kompilacja z libfoo.so jako biblioteką współdzieloną.
g ++ -Wall -Wextra -fPIC -shared foo.cc -o libfoo.so
g ++ -lfoo -L./ -Wall -Wextra program.cc Foo.h -o Program
Program LDD
...
libfoo.so => ​​nie znaleziono

Aktualizacja /etc/ld.so.cache
sudo ldconfig/home/Tobias/projekty/en/tak/

LDD pokazuje, że dynamiczny linker znajdzie libfoo.so
program ldd
...
libfoo.so => ​​/home/tobias/projects/stubs/so/libfoo.so (0x00007f0bb9f15000)

Dodaj wezwanie do libbar.so w libfoo.so
Nowym foo.cc

#include "foo.h" 
#include "bar.h" 

std::string func_foo(std::string const &arg) 
{ 
    return arg + "|" + __func__ + "|" + func_bar(); 
} 

Budowa libbar.so i odbudować libfoo.so
g ++ -Wall -Wextra -fPIC -shared bar.cc -o libbar.so
g ++ -Wall -Wextra -fPIC -shared libbar.so foo .cc -o libfoo.so
ldd libfoo.so
...
libbar.so => ​​Nie znaleziono

Program LDD
...
libfoo.so => ​​/home/tobias/projects/stubs/so/libfoo.so (0x00007f49236c7000)
libbar.so => ​​Nie znaleziono
To pokazuje, że dynamiczny linker nadal znajduje libfoo.so ale nie libbar. więc
Ponownie zaktualizuj /etc/ld.so.cache i ponownie sprawdź.
sudo ldconfig/home/Tobias/projekty/en/tak/
LDD libfoo.so
...
libbar.so => ​​/home/tobias/projects/stubs/so/libbar.so (0x00007f935e0bd000)

Program LDD
...
libfoo.so => ​​/home/tobias/projects/stubs/so/libfoo.so (0x00007f2be4f11000)
libbar.so => ​​/ home/Tobias/projekty/stubs/so/libbar.so (0x00007f2be4d0e000)

B znaleziono inne libfoo.so i libbar.so.

Uwaga: ten ostatni krok nie ma wpływu na program aplikacji. Jeśli naprawdę ściśle działasz, ldconfig jest w rodzaju ponownego łączenia. Dziwne czy nie, linker musi znać zależności bibliotek, do których prowadzi. Istnieje wiele innych sposobów wdrożenia tego, ale zostało to wybrane.

+0

Dzięki za informację :) – ereOn

1

Nie powinno tak być, chyba że zmieniono element o symbolu bar_func o symbolu. Użyj polecenia "nm", aby uzyskać zrzut symboli zarówno w programie, jak i współdzielonym obiekcie - sprawdź, czy istnieje niezgodność i dlaczego.

+1

Moje pytanie brzmi, że zmiana w 'bar_func()' jest taka, że ​​wcześniej nie była używana, ale teraz jest używana. –