Das direkte Linken eines C-Programms mit ld schlägt mit undefiniertem Verweis auf `__libc_csu_fini` fehl

Lesezeit: 6 Minuten

Ich versuche, ein C-Programm unter Linux zu kompilieren. Aus Neugier versuche ich jedoch, einige Schritte von Hand auszuführen: Ich verwende:

  • das gcc-Frontend, um Assembler-Code zu erzeugen
  • Führen Sie dann den GNU-Assembler aus, um eine Objektdatei zu erhalten
  • und verknüpfen Sie es dann mit der C-Laufzeit, um eine funktionierende ausführbare Datei zu erhalten.

Jetzt stecke ich mit dem Linking-Teil fest.

Das Programm ist ein sehr einfaches “Hallo Welt”:

#include <stdio.h>
int main() {
   printf("Hello\n");
   return 0;
}

Ich verwende den folgenden Befehl, um den Assemblercode zu erzeugen:

gcc hello.c -S -masm=intel

Ich sage gcc, dass es nach dem Kompilieren beenden und den Assembler-Code mit Intel-Syntax ausgeben soll.

Dann verwende ich den GNU-Assembler, um die Objektdatei zu erzeugen:

as -o hello.o hello.s

Dann versuche ich, ld zu verwenden, um die endgültige ausführbare Datei zu erstellen:

ld hello.o /usr/lib/libc.so /usr/lib/crt1.o -o hello

Aber ich bekomme immer wieder folgende Fehlermeldung:

/usr/lib/crt1.o: In function `_start':
(.text+0xc): undefined reference to `__libc_csu_fini'
/usr/lib/crt1.o: In function `_start':
(.text+0x11): undefined reference to `__libc_csu_init'

Die Symbole __libc_csu_fini/init scheinen ein Teil von glibc zu sein, aber ich kann sie nirgendwo finden! Ich habe versucht, statisch gegen libc zu linken (gegen /usr/lib/libc.a) mit dem gleichen Ergebnis.

Was könnte das Problem sein?

  • was zeigt objdump für crt1.o. Meins zeigt diese Symbole –

    – Jim Mcnamara

    11. Juli 2011 um 21:02 Uhr

  • Festlegen der Objektdateireihenfolge und Hinzufügen /usr/lib/crti.o zum Mix bringt mich am Linker vorbei, aber die resultierende ausführbare Datei wird nicht ausgeführt (bash: ./hello: No such file for directory) also fehlt noch was.

    – mu ist zu kurz

    11. Juli 2011 um 21:26 Uhr

  • Gleiches gilt für manuell in Assembly geschriebenen Code: stackoverflow.com/questions/3577922/…

    – Ciro Santilli Путлер Капут 六四事

    8. Juni 2015 um 10:49 Uhr

/usr/lib/libc.so ist ein Linker-Skript, das den Linker anweist, die gemeinsam genutzte Bibliothek einzubinden /lib/libc.so.6und ein nicht geteilter Teil, /usr/lib/libc_nonshared.a.

__libc_csu_init und __libc_csu_fini komme aus /usr/lib/libc_nonshared.a. Sie werden nicht gefunden, da Verweise auf Symbole in nicht gemeinsam genutzten Bibliotheken angezeigt werden müssen Vor das Archiv, das sie in der Linker-Zeile definiert. In Ihrem Fall, /usr/lib/crt1.o (die auf sie verweist) erscheint nach /usr/lib/libc.so (was sie hineinzieht), also funktioniert es nicht.

Das Korrigieren der Reihenfolge in der Linkzeile bringt Sie etwas weiter, aber dann bekommen Sie wahrscheinlich ein neues Problem, wo __libc_csu_init und __libc_csu_fini (die jetzt gefunden werden) nicht finden können _init und _fini. Um C-Bibliotheksfunktionen aufzurufen, sollten Sie auch verknüpfen /usr/lib/crti.o (nach crt1.o aber Vor die C-Bibliothek) und /usr/lib/crtn.o (nach die C-Bibliothek), die Initialisierungs- und Finalisierungscode enthalten.

Wenn Sie diese hinzufügen, sollten Sie eine erfolgreich verknüpfte ausführbare Datei erhalten. Es still funktioniert nicht, da es die dynamisch verknüpfte C-Bibliothek verwendet, ohne anzugeben, was der dynamische Linker ist. Sie müssen das auch dem Linker mitteilen, mit etwas wie -dynamic-linker /lib/ld-linux.so.2 (zumindest für 32-Bit x86; der Name des standardmäßigen dynamischen Linkers variiert je nach Plattform).

Wenn Sie das alles tun (im Wesentlichen gemäß Robs Antwort), erhalten Sie etwas, das in einfachen Fällen funktioniert. Bei komplexerem Code können Sie jedoch auf weitere Probleme stoßen, da GCC einige seiner eigenen Bibliotheksroutinen bereitstellt, die möglicherweise benötigt werden, wenn Ihr Code bestimmte Funktionen verwendet. Diese werden irgendwo tief in den GCC-Installationsverzeichnissen vergraben sein …

