(Zobacz historię tej odpowiedzi, aby uzyskać bardziej skomplikowany tekst, ale myślę, że teraz czytelnikowi łatwiej jest zobaczyć prawdziwe wiersze poleceń).
Wspólne pliki współdzielone przez wszystkie poniższe polecenia
$ cat a.cpp
externint a;int main(){return a;}
$ cat b.cpp
externint b;int a = b;
$ cat d.cpp
int b;
Łączenie z bibliotekami statycznymi
$ g++-c b.cpp -o b.o
$ ar cr libb.a b.o
$ g++-c d.cpp -o d.o
$ ar cr libd.a d.o
$ g++-L.-ld -lb a.cpp # wrong order
$ g++-L.-lb -ld a.cpp # wrong order
$ g++ a.cpp -L.-ld -lb # wrong order
$ g++ a.cpp -L.-lb -ld # right order
Linker wyszukuje od lewej do prawej i zauważa nierozstrzygnięte symbole. Jeśli biblioteka rozwiązuje symbol, pliki obiektowe tej biblioteki muszą rozwiązać symbol (w tym przypadku bo z libb.a).
Zależności między bibliotekami statycznymi działają tak samo - biblioteka, która potrzebuje symboli, musi być pierwsza, a następnie biblioteka, która rozwiązuje symbol.
Jeśli biblioteka statyczna zależy od innej biblioteki, ale druga biblioteka ponownie zależy od poprzedniej biblioteki, następuje cykl. Możesz rozwiązać ten problem, dołączając biblioteki zależne cyklicznie przez -(i -), np. -( -la -lb -)(Może być konieczne ucieczkę przed parenami, takimi jak -\(i -\)). Linker przeszukuje te załączone biblioteki wiele razy, aby upewnić się, że zależności cykliczne zostały rozwiązane. Alternatywnie, można określić bibliotekach wiele razy, więc każdy ma przed sobą: -la -lb -la.
Łączenie z bibliotekami dynamicznymi
$ export LD_LIBRARY_PATH=.# not needed if libs go to /usr/lib etc
$ g++-fpic -shared d.cpp -o libd.so
$ g++-fpic -shared b.cpp -L.-ld -o libb.so # specifies its dependency!
$ g++-L.-lb a.cpp # wrong order (works on some distributions)
$ g++-Wl,--as-needed -L.-lb a.cpp # wrong order
$ g++-Wl,--as-needed a.cpp -L.-lb # right order
Tutaj jest tak samo - biblioteki muszą podążać za plikami obiektowymi programu. Różnica w porównaniu z bibliotekami statycznymi polega na tym, że nie musisz przejmować się wzajemnymi zależnościami bibliotek , ponieważ biblioteki dynamiczne same uporządkują ich zależności .
Niektóre najnowsze dystrybucje najwyraźniej domyślnie używają --as-neededflagi linkera, która wymusza, aby pliki obiektowe programu znajdowały się przed bibliotekami dynamicznymi. Jeśli ta flaga zostanie przekazana, linker nie będzie łączył się z bibliotekami, które w rzeczywistości nie są potrzebne plikowi wykonywalnemu (i wykrywa to od lewej do prawej). Moja ostatnia dystrybucja archlinux domyślnie nie używa tej flagi, więc nie dała błędu za nieprzestrzeganie prawidłowej kolejności.
To nie jest poprawna pominąć zależność b.sowobec d.sopodczas tworzenia tej pierwszej. Będziesz wtedy musiał określić bibliotekę podczas łączenia a, ale atak naprawdę nie potrzebuje bsamej liczby całkowitej , więc nie należy dbać o bwłasne zależności.
Oto przykład implikacji, jeśli pominiesz określenie zależności libb.so
$ export LD_LIBRARY_PATH=.# not needed if libs go to /usr/lib etc
$ g++-fpic -shared d.cpp -o libd.so
$ g++-fpic -shared b.cpp -o libb.so # wrong (but links)
$ g++-L.-lb a.cpp # wrong, as above
$ g++-Wl,--as-needed -L.-lb a.cpp # wrong, as above
$ g++ a.cpp -L.-lb # wrong, missing libd.so
$ g++ a.cpp -L.-ld -lb # wrong order (works on some distributions)
$ g++-Wl,--as-needed a.cpp -L.-ld -lb # wrong order (like static libs)
$ g++-Wl,--as-needed a.cpp -L.-lb -ld # "right"
Jeśli spojrzysz teraz na zależności, jakie ma plik binarny, zauważysz, że sam plik binarny zależy również od libd, a nie tylko libbtak, jak powinien. Plik binarny będzie musiał zostać ponownie połączony, jeśli libbpóźniej zależy od innej biblioteki, jeśli zrobisz to w ten sposób. A jeśli ktoś inny ładuje libbprzy użyciu dlopenw czasie wykonywania (pomyśl o dynamicznym ładowaniu wtyczek), wywołanie również się nie powiedzie. Więc tak "right"naprawdę powinno być wrong.
Powtarzaj, aż wszystkie symbole zostaną rozwiązane, eh - można by pomyśleć, że mogą zarządzać rodzajem topologicznym. LLVM ma 78 własnych bibliotek statycznych z zależnościami kto-co-wie. To prawda, że ma również skrypt do określania opcji kompilacji / łącza - ale nie można go używać we wszystkich okolicznościach.
Steve314,
6
@ Steve właśnie to robią programy lorder+ tsort. Ale czasami nie ma porządku, jeśli masz cykliczne odniesienia. Następnie wystarczy przejrzeć listę bibliotek, aż wszystko zostanie rozwiązane.
Johannes Schaub - litb
10
@Johannes - Określ maksymalne silnie połączone komponenty (np. Algorytm Tarjansa), a następnie sortuj topologicznie (z natury niecykliczny) digraf elementów. Każdy komponent może być traktowany jako jedna biblioteka - jeśli potrzebna jest jakakolwiek biblioteka z komponentu, cykle zależności spowodują, że wszystkie biblioteki w tym komponencie będą potrzebne. Więc nie, naprawdę nie ma potrzeby przechodzenia między wszystkimi bibliotekami w celu rozwiązania wszystkiego i nie ma potrzeby stosowania niewygodnych opcji wiersza poleceń - jedna metoda wykorzystująca dwa dobrze znane algorytmy może poprawnie obsłużyć wszystkie przypadki.
Steve314,
4
Chciałbym dodać jeden ważny szczegół do tej doskonałej odpowiedzi: użycie „- (archiwa -)” lub „--start-group archives --end-group” to jedyny pewny sposób rozwiązania zależności cyklicznych , ponieważ za każdym razem linker odwiedza archiwum, pobiera (i rejestruje nierozwiązane symbole) tylko pliki obiektowe, które rozwiązują aktualnie nierozwiązane symbole . Z tego powodu algorytm CMake powtarzania połączonych komponentów na wykresie zależności może czasami zawieść. (Zobacz także doskonały post Iana Lance'a Taylora na
linkerach,
3
Twoja odpowiedź pomogła mi rozwiązać moje błędy linkowania i bardzo jasno wyjaśniłeś, JAK uniknąć kłopotów, ale czy masz pojęcie DLACZEGO został zaprojektowany tak, aby działał w ten sposób?
Anton Daneyko
102
Linker GNU ld to tak zwany inteligentny linker. Będzie śledził funkcje używane przez poprzednie biblioteki statyczne, trwale odrzucając te funkcje, które nie są używane z jego tabel odnośników. W rezultacie, jeśli podłączysz bibliotekę statyczną zbyt wcześnie, funkcje w tej bibliotece nie będą już dostępne dla bibliotek statycznych później w linii łącza.
Typowy linker UNIX działa od lewej do prawej, więc umieść wszystkie zależne biblioteki po lewej, a te, które spełniają te zależności, po prawej stronie linii łącza. Może się okazać, że niektóre biblioteki zależą od innych, a jednocześnie inne biblioteki od nich zależą. To się komplikuje. Jeśli chodzi o odwołania cykliczne, napraw swój kod!
Czy to coś z tylko gnu ld / gcc? Czy jest to coś wspólnego z linkerami?
Mike
2
Najwyraźniej więcej kompilatorów uniksowych ma podobne problemy. MSVC nie jest całkowicie wolny od tych problemów, ale nie wydają się one takie złe.
MSalters
4
Narzędzia deweloperskie MS nie pokazują tych problemów tak często, ponieważ jeśli używasz łańcucha narzędziowego MS, kończy się to prawidłowym ustawieniem kolejności linkerów i nigdy nie zauważysz problemu.
Michael Kohne,
16
Linker MSVC jest mniej wrażliwy na ten problem, ponieważ przeszuka wszystkie biblioteki w poszukiwaniu niereferencyjnego symbolu. Kolejność bibliotek nadal może wpływać na to, który symbol zostanie rozwiązany, jeśli symbol ma więcej niż jedna biblioteka. Z MSDN: „Biblioteki są również przeszukiwane w kolejności wiersza poleceń, z następującym zastrzeżeniem: Symbole, które nie są rozwiązane podczas pobierania pliku obiektu z biblioteki, są najpierw wyszukiwane w tej bibliotece, a następnie następujące biblioteki z wiersza poleceń i / DEFAULTLIB (Określ domyślną bibliotekę), a następnie do dowolnych bibliotek na początku wiersza poleceń ”
Michael Burr
4
„... inteligentny linker ...” - Uważam, że jest klasyfikowany jako linker „jednoprzebiegowy”, a nie „inteligentny linker”.
jww
54
Oto przykład, aby wyjaśnić, jak działa GCC w przypadku bibliotek statycznych . Załóżmy więc, że mamy następujący scenariusz:
libmysqlclient- statyczny, na przykład (wolisz oczywiście bibliotekę współdzieloną, ponieważ libmysqlclientjest ogromna); w /usr/local/lib; i zależy od rzeczy zlibz
libz (dynamiczny)
Jak to połączyć? (Uwaga: przykłady z kompilacji na Cygwin przy użyciu gcc 4.3.4)
gcc -L/usr/local/lib -lmysqlclient myprog.o
# undefined reference to `_mysql_init'# myprog depends on libmysqlclient# so myprog has to come earlier on the command line
gcc myprog.o -L/usr/local/lib -lmysqlclient
# undefined reference to `_uncompress'# we have to link with libz, too
gcc myprog.o -lz -L/usr/local/lib -lmysqlclient
# undefined reference to `_uncompress'# libz is needed by libmysqlclient# so it has to appear *after* it on the command line
gcc myprog.o -L/usr/local/lib -lmysqlclient -lz
# this works
Kolejność w grupie jest ważna! Oto przykład: biblioteka debugowania ma procedurę debugowania, ale biblioteka bez debugowania ma jej słabą wersję. Musisz umieścić bibliotekę debugowania PIERWSZE w grupie, w przeciwnym razie przejdziesz do wersji bez debugowania.
Musisz poprzedzić każdą bibliotekę na liście grup -Xlinker
Szybka wskazówka, która mnie zaskoczyła: jeśli wywołujesz linker jako „gcc” lub „g ++”, to użycie „--start-group” i „--end-group” nie przekaże tych opcji do linker - nie będzie też oznaczać błędu. To po prostu zawiedzie łącze z niezdefiniowanymi symbolami, jeśli źle zrobisz porządek w bibliotece.
Musisz napisać je jako „-Wl, - start-group” itp., Aby powiedzieć GCC, aby przekazał argument do linkera.
Kolejność linków na pewno ma znaczenie, przynajmniej na niektórych platformach. Widziałem awarie aplikacji powiązanych z bibliotekami w niewłaściwej kolejności (gdzie zła oznacza A połączone przed B, ale B zależy od A).
Widziałem to bardzo często, niektóre nasze moduły łączą ponad 100 bibliotek naszego kodu oraz bibliotek systemowych i zewnętrznych.
W zależności od różnych linkerów HP / Intel / GCC / SUN / SGI / IBM / itp. Możesz uzyskać nierozwiązane funkcje / zmienne itp., Na niektórych platformach musisz dwukrotnie wyświetlić biblioteki.
W przeważającej części używamy strukturalnej hierarchii bibliotek, rdzenia, platformy, różnych warstw abstrakcji, ale w niektórych systemach nadal musisz grać w kolejności w poleceniu link.
Po znalezieniu dokumentu rozwiązania, aby następny programista nie musiał go ponownie opracowywać.
Mój dawny wykładowca zwykł mawiać: „ wysoka kohezja i niskie sprzężenie ”, to jest do dzisiaj prawdą.
gcc
zmieniono TLDR na bardziej rygorystyczne zachowanie.Odpowiedzi:
(Zobacz historię tej odpowiedzi, aby uzyskać bardziej skomplikowany tekst, ale myślę, że teraz czytelnikowi łatwiej jest zobaczyć prawdziwe wiersze poleceń).
Wspólne pliki współdzielone przez wszystkie poniższe polecenia
Łączenie z bibliotekami statycznymi
Linker wyszukuje od lewej do prawej i zauważa nierozstrzygnięte symbole. Jeśli biblioteka rozwiązuje symbol, pliki obiektowe tej biblioteki muszą rozwiązać symbol (w tym przypadku bo z libb.a).
Zależności między bibliotekami statycznymi działają tak samo - biblioteka, która potrzebuje symboli, musi być pierwsza, a następnie biblioteka, która rozwiązuje symbol.
Jeśli biblioteka statyczna zależy od innej biblioteki, ale druga biblioteka ponownie zależy od poprzedniej biblioteki, następuje cykl. Możesz rozwiązać ten problem, dołączając biblioteki zależne cyklicznie przez
-(
i-)
, np.-( -la -lb -)
(Może być konieczne ucieczkę przed parenami, takimi jak-\(
i-\)
). Linker przeszukuje te załączone biblioteki wiele razy, aby upewnić się, że zależności cykliczne zostały rozwiązane. Alternatywnie, można określić bibliotekach wiele razy, więc każdy ma przed sobą:-la -lb -la
.Łączenie z bibliotekami dynamicznymi
Tutaj jest tak samo - biblioteki muszą podążać za plikami obiektowymi programu. Różnica w porównaniu z bibliotekami statycznymi polega na tym, że nie musisz przejmować się wzajemnymi zależnościami bibliotek , ponieważ biblioteki dynamiczne same uporządkują ich zależności .
Niektóre najnowsze dystrybucje najwyraźniej domyślnie używają
--as-needed
flagi linkera, która wymusza, aby pliki obiektowe programu znajdowały się przed bibliotekami dynamicznymi. Jeśli ta flaga zostanie przekazana, linker nie będzie łączył się z bibliotekami, które w rzeczywistości nie są potrzebne plikowi wykonywalnemu (i wykrywa to od lewej do prawej). Moja ostatnia dystrybucja archlinux domyślnie nie używa tej flagi, więc nie dała błędu za nieprzestrzeganie prawidłowej kolejności.To nie jest poprawna pominąć zależność
b.so
wobecd.so
podczas tworzenia tej pierwszej. Będziesz wtedy musiał określić bibliotekę podczas łączeniaa
, alea
tak naprawdę nie potrzebujeb
samej liczby całkowitej , więc nie należy dbać ob
własne zależności.Oto przykład implikacji, jeśli pominiesz określenie zależności
libb.so
Jeśli spojrzysz teraz na zależności, jakie ma plik binarny, zauważysz, że sam plik binarny zależy również od
libd
, a nie tylkolibb
tak, jak powinien. Plik binarny będzie musiał zostać ponownie połączony, jeślilibb
później zależy od innej biblioteki, jeśli zrobisz to w ten sposób. A jeśli ktoś inny ładujelibb
przy użyciudlopen
w czasie wykonywania (pomyśl o dynamicznym ładowaniu wtyczek), wywołanie również się nie powiedzie. Więc tak"right"
naprawdę powinno byćwrong
.źródło
lorder
+tsort
. Ale czasami nie ma porządku, jeśli masz cykliczne odniesienia. Następnie wystarczy przejrzeć listę bibliotek, aż wszystko zostanie rozwiązane.Linker GNU ld to tak zwany inteligentny linker. Będzie śledził funkcje używane przez poprzednie biblioteki statyczne, trwale odrzucając te funkcje, które nie są używane z jego tabel odnośników. W rezultacie, jeśli podłączysz bibliotekę statyczną zbyt wcześnie, funkcje w tej bibliotece nie będą już dostępne dla bibliotek statycznych później w linii łącza.
Typowy linker UNIX działa od lewej do prawej, więc umieść wszystkie zależne biblioteki po lewej, a te, które spełniają te zależności, po prawej stronie linii łącza. Może się okazać, że niektóre biblioteki zależą od innych, a jednocześnie inne biblioteki od nich zależą. To się komplikuje. Jeśli chodzi o odwołania cykliczne, napraw swój kod!
źródło
Oto przykład, aby wyjaśnić, jak działa GCC w przypadku bibliotek statycznych . Załóżmy więc, że mamy następujący scenariusz:
myprog.o
- zawierającemain()
funkcję, zależną odlibmysqlclient
libmysqlclient
- statyczny, na przykład (wolisz oczywiście bibliotekę współdzieloną, ponieważlibmysqlclient
jest ogromna); w/usr/local/lib
; i zależy od rzeczy zlibz
libz
(dynamiczny)Jak to połączyć? (Uwaga: przykłady z kompilacji na Cygwin przy użyciu gcc 4.3.4)
źródło
Jeśli dodasz
-Wl,--start-group
do flag linkera, nie ma znaczenia, w jakiej są kolejności lub czy istnieją zależności cykliczne.W Qt oznacza to dodanie:
Oszczędza mnóstwo czasu na bałaganie i wydaje się, że nie spowalnia dużo linkowania (co i tak zajmuje znacznie mniej czasu niż kompilacja).
źródło
Inną alternatywą byłoby dwukrotne określenie listy bibliotek:
Robiąc to, nie musisz zawracać sobie głowy właściwą sekwencją, ponieważ odniesienie zostanie rozwiązane w drugim bloku.
źródło
Możesz użyć opcji -Xlinker.
jest PRAWIE równy
Ostrożnie!
źródło
Szybka wskazówka, która mnie zaskoczyła: jeśli wywołujesz linker jako „gcc” lub „g ++”, to użycie „--start-group” i „--end-group” nie przekaże tych opcji do linker - nie będzie też oznaczać błędu. To po prostu zawiedzie łącze z niezdefiniowanymi symbolami, jeśli źle zrobisz porządek w bibliotece.
Musisz napisać je jako „-Wl, - start-group” itp., Aby powiedzieć GCC, aby przekazał argument do linkera.
źródło
Kolejność linków na pewno ma znaczenie, przynajmniej na niektórych platformach. Widziałem awarie aplikacji powiązanych z bibliotekami w niewłaściwej kolejności (gdzie zła oznacza A połączone przed B, ale B zależy od A).
źródło
Widziałem to bardzo często, niektóre nasze moduły łączą ponad 100 bibliotek naszego kodu oraz bibliotek systemowych i zewnętrznych.
W zależności od różnych linkerów HP / Intel / GCC / SUN / SGI / IBM / itp. Możesz uzyskać nierozwiązane funkcje / zmienne itp., Na niektórych platformach musisz dwukrotnie wyświetlić biblioteki.
W przeważającej części używamy strukturalnej hierarchii bibliotek, rdzenia, platformy, różnych warstw abstrakcji, ale w niektórych systemach nadal musisz grać w kolejności w poleceniu link.
Po znalezieniu dokumentu rozwiązania, aby następny programista nie musiał go ponownie opracowywać.
Mój dawny wykładowca zwykł mawiać: „ wysoka kohezja i niskie sprzężenie ”, to jest do dzisiaj prawdą.
źródło