Warum führt die Reihenfolge, in der Bibliotheken gelinkt werden, manchmal zu Fehlern im GCC?
Antworten
Zu viele Anzeigen?(In der Historie zu dieser Antwort finden Sie den ausführlicheren Text, aber ich denke, es ist einfacher für den Leser, echte Befehlszeilen zu sehen).
Gemeinsame Dateien für alle folgenden Befehle
// a depends on b, b depends on d
$ cat a.cpp
extern int a;
int main() {
return a;
}
$ cat b.cpp
extern int b;
int a = b;
$ cat d.cpp
int b;
Verknüpfung mit statischen Bibliotheken
$ 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
Der Linker sucht von links nach rechts und notiert dabei nicht aufgelöste Symbole. Wenn eine Bibliothek das Symbol auflöst, nimmt er die Objektdateien dieser Bibliothek, um das Symbol aufzulösen (in diesem Fall b.o aus libb.a).
Abhängigkeiten von statischen Bibliotheken untereinander funktionieren auf die gleiche Weise - die Bibliothek, die Symbole benötigt, muss an erster Stelle stehen, dann die Bibliothek, die das Symbol auflöst.
Wenn eine statische Bibliothek von einer anderen Bibliothek abhängt, diese aber wiederum von der erstgenannten Bibliothek abhängt, entsteht ein Zyklus. Sie können dies auflösen, indem Sie die zyklisch abhängigen Bibliotheken mit -(
y -)
, wie zum Beispiel -( -la -lb -)
(eventuell müssen Sie die Parens auslassen, z. B. -\(
y -\)
). Der Linker durchsucht diese eingeschlossenen Bibliotheken dann mehrfach, um sicherzustellen, dass die zyklischen Abhängigkeiten aufgelöst werden. Alternativ können Sie die Bibliotheken auch mehrfach angeben, so dass jede vor der anderen steht: -la -lb -la
.
Verknüpfung mit dynamischen Bibliotheken
$ 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
Hier ist es dasselbe - die Bibliotheken müssen den Objektdateien des Programms folgen. Der Unterschied zu statischen Bibliotheken besteht darin, dass man sich nicht um die Abhängigkeiten der Bibliotheken untereinander kümmern muss, da dynamische Bibliotheken sortieren ihre Abhängigkeiten selbst .
Einige neuere Distributionen verwenden anscheinend standardmäßig die --as-needed
Linker-Flag, das erzwingt, dass die Objektdateien des Programms vor den dynamischen Bibliotheken kommen. Wenn dieses Flag übergeben wird, wird der Linker nicht auf Bibliotheken linken, die nicht wirklich von der ausführbaren Datei benötigt werden (und er erkennt dies von links nach rechts). Meine aktuelle Archlinux-Distribution verwendet dieses Flag standardmäßig nicht, so dass es keine Fehlermeldung wegen Nichteinhaltung der korrekten Reihenfolge gab.
Es ist nicht korrekt, die Abhängigkeit von b.so
contra d.so
bei der Erstellung des Ersteren. Sie müssen die Bibliothek beim Verknüpfen angeben a
dann, aber a
braucht die Ganzzahl nicht wirklich b
selbst, daher sollte sie nicht dazu gebracht werden, sich um b
die eigenen Abhängigkeiten.
Hier ist ein Beispiel für die Auswirkungen, wenn Sie die Abhängigkeiten für 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"
Wenn Sie sich nun ansehen, welche Abhängigkeiten die Binärdatei hat, stellen Sie fest, dass die Binärdatei selbst auch abhängig ist von libd
, nicht nur libb
wie es sein sollte. Die Binärdatei muss neu verknüpft werden, wenn libb
später von einer anderen Bibliothek abhängt, wenn Sie es auf diese Weise machen. Und wenn jemand anderes lädt libb
mit dlopen
zur Laufzeit (denken Sie an das dynamische Laden von Plugins), wird der Aufruf ebenfalls fehlschlagen. Also wird der "right"
sollte wirklich ein wrong
auch.
Der GNU ld Linker ist ein sogenannter Smart Linker. Er verfolgt die Funktionen, die von vorangehenden statischen Bibliotheken verwendet werden, und wirft die Funktionen, die nicht verwendet werden, permanent aus seinen Lookup-Tabellen. Das Ergebnis ist, dass, wenn Sie eine statische Bibliothek zu früh linken, dann die Funktionen in dieser Bibliothek nicht mehr für statische Bibliotheken später auf der Link-Linie verfügbar sind.
Der typische UNIX-Linker arbeitet von links nach rechts. Legen Sie also alle abhängigen Bibliotheken auf die linke Seite und die Bibliotheken, die diese Abhängigkeiten erfüllen, auf die rechte Seite der Linkzeile. Möglicherweise stellen Sie fest, dass einige Bibliotheken von anderen abhängen, während gleichzeitig andere Bibliotheken von ihnen abhängen. An dieser Stelle wird es kompliziert. Wenn es um zirkuläre Verweise geht, korrigieren Sie Ihren Code!
Hier ist ein Beispiel, um zu verdeutlichen, wie die Dinge mit GCC funktionieren, wenn statisch Bibliotheken beteiligt sind. Gehen wir also von folgendem Szenario aus:
myprog.o
- enthaltendmain()
Funktion, abhängig vonlibmysqlclient
libmysqlclient
- statisch, um des Beispiels willen (Sie würden natürlich die Shared Library bevorzugen, da dielibmysqlclient
ist riesig); in/usr/local/lib
und abhängig von Dingen auslibz
libz
(dynamisch)
Wie können wir dies miteinander verbinden? (Hinweis: Beispiele aus der Kompilierung auf Cygwin mit 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
Wenn Sie Folgendes hinzufügen -Wl,--start-group
zu den Linker-Flags ist es egal, in welcher Reihenfolge sie stehen oder ob es zirkuläre Abhängigkeiten gibt.
Bei Qt bedeutet dies das Hinzufügen:
QMAKE_LFLAGS += -Wl,--start-group
Das spart eine Menge Zeit und scheint das Linken nicht zu verlangsamen (was ohnehin viel weniger Zeit in Anspruch nimmt als das Kompilieren).
- See previous answers
- Weitere Antworten anzeigen
1 Stimmen
Siehe auch jetzt stackoverflow.com/questions/7826448/ -- TLDR
gcc
vor kurzem zu einem (relativ) strengeren Verhalten übergegangen.