Warum spielt die Reihenfolge der Option „-l“ in gcc eine Rolle? [duplicate]

Lesezeit: 7 Minuten

Ich versuche, ein Programm zu kompilieren, das verwendet udis86 Bibliothek. Eigentlich verwende ich ein Beispielprogramm, das in der angegeben ist Benutzerhandbuch der Bibliothek. Aber beim Kompilieren gibt es einen Fehler. Die Fehler, die ich bekomme, sind:

example.c:(.text+0x7): undefined reference to 'ud_init'
example.c:(.text+0x7): undefined reference to 'ud_set_input_file'
.
.
example.c:(.text+0x7): undefined reference to 'ud_insn_asm'

Der Befehl, den ich verwende, lautet:

$ gcc -ludis86 example.c -o example 

wie in der Bedienungsanleitung beschrieben.

Der Linker ist eindeutig nicht in der Lage, die Libudis-Bibliothek zu verknüpfen. Aber wenn ich meinen Befehl ändere zu:

$ gcc example.c -ludis86 -o example 

Es beginnt zu arbeiten. Kann bitte jemand erklären, was das Problem mit dem ersten Befehl ist?

  • welche gcc-version? Es könnte ein Versionsfehler sein.

    – EnabrenTane

    10. August 2012 um 0:52 Uhr

  • Es ist kein Fehler!! Version ist: gcc (Ubuntu/Linaro 4.4.4-14ubuntu5.1) 4.4.5

    Benutzer1129237

    10. August 2012 um 1:10 Uhr

AnT steht mit Russlands Benutzer-Avatar
AnT steht zu Russland

Denn so funktioniert der Linking-Algorithmus, der vom GNU-Linker verwendet wird (zumindest wenn es um das Linken statischer Bibliotheken geht). Der Linker ist ein Single-Pass-Linker und besucht Bibliotheken nicht erneut, sobald sie gesehen wurden.

Eine Bibliothek ist eine Sammlung (ein Archiv) von Objektdateien. Wenn Sie eine Bibliothek mit der -l Option, die der Linker nicht bedingungslos übernimmt alle Objektdateien aus der Bibliothek. Es werden nur die Objektdateien benötigt, die vorhanden sind derzeit benötigt, dh Dateien, die einige derzeit nicht aufgelöste (ausstehende) Symbole auflösen. Danach vergisst der Linker diese Bibliothek vollständig.

Die Liste der anhängigen Symbole wird kontinuierlich vom Linker gepflegt, während der Linker Eingabeobjektdateien verarbeitet, eine nach der anderen von links nach rechts. Bei der Verarbeitung jeder Objektdatei werden einige Symbole aufgelöst und aus der Liste entfernt, andere neu entdeckte, nicht aufgelöste Symbole werden der Liste hinzugefügt.

Wenn Sie also eine Bibliothek mit eingeschlossen haben -l, verwendet der Linker diese Bibliothek, um so viele derzeit ausstehende Symbole wie möglich aufzulösen, und vergisst dann diese Bibliothek vollständig. Wenn es später plötzlich entdeckt, dass er jetzt einige zusätzliche Objektdateien aus dieser Bibliothek benötigt, wird der Linker nicht zu dieser Bibliothek “zurückkehren”, um diese zusätzlichen Objektdateien abzurufen. Es ist bereits zu spät.

Aus diesem Grund ist es immer eine gute Idee, es zu verwenden -l Möglichkeit spät in der Befehlszeile des Linkers, damit der Linker zu dem Zeitpunkt dorthin gelangt -l es kann zuverlässig feststellen, welche Objektdateien es benötigt und welche nicht. Platzieren der -l Option als allererster Parameter für den Linker macht im Allgemeinen überhaupt keinen Sinn: Ganz am Anfang ist die Liste der ausstehenden Symbole leer (oder genauer gesagt besteht sie aus einzelnen Symbolen main), was bedeutet, dass der Linker überhaupt nichts aus der Bibliothek übernimmt.

In Ihrem Fall Ihre Objektdatei example.o enthält Verweise auf Symbole ud_init, ud_set_input_file usw. Der Linker sollte diese Objektdatei zuerst erhalten. Diese Symbole werden der Liste der ausstehenden Symbole hinzugefügt. Danach können Sie verwenden -l Option zum Hinzufügen Ihrer Bibliothek: -ludis86. Der Linker durchsucht Ihre Bibliothek und nimmt alles heraus, was diese ausstehenden Symbole auflöst.

Wenn Sie die platzieren -ludis86 Option zuerst in der Befehlszeile, wird der Linker effektiv ignorieren Ihre Bibliothek, da sie am Anfang nicht weiß, dass sie sie brauchen wird ud_init, ud_set_input_file usw. Später bei der Bearbeitung example.o Es erkennt diese Symbole und fügt sie der Liste der ausstehenden Symbole hinzu. Aber diese Symbole werden bis zum Ende ungelöst bleiben, da -ludis86 wurde bereits verarbeitet (und effektiv ignoriert).

