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
$ cat a.cpp
extern int a;
int main() {
return a;
}
$ cat b.cpp
extern int b;
int a = b;
$ cat d.cpp
int b;
$ 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
.
$ 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-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
wobec d.so
podczas tworzenia tej pierwszej. Będziesz wtedy musiał określić bibliotekę podczas łączenia a
, ale a
tak naprawdę nie potrzebuje b
samej liczby całkowitej , więc nie należy dbać o b
wł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 libb
tak, jak powinien. Plik binarny będzie musiał zostać ponownie połączony, jeśli libb
później zależy od innej biblioteki, jeśli zrobisz to w ten sposób. A jeśli ktoś inny ładuje libb
przy użyciu dlopen
w czasie wykonywania (pomyśl o dynamicznym ładowaniu wtyczek), wywołanie również się nie powiedzie. Więc tak "right"
naprawdę powinno być wrong
.
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!
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ące main()
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)
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
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:
QMAKE_LFLAGS += -Wl,--start-group
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).
Możesz użyć opcji -Xlinker.
g++ -o foobar -Xlinker -start-group -Xlinker libA.a -Xlinker libB.a -Xlinker libC.a -Xlinker -end-group
jest PRAWIE równy
g++ -o foobar -Xlinker -start-group -Xlinker libC.a -Xlinker libB.a -Xlinker libA.a -Xlinker -end-group
Ostrożnie!
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.