Verknüpfung mit dynamischer Bibliothek mit Abhängigkeiten

79

Stellen Sie sich das folgende Szenario vor:

  • Shared Library libA.so, ohne Abhängigkeiten.
  • Shared Library libB.so mit libA.so als Abhängigkeit.

Ich möchte eine Binärdatei kompilieren, die mit der libB verknüpft ist. Sollte ich die Binärdatei nur mit libB oder entweder mit libA verknüpfen?

Gibt es eine Möglichkeit, nur mit den direkten Abhängigkeiten zu verknüpfen und die Auflösung nicht aufgelöster Symbole aus den Abhängigkeiten zur Laufzeit zu ermöglichen?

Ich mache mir Sorgen darüber, dass sich die libB-Implementierung der Bibliothek in Zukunft ändern und andere Abhängigkeiten einführen könnte (z. B. libC, libD, libE). Werde ich damit Probleme haben?

Mit anderen Worten:

  • libA-Dateien: a.cpp ah
  • libB-Dateien: b.cpp bh
  • Hauptprogrammdateien: main.cpp

Natürlich enthält b.cpp ah und main.cpp bh

Kompilierungsbefehle:

Welche der folgenden Optionen sollte ich verwenden?

oder

Ich konnte die erste Option nicht verwenden. Der Linker beschwert sich über die ungelösten Symbole aus der Bibliothek libA. Aber es klingt ein bisschen seltsam für mich.

Vielen Dank.

- Aktualisierte Kommentare:

Wenn ich die Binärdatei verknüpfe, versucht der Linker, alle Symbole aus der Haupt- und der libB aufzulösen. LibB hat jedoch undefinierte Symbole aus der libA. Deshalb beschwert sich der Linker darüber.

Deshalb muss ich mich auch mit der libA verbinden. Ich habe jedoch einen Weg gefunden, ungelöste Symbole aus gemeinsam genutzten Bibliotheken zu ignorieren. Sieht so aus, als ob ich die folgende Befehlszeile verwenden sollte, um das zu tun:

Es sieht so aus, als ob es immer noch möglich ist, die -rpathOption zu verwenden. Allerdings muss ich es etwas besser verstehen.

Kennt jemand mögliche Fallstricke bei der Verwendung der -Wl,-unresolved-symbols=ignore-in-shared-libsOption?

- Aktualisierte Kommentare 2:

-rpathsollte nicht für diesen Zweck verwendet werden. Es ist nützlich, zu erzwingen, dass eine Bibliothek in einem bestimmten Verzeichnis gefunden wird. Der -unresolved-symbolAnsatz sieht viel besser aus.

Danke noch einmal.

Marcus
quelle
Hier ist ein lauffähiges minimales Beispiel für diejenigen, die diese Art von Sachen
testen möchten

Antworten:

42

Es sieht so aus, als wären Sie schon den größten Teil des Weges dorthin. Gut gemacht mit Ihrer Untersuchung. Mal sehen, ob ich helfen kann, das "Warum" dahinter zu klären.

Hier ist, was der Linker tut. Wenn Sie Ihre ausführbare Datei ('main' oben) verknüpfen, sind einige Symbole (Funktionen und andere Dinge) ungelöst. Es wird die Liste der folgenden Bibliotheken durchgesehen und versucht, ungelöste Symbole aufzulösen. Unterwegs stellt es fest, dass einige der Symbole von libB.so bereitgestellt werden, und stellt fest, dass sie jetzt von dieser Bibliothek aufgelöst werden.

Es wird jedoch auch festgestellt, dass einige dieser Symbole andere Symbole verwenden, die in Ihrer ausführbaren Datei noch nicht aufgelöst sind. Daher müssen diese nun auch aufgelöst werden. Ohne Verknüpfung mit libA.so wäre Ihre Bewerbung unvollständig. Sobald die Verknüpfung mit libA.so hergestellt ist, werden alle Symbole aufgelöst und die Verknüpfung ist abgeschlossen.

Wie Sie gesehen haben, -unresolved-symbols-in-shared-libsbehebt die Verwendung von das Problem nicht. Es wird nur so verschoben, dass diese Symbole zur Laufzeit aufgelöst werden. Das ist es, wofür -rpath: die Bibliotheken angegeben werden sollen, die zur Laufzeit durchsucht werden sollen. Wenn diese Symbole nicht aufgelöst werden können, kann Ihre App nicht gestartet werden.

Es ist nicht einfach, Bibliotheksabhängigkeiten herauszufinden, da ein Symbol von mehr als einer Bibliothek bereitgestellt werden kann und durch Verknüpfung mit einer dieser Bibliotheken erfüllt werden kann.

Es gibt hier eine andere Beschreibung dieses Prozesses: Warum verursacht die Reihenfolge, in der Bibliotheken verknüpft sind, manchmal Fehler in GCC?