Manchmal, wenn zwei (oder mehr) Bibliotheken zirkulär aufeinander verweisen, muss man vielleicht sogar die verwenden -l Option zweimal mit derselben Bibliothek, um dem Linker zwei Möglichkeiten zu geben, die erforderlichen Objektdateien aus dieser Bibliothek abzurufen.

  • Es ist nicht nur eine GNU-Sache. Dies ist das standardmäßige, von POSIX geforderte Verhalten: -l Bibliothek Durchsucht die Bibliothek namens liblibrary.a. Eine Bibliothek soll durchsucht werden, wenn ihr Name gefunden wird, daher ist die Platzierung einer Option -l von Bedeutung. Mehrere Standardbibliotheken können auf diese Weise angegeben werden, wie im Abschnitt ERWEITERTE BESCHREIBUNG beschrieben. Implementierungen können andere implementierungsdefinierte Suffixe als .a als Bezeichnung für Bibliotheken erkennen. Sehen pubs.opengroup.org/onlinepubs/9699919799/utilities/c99.html

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    10. August 2012 um 2:37 Uhr

  • @R .. Dies wirft die Frage auf, warum der Standard dieses Verhalten erfordert? Gibt es irgendeinen Vorteil, wenn man diesen Ansatz verwendet? Andere Compiler-Tools wie msvc und borland folgen diesem Ansatz nicht und es funktioniert einwandfrei. In vielerlei Hinsicht scheint es besser zu sein, da es für Benutzer dieses Tools weniger fehleranfällig ist.

    – Großwolf

    4. August 2013 um 1:44 Uhr


  • @greatwolf: MSVC ist genau das Gegenteil von “funktioniert einwandfrei”, wenn es um C geht. Wie auch immer, die Motivation für die Reihenfolge ist, dass Sie dieselben Symbole in mehr als einer Bibliothek definieren können, in diesem Fall Sie wollen in der Lage sein, zu steuern, welche verwendet wird.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    4. August 2013 um 2:15 Uhr

  • Mein Eindruck ist, dass dies nicht nur ein statisches Bibliotheksproblem ist, wenn Sie beispielsweise explizit -l:libwhatever.so angeben, bleibt der undefinierte Verweis linkererror bestehen, solange das -l:libwhatever.so-Token früher im gcc-Befehl auftritt als das object_file.o-Token

    – alexandre iolov

    27. Dezember 2014 um 13:16 Uhr

  • Vielleicht möchten Sie einen Absatz über Gruppen in GNUs hinzufügen ld. Sehen --start-group und --end-group in dem ld(1) Manpage. Es weist den Linker effektiv an, Archive in der Gruppe erneut zu besuchen.

    – jww

    18. Februar 2018 um 7:11 Uhr


Selbies Benutzeravatar
Selbie

Ich bin vor einiger Zeit auf dasselbe Problem gestoßen. Fazit ist, dass GNU-Tools nicht immer in der Bibliotheksliste “zurücksuchen”, um fehlende Symbole aufzulösen. Einfache Korrekturen sind eine der folgenden:

  1. Geben Sie einfach die libs und objs in der Abhängigkeitsreihenfolge an (wie Sie oben entdeckt haben).

  2. ODER wenn Sie eine zirkuläre Abhängigkeit haben (wobei libA auf eine Funktion in libB verweist, aber libB auf eine Funktion in libA verweist), geben Sie die libs einfach zweimal in der Befehlszeile an. Das schlägt auch die Handbuchseite vor. Z.B

    gcc foo.c -lfoo -lbar -lfoo
    
  3. Verwenden Sie die -( und -) params, um eine Gruppe von Archiven anzugeben, die solche zirkulären Abhängigkeiten haben. Schauen Sie im GNU-Linker-Handbuch nach --start-group und --end-group. Sehen hier für mehr Details.

Wenn Sie Option 2 oder 3 verwenden, führen Sie wahrscheinlich Leistungskosten für die Verknüpfung ein. Wenn Sie nicht so viel zu verlinken haben, spielt es vielleicht keine Rolle.

Oder verwenden neu scannen

ab Seite 41 des Handbuch für Oracle Solaris 11.1 Linker und Bibliotheken:

Zwischen Archiven können Abhängigkeiten bestehen, so dass das Extrahieren von Mitgliedern aus einem Archiv durch Extrahieren von Mitgliedern aus einem anderen Archiv gelöst werden muss. Wenn diese Abhängigkeiten zyklisch sind, müssen die Archive wiederholt in der Befehlszeile angegeben werden, um vorherige Verweise zu erfüllen.

$ cc -o prog .... -lA -lB -lC -lA -lB -lC -lA 

Die Ermittlung und Pflege wiederholter Archivspezifikationen kann mühsam sein.

Die Option -z rescan-now vereinfacht diesen Vorgang. Die Option -z rescan-now wird vom Link-Editor sofort verarbeitet, wenn die Option in der Befehlszeile angetroffen wird. Alle Archive, die vor dieser Option über die Befehlszeile verarbeitet wurden, werden sofort erneut verarbeitet. Diese Verarbeitung versucht, zusätzliche Archivelemente zu lokalisieren, die Symbolverweise auflösen. Dieses erneute Scannen des Archivs wird fortgesetzt, bis ein Durchgang über die Archivliste auftritt, in dem keine neuen Mitglieder extrahiert werden. Das vorherige Beispiel kann wie folgt vereinfacht werden.

$ cc -o prog .... -lA -lB -lC -z rescan-now 

Alternativ können die Optionen -z rescan-start und -z rescan-end verwendet werden, um voneinander abhängige Archive zu einer Archivgruppe zusammenzufassen. Diese Gruppen werden vom Linkeditor sofort erneut verarbeitet, wenn das schließende Trennzeichen in der Befehlszeile gefunden wird. Archive, die innerhalb der Gruppe gefunden werden, werden erneut verarbeitet, um zu versuchen, zusätzliche Archivmitglieder zu finden, die Symbolverweise auflösen. Dieses erneute Scannen des Archivs wird fortgesetzt, bis ein Durchgang über die Archivgruppe auftritt, in der keine neuen Mitglieder extrahiert werden. Unter Verwendung von Archivgruppen kann das vorherige Beispiel wie folgt geschrieben werden.

$ cc -o prog .... -z rescan-start -lA -lB -lC -z rescan-end

1418090cookie-checkWarum spielt die Reihenfolge der Option „-l“ in gcc eine Rolle? [duplicate]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy