Antworten:
(Sehen Sie sich den Verlauf dieser Antwort an, um den ausführlicheren Text zu erhalten, aber ich denke jetzt, dass es für den Leser einfacher ist, echte Befehlszeilen zu sehen.)
Gemeinsame Dateien, die von allen unten aufgeführten Befehlen gemeinsam genutzt werden
$ 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
Der Linker sucht von links nach rechts und notiert dabei ungelöste Symbole. Wenn eine Bibliothek das Symbol auflöst, werden die Objektdateien dieser Bibliothek benötigt, um das Symbol aufzulösen (in diesem Fall bo aus libb.a).
Die Abhängigkeiten statischer Bibliotheken untereinander funktionieren gleich - die Bibliothek, die Symbole benötigt, muss zuerst die Bibliothek sein, die das Symbol auflöst.
Wenn eine statische Bibliothek von einer anderen Bibliothek abhängt, die andere Bibliothek jedoch wiederum von der vorherigen Bibliothek abhängt, gibt es einen Zyklus. Sie können dies beheben, indem Sie die zyklisch abhängigen Bibliotheken durch -(
und einschließen -)
(z. B. -( -la -lb -)
(Sie müssen möglicherweise den Parens entkommen, z. B. -\(
und -\)
). Der Linker durchsucht dann die eingeschlossene Bibliothek mehrmals, um sicherzustellen, dass die Fahrradabhängigkeiten aufgelöst werden. Alternativ können Sie die Bibliotheken mehrmals angeben, sodass sie jeweils voreinander liegen : -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
Hier ist es genauso - die Bibliotheken müssen den Objektdateien des Programms folgen. Der Unterschied zu statischen Bibliotheken besteht darin, dass Sie sich nicht um die Abhängigkeiten der Bibliotheken voneinander kümmern müssen, da dynamische Bibliotheken ihre Abhängigkeiten selbst sortieren .
Einige neuere Distributionen verwenden anscheinend standardmäßig das --as-needed
Linker-Flag, wodurch erzwungen wird, dass die Objektdateien des Programms vor den dynamischen Bibliotheken stehen. Wenn dieses Flag übergeben wird, verknüpft der Linker keine Bibliotheken, die von der ausführbaren Datei nicht benötigt werden (und erkennt dies von links nach rechts). Meine aktuelle Archlinux-Distribution verwendet dieses Flag standardmäßig nicht, daher wurde kein Fehler ausgegeben, weil die richtige Reihenfolge nicht eingehalten wurde.
Es ist nicht richtig, die Abhängigkeit von b.so
gegen d.so
beim Erstellen des ersteren wegzulassen . Sie müssen die Bibliothek dann beim Verknüpfen angeben a
, a
benötigen jedoch nicht wirklich die Ganzzahl b
selbst, sodass die b
eigenen Abhängigkeiten nicht berücksichtigt werden sollten .
Hier ist ein Beispiel für die Auswirkungen, wenn Sie die Abhängigkeiten für nicht angeben 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 nun untersuchen, welche Abhängigkeiten die Binärdatei hat, stellen Sie fest, dass die Binärdatei selbst auch davon abhängt libd
, nicht nur libb
wie sie sollte. Die Binärdatei muss erneut verknüpft werden, wenn sie libb
später von einer anderen Bibliothek abhängt. Wenn Sie dies auf diese Weise tun. Und wenn jemand andere Lasten libb
mit dlopen
zur Laufzeit (man denken an dem Laden von Plugins dynamisch), wird der Anruf als auch fehlschlagen. Also sollte das "right"
wirklich auch so sein wrong
.
lorder
+ tsort
. Aber manchmal gibt es keine Reihenfolge, wenn Sie zyklische Referenzen haben. Dann müssen Sie nur noch die Bibliotheksliste durchlaufen, bis alles gelöst ist.
Der GNU ld Linker ist ein sogenannter Smart Linker. Es verfolgt die Funktionen, die von vorhergehenden statischen Bibliotheken verwendet wurden, und wirft die Funktionen, die nicht verwendet werden, dauerhaft aus den Nachschlagetabellen. Wenn Sie eine statische Bibliothek zu früh verknüpfen, stehen die Funktionen in dieser Bibliothek statischen Bibliotheken später in der Verknüpfungszeile nicht mehr zur Verfügung.
Der typische UNIX-Linker funktioniert von links nach rechts. Platzieren Sie also alle abhängigen Bibliotheken links und diejenigen, die diese Abhängigkeiten erfüllen, rechts von der Linklinie. Möglicherweise stellen Sie fest, dass einige Bibliotheken von anderen abhängen, während andere Bibliotheken gleichzeitig von ihnen abhängen. Hier wird es kompliziert. Wenn es um Zirkelverweise geht, korrigieren Sie Ihren Code!
Hier ist ein Beispiel, um zu verdeutlichen, wie die Dinge mit GCC funktionieren, wenn statische Bibliotheken beteiligt sind. Nehmen wir also an, wir haben das folgende Szenario:
myprog.o
- enthält main()
Funktion, abhängig vonlibmysqlclient
libmysqlclient
- statisch, um des Beispiels willen (Sie würden natürlich die gemeinsam genutzte Bibliothek bevorzugen, da diese libmysqlclient
riesig ist); in /usr/local/lib
; und abhängig von Sachen auslibz
libz
(dynamisch)Wie verknüpfen wir das? (Hinweis: Beispiele aus dem Kompilieren 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 -Wl,--start-group
den Linker-Flags hinzufügen , ist es egal, in welcher Reihenfolge sie sich befinden oder ob zirkuläre Abhängigkeiten bestehen.
Auf Qt bedeutet dies das Hinzufügen von:
QMAKE_LFLAGS += -Wl,--start-group
Spart viel Zeit beim Herumspielen und scheint die Verknüpfung nicht viel zu verlangsamen (was ohnehin viel weniger Zeit in Anspruch nimmt als das Kompilieren).
Sie können die Option -Xlinker verwenden.
g++ -o foobar -Xlinker -start-group -Xlinker libA.a -Xlinker libB.a -Xlinker libC.a -Xlinker -end-group
ist fast gleich
g++ -o foobar -Xlinker -start-group -Xlinker libC.a -Xlinker libB.a -Xlinker libA.a -Xlinker -end-group
Vorsichtig !
Ein kurzer Tipp, der mich auslöste: Wenn Sie den Linker als "gcc" oder "g ++" aufrufen, werden diese Optionen bei Verwendung von "--start-group" und "--end-group" nicht an die weitergeleitet Linker - noch wird es einen Fehler kennzeichnen. Die Verknüpfung mit undefinierten Symbolen schlägt nur fehl, wenn die Bibliotheksreihenfolge falsch war.
Sie müssen sie als "-Wl, - start-group" usw. schreiben, um GCC anzuweisen, das Argument an den Linker weiterzuleiten.
Die Linkreihenfolge spielt sicherlich eine Rolle, zumindest auf einigen Plattformen. Ich habe Abstürze für Anwendungen gesehen, die mit Bibliotheken in falscher Reihenfolge verknüpft sind (wobei falsch bedeutet, dass A vor B verknüpft ist, B jedoch von A abhängt).
Ich habe dies oft gesehen, einige unserer Module verknüpfen mehr als 100 Bibliotheken unseres Codes plus System- und Drittanbieter-Bibliotheken.
Abhängig von verschiedenen Linkern HP / Intel / GCC / SUN / SGI / IBM / usw. können Sie ungelöste Funktionen / Variablen usw. erhalten. Auf einigen Plattformen müssen Sie Bibliotheken zweimal auflisten.
Zum größten Teil verwenden wir eine strukturierte Hierarchie von Bibliotheken, Kern, Plattform und verschiedenen Abstraktionsebenen, aber für einige Systeme müssen Sie immer noch mit der Reihenfolge im Befehl link spielen.
Sobald Sie auf ein Lösungsdokument gestoßen sind, muss der nächste Entwickler es nicht erneut ausarbeiten.
Mein alter Dozent sagte immer: " Hoher Zusammenhalt und niedrige Kopplung ", das ist bis heute so.
gcc
kürzlich (relativ) zu einem strengeren Verhalten geändert.