Sie können sehen, was gcc tut, indem Sie es entweder mit dem ausführen -v Option (die Ihnen die Befehle anzeigt, die es während der Ausführung aufruft) oder die -### Option (die nur die Befehle ausgibt, die sie ausführen würde, mit allen Argumenten in Anführungszeichen, aber tatsächlich nichts ausführt). Die Ausgabe wird verwirrend sein, es sei denn, Sie wissen, dass sie normalerweise aufgerufen wird ld indirekt über eine eigene Komponente, collect2 (der verwendet wird, um C++-Konstruktoraufrufe an der richtigen Stelle einzufügen).

  • Brillant. Klar erklärt, prägnant und auf das Wesentliche reduziert. Ich bin durch den Dschungel von ld navigiert, habe eine direkt verlinkte ausführbare Datei von Solaris nach Linux portiert und bin hier gelandet, weil ich darauf beschränkt war, die Reihenfolge der Objekte in der Linker-Zeile zu tauschen, als ich versuchte, einen „Permission denied“-Fehler zu beheben beim Ausführen der gerade verlinkten ausführbaren Datei. Für alle anderen, die nach dem Kompilieren oder Verknüpfen einer ausführbaren Datei auf „Berechtigung verweigert“ stoßen, gibt das Ticket möglicherweise den dynamischen Linker an, wie Matthew erklärt. Es war für mich.

    – Petrus

    11. Juni 2013 um 16:55 Uhr

Benutzer-Avatar
Robᵩ

Ich habe einen anderen Beitrag gefunden, der einen Hinweis enthielt: -dynamic-linker /lib/ld-linux.so.2.

Versuche dies:

$ gcc hello.c -S -masm=intel
$ as -o hello.o hello.s
$ ld -o hello -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o hello.o -lc /usr/lib/crtn.o
$ ./hello
hello, world
$ 

Angenommen, ein normaler Aufruf von gcc -o hello hello.c einen funktionierenden Build erzeugt, führen Sie diesen Befehl aus:

gcc --verbose -o hello hello.c

und gcc wird Ihnen sagen, wie es Dinge verbindet. Das sollte Ihnen eine gute Vorstellung davon geben, was Sie möglicherweise in Ihrem Linkschritt berücksichtigen müssen.

Benutzer-Avatar
Ciro Santilli Путлер Капут 六四事

In Ubuntu 14.04 (GCC 4.8) lautet der minimale Verknüpfungsbefehl:

ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 \
  /usr/lib/x86_64-linux-gnu/crt1.o \
  /usr/lib/x86_64-linux-gnu/crti.o \
  -L/usr/lib/gcc/x86_64-linux-gnu/4.8/ \
  -lc -lgcc -lgcc_s \
  hello.o \
  /usr/lib/x86_64-linux-gnu/crtn.o

Obwohl sie möglicherweise nicht erforderlich sind, sollten Sie sie auch verlinken -lgcc und -lgcc_sda GCC möglicherweise Aufrufe an Funktionen ausgibt, die in diesen Bibliotheken für Operationen vorhanden sind, die Ihre Hardware nicht nativ implementiert, z long long int Operationen auf 32-Bit. Siehe auch: Brauche ich wirklich libgcc?

Ich musste hinzufügen:

  -L/usr/lib/gcc/x86_64-linux-gnu/4.8/ \

da das standardmäßige Linker-Skript dieses Verzeichnis nicht enthält, und dort libgcc.a war lokalisiert.

Wie von Michael Burr erwähnt, finden Sie die Pfade mit gcc -v. Genauer gesagt braucht man:

gcc -v hello_world.c |& grep 'collect2' | tr ' ' '\n'

Benutzer-Avatar
Detys

So habe ich es unter Ubuntu 11.10 behoben:

apt-get remove libc-dev

Sagen Sie Ja, um alle Pakete zu entfernen, aber kopieren Sie die Liste, um sie danach neu zu installieren.

apt-get install libc-dev

Benutzer-Avatar
mwk

Wenn Sie ein 64-Bit-Betriebssystem ausführen, ist Ihre glibc(-devel) möglicherweise beschädigt. Durch Anschauen Dies und Dies Sie können diese 3 möglichen Lösungen finden:

  1. lib64 zu LD_LIBRARY_PATH hinzufügen
  2. Verwenden Sie lc_noshared
  3. glibc-devel neu installieren

Benutzer-Avatar
Vinicius Kamakura

Da Sie den Verknüpfungsprozess von Hand durchführen, vergessen Sie, den C-Laufzeitinitialisierer oder wie auch immer er heißt zu verknüpfen.

Um nicht auf die Einzelheiten einzugehen, wo und was Sie für Ihre Plattform verlinken sollten, verwenden Sie nach Erhalt Ihrer Intel-ASM-Datei gcc, um Ihre ausführbare Datei zu generieren (kompilieren und verknüpfen).

einfach tun gcc hello.c -o hello sollte arbeiten.

1257180cookie-checkDas direkte Linken eines C-Programms mit ld schlägt mit undefiniertem Verweis auf `__libc_csu_fini` fehl

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

Privacy policy