596 Stimmen

Warum führt die Reihenfolge, in der Bibliotheken gelinkt werden, manchmal zu Fehlern im GCC?

Warum führt die Reihenfolge, in der Bibliotheken gelinkt werden, manchmal zu Fehlern im GCC?

1 Stimmen

Siehe auch jetzt stackoverflow.com/questions/7826448/ -- TLDR gcc vor kurzem zu einem (relativ) strengeren Verhalten übergegangen.

705voto

(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.

145voto

casualcoder Punkte 4476

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!

63voto

Lumi Punkte 14158

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 - enthaltend main() Funktion, abhängig von libmysqlclient
  • libmysqlclient - statisch, um des Beispiels willen (Sie würden natürlich die Shared Library bevorzugen, da die libmysqlclient ist riesig); in /usr/local/lib und abhängig von Dingen aus libz
  • 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

54voto

SvaLopLop Punkte 919

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).

12voto

eckes Punkte 59894

Eine andere Möglichkeit wäre, die Liste der Bibliotheken zweimal anzugeben:

gcc prog.o libA.a libB.a libA.a libB.a -o prog.x

Auf diese Weise müssen Sie sich nicht um die richtige Reihenfolge kümmern, da die Referenz im zweiten Block aufgelöst wird.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X