wake-up-neo.com

ld-Linker-Frage: Die Option --whole-archive

Die einzige wirkliche Verwendung der Linker-Option --whole-archive, die ich gesehen habe, ist das Erstellen von gemeinsam genutzten Bibliotheken aus statischen. Vor kurzem bin ich auf Makefile (s) gestoßen, die diese Option immer verwenden, wenn sie mit statischen internen Bibliotheken verbunden werden. Dies führt natürlich dazu, dass die ausführbaren Dateien nicht referenzierten Objektcode unnötigerweise einlesen. Meine Reaktion darauf war, dass dies eindeutig falsch ist. Fehlt mir hier etwas? 

Die zweite Frage, die ich habe, hat etwas zu tun, das ich bezüglich der Option "Ganzes Archiv" gelesen habe, aber nicht ganz analysieren konnte. Dies hat den Effekt, dass die --whole-archive-Option beim Verknüpfen mit einer statischen Bibliothek verwendet werden sollte, wenn die ausführbare Datei auch mit einer gemeinsam genutzten Bibliothek verlinkt wird, die wiederum denselben Objektcode wie die statische Bibliothek aufweist. Das heißt, die gemeinsam genutzte Bibliothek und die statische Bibliothek haben sich in Bezug auf den Objektcode überlappt. Wenn Sie diese Option verwenden, werden alle Symbole (unabhängig von der Verwendung) in der ausführbaren Datei aufgelöst. Dies soll Objektcode-Duplizierung vermeiden. Dies ist verwirrend, wenn ein Symbol im Programm referenziert wird, muss es zum Zeitpunkt der Verknüpfung eindeutig aufgelöst werden. Worum geht es dabei bei der Duplizierung? (Vergib mir, wenn dieser Absatz nicht der Inbegriff von Klarheit ist)

Vielen Dank 

32
jasmeet

Es gibt legitime Anwendungen von --whole-archive beim Verknüpfen der ausführbaren Datei mit statischen Bibliotheken. Ein Beispiel ist das Erstellen von C++ - Code, bei dem globale Instanzen sich in ihren Konstruktoren "registrieren" (Warnung: ungetesteter Code):

main.cc

typedef void (*handler)(const char *protocol);
typedef map<const char *, handler> M;
M m;

void register_handler(const char *protocol, handler) {
   m[protocol] = handler;
}
int main(int argc, char *argv[])
{
   for (int i = 1; i < argc-1; i+= 2) {
      M::iterator it = m.find(argv[i]);
      if (it != m.end()) it.second(argv[i+1]);
   }
}

http.cc (Teil von libhttp.a)

class HttpHandler {
  HttpHandler() { register_handler("http", &handle_http); }
  static void handle_http(const char *) { /* whatever */ }
};
HttpHandler h; // registers itself with main!

Beachten Sie, dass es in http.cc keine Symbole gibt, die main.cc benötigt. Wenn Sie dies als verknüpfen

g++ main.cc -lhttp

sie erhalten nicht einen http-Handler, der mit der Hauptprogrammdatei verknüpft ist, und kann handle_http() nicht aufrufen. Vergleichen Sie dies mit dem, was passiert, wenn Sie als verknüpfen:

g++ main.cc -Wl,--whole-archive -lhttp -Wl,--no-whole-archive

Der gleiche "Selbstregistrierungs" -Stil ist auch in normalem C möglich, z. mit der Erweiterung __attribute__((constructor)) GNU.

57

Eine weitere legitime Verwendung für --whole-archive ist für Toolkit-Entwickler, um Bibliotheken mit mehreren Funktionen in einer einzigen statischen Bibliothek zu verteilen. In diesem Fall hat der Anbieter keine Ahnung, welche Teile der Bibliothek vom Verbraucher verwendet werden, und muss daher alles enthalten. 

9
Steve Brooks

Ich bin damit einverstanden, dass die Verwendung von - Whole-Archive zum Erstellen von ausführbaren Dateien wahrscheinlich nicht das ist, was Sie möchten (aufgrund der Verknüpfung nicht benötigten Codes und der Erstellung aufgeblähter Software). Wenn sie einen guten Grund hatten, sollten sie dies im Build-System dokumentieren, da Sie jetzt nur noch raten müssen.

Zum zweiten Teil der Frage. Wenn eine ausführbare Datei sowohl eine statische Bibliothek als auch eine dynamische Bibliothek verknüpft, die (zum Teil) den gleichen Objektcode wie die statische Bibliothek hat, stellt das -ole-archive sicher, dass zum Zeitpunkt der Verknüpfung der Code aus der statische Bibliothek wird bevorzugt. Dies ist normalerweise das, was Sie bei statischen Verknüpfungen wünschen.

4
lothar

Alte Abfrage, aber bei Ihrer ersten Frage ("Warum") habe ich gesehen, dass - Whole-Archive auch für Inhouse-Bibliotheken verwendet wird, hauptsächlich um zirkuläre Referenzen zwischen diesen Bibliotheken zu umgehen. Es neigt dazu, die schlechte Architektur der Bibliotheken zu verbergen, also würde ich es nicht empfehlen. Es ist jedoch ein schneller Weg, um eine schnelle Testphase zu erhalten.

Wenn bei der zweiten Abfrage dasselbe Symbol in einem gemeinsam genutzten Objekt und in einer statischen Bibliothek vorhanden war, erfüllt der Linker die Referenz mit der Bibliothek, die er zuerst gefunden hat.
Wenn die gemeinsam genutzte Bibliothek und die statische Bibliothek eine exakte gemeinsame Nutzung von Code haben, kann dies alles nur funktionieren. Wenn jedoch die gemeinsam genutzte Bibliothek und die statische Bibliothek unterschiedliche Implementierungen der gleichen Symbole aufweisen, wird Ihr Programm zwar kompiliert, verhält sich jedoch je nach Reihenfolge der Bibliotheken anders.

Das Erzwingen, dass alle Symbole aus der statischen Bibliothek geladen werden, ist eine Möglichkeit, Verwirrung darüber zu beseitigen, was von wo geladen wird. Im Allgemeinen klingt dies jedoch nach der Lösung des falschen Problems. Sie möchten meistens nicht die gleichen Symbole in verschiedenen Bibliotheken.

3
Rob Swarbrick

Ein weiteres gutes Szenario, in dem --whole-archive gut verwendet wird, ist der Umgang mit statischen Bibliotheken und inkrementellen Verknüpfungen.

Nehmen wir an, dass

  1. libA implementiert die Funktionen a() und b().
  2. Ein Teil des Programms muss nur mit libA verknüpft sein, z. aufgrund einiger Funktionsumbrüche mit --wrap (ein klassisches Beispiel ist malloc)
  3. libC implementiert die c()-Funktionen und verwendet a()
  4. das letzte Programm verwendet a() und c()

Inkrementelle Verknüpfungsschritte könnten sein:

ld -r -o step1.o module1.o --wrap malloc --whole-archive -lA
ld -r -o step2.o step1.o module2.o --whole-archive -lC
cc step3.o module3.o -o program

Wenn Sie --whole-archive nicht einfügen, wird die Funktion c() entfernt, die jedoch von program verwendet wird, wodurch der korrekte Kompilierungsprozess verhindert wird.

Dies ist natürlich ein besonderer Fall, in dem inkrementelle Verknüpfungen vorgenommen werden müssen, um zu vermeiden, dass alle Aufrufe von malloc in allen Modulen eingeschlossen werden. Dies ist jedoch ein Fall, der erfolgreich von --whole-archive unterstützt wird.

0
ilpelle