Kithril
quelle
2
Ich weiß, dass es lange her ist, ich habe vergessen, es als gelöst zu markieren. Vielen Dank für Ihre Antwort.]
Marcus
Vielen Dank für die erstaunliche Frage und eine angemessene Antwort! Ich habe fast genau das gleiche Szenario. Ich versuche, freigegebene openCV-Objekte in ein benutzerdefiniertes freigegebenes Objekt zu verpacken. Nehmen wir an, C.so ist meine Gewohnheit, und CV.so ist eine OpenCV. Ich habe C.so erfolgreich mit CV.so verknüpft, und ich möchte eine main.cpp (Sie bekommen es!) Mit C.so verknüpfen, aber abgesehen von der Verwendung von Objekten aus C.so verwendet main.cpp eine Objekt 'cv :: Mat', das in CV.so definiert ist. In diesem Fall muss für main.cpp eine Verknüpfung sowohl mit C.so als auch mit CV.so hergestellt werden? Ich würde es lieben, wenn jemand Licht ins Dunkel bringen würde. Vielen Dank! :)
Lenny Linus
2
Warum löst der Linker bei der Erstellung von libB.so nicht alle erforderlichen libA.so-Symbole auf? Die Hauptanwendung sollte nicht mit libA verknüpft sein müssen, da sie transparent sein sollte, z. B. indirekt durch libB geschützt?
Wilson
Das ist falsch. Linker verwendet libA.so nur, um einen Benutzer über eine unvollständige Abhängigkeitskette zu informieren. Ohne die libA.so anzugeben, aber stattdessen die -unresolved-symbole = ignore-in-shared-libs zu übergeben, erhalten Sie genau dieselbe ausführbare Datei.
Listerreg
21

Für die dynamische Verknüpfung nur mit direkten Abhängigkeiten Sie können -Wl,--as-neededmit dem Hinzufügen von LIBS nach -Wl,--as-needed :

Zum Überprüfen der direkten Abhängigkeiten sollten Sie readelf anstelle von ldd verwenden, da ldd auch die indirekten Abhängigkeiten anzeigt.

ldd zeigt auch die indirekten Abhängigkeiten:

Wenn Sie cmake verwenden , können Sie die folgenden Zeilen hinzufügen, um nur direkte Abhängigkeiten einzuschließen :

user1047271
quelle
Entschuldigung, ich habe Ihr Beispiel als c-Programm anstelle von c ++ erstellt. Deshalb habe ich den gcc Compiler benutzt. Es funktioniert aber auch mit g ++.
user1047271
1
Das sind wirklich nützliche Informationen. Ich wusste nicht, wie man sich selbst anstelle von ldd verwendet. Vielen Dank.
Marcus
1

Eine andere Option ist zu verwenden libtool

Wenn Sie den g++Aufruf ändern libtool --mode=compile g++, um den Quellcode zu kompilieren und dann libtool --mode=link g++die Anwendung aus zu erstellen libB, libAwird diese automatisch verknüpft.

mattmilten
quelle
0

Dies ist ein interessanter Beitrag - ich habe mir auch damit den Kopf geschlagen, aber ich denke, Sie verpassen hier einen Punkt.

Die Idee ist wie folgt, richtig?

Betrachten wir das weiter.

  • In a.cpp (und nur dort) definieren Sie eine Klasse / Variable, nennen wir sie "symA"
  • In b.cpp (und nur dort) definieren Sie eine Klasse / Variable, nennen wir sie "symB".
  • symB verwendet symA
  • main.cpp verwendet symB

Jetzt wurden libB.so und libA.so wie oben beschrieben kompiliert. Danach wird die erste Option sollte funktionieren, das heißt:

Ich denke, dass Ihr Problem von der Tatsache herrührt, dass

in main.cpp verweisen Sie auch auf symA

Hab ich recht?

Wenn Sie in Ihrem Code ein Symbol verwenden, muss dieses Symbol in einer .so-Datei enthalten sein

Die gesamte Idee der Verweise auf gemeinsam genutzte Bibliotheken (dh das Erstellen von APIs) besteht darin, dass die Symbole in den tieferen Ebenen ausgeblendet werden (denken Sie an das Schälen von Zwiebeln) und nicht verwendet werden. .. dh beziehen Sie sich nicht auf symA in Ihrer main.cpp, sondern nur auf symB (und lassen Sie symB nur symA verweisen).

El Sampsa
quelle
1
Du hast es falsch verstanden. Offensichtlich müssen Sie die sofort erforderlichen Symbole auflösen. Das Problem hängt mit Symbolen zusammen, die Ihre Abhängigkeiten möglicherweise erfordern. Es ist üblich, wenn Bibliotheken entwickelt werden, die von Drittanbietern verwendet werden sollen, wobei der Bibliotheksbenutzer die von der Bibliothek verwendeten Funktionen implementieren muss. Sie entwickeln mit einem Stub, ohne Abhängigkeiten, und der Integrator entwickelt eine Bibliothek, um den Stub zu ersetzen, und verknüpft dann alles miteinander. Der Loader muss alle Abhängigkeiten auflösen. Ihre Kompilierungsskripte (und die Ihrer Benutzer) müssen dies jedoch berücksichtigen.
Marcus