Ich versuche folgendes Programm ohne main()
Funktion in zu kompilieren und auszuführen C
. Ich habe mein Programm mit dem folgenden Befehl kompiliert.
gcc -nostartfiles nomain.c
Und der Compiler warnt
/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000400340
OK, kein Problem. Dann habe ich eine ausführbare Datei (a.out) ausgeführt, beide printf
Anweisungen wurden erfolgreich gedruckt und erhalten dann einen Segmentierungsfehler .
Meine Frage lautet also: Warum Segmentierungsfehler nach erfolgreicher Ausführung von Druckanweisungen?
Mein Code:
#include <stdio.h>
void nomain()
{
printf("Hello World...\n");
printf("Successfully run without main...\n");
}
Ausgabe:
Hello World...
Successfully run without main...
Segmentation fault (core dumped)
Hinweis:
Hier -nostartfiles
verhindert das gcc-Flag, dass der Compiler beim Verknüpfen Standardstartdateien verwendet
main
, aberWinMain
oderwWinMain
.ld
wäre dies eine-e
Option, für den MSVC-Linker von Windows eine/ENTRY
Option.Antworten:
Werfen wir einen Blick auf die generierte Assembly Ihres Programms:
.LC0: .string "Hello World..." .LC1: .string "Successfully run without main..." nomain: push rbp mov rbp, rsp mov edi, OFFSET FLAT:.LC0 call puts mov edi, OFFSET FLAT:.LC1 call puts nop pop rbp ret
Beachten Sie die
ret
Aussage. Der Einstiegspunkt Ihres Programms ist bestimmt, damitnomain
ist alles in Ordnung. Sobald die Funktion zurückkehrt, versucht sie, in eine Adresse auf dem Aufrufstapel zu springen, die nicht ausgefüllt ist. Das ist ein illegaler Zugriff und es folgt ein Segmentierungsfehler.Eine schnelle Lösung wäre,
exit()
am Ende Ihres Programms aufzurufen (und unter der Annahme von C11 können wir die Funktion genauso gut als markieren_Noreturn
):#include <stdio.h> #include <stdlib.h> _Noreturn void nomain(void) { printf("Hello World...\n"); printf("Successfully run without main...\n"); exit(0); }
Tatsächlich verhält sich Ihre Funktion jetzt ziemlich ähnlich wie eine reguläre
main
Funktion, damain
dieexit
Funktion nach der Rückkehr von mitmain
dem Rückgabewert aufgerufen wird.quelle
-nostartfiles
kann auch die C-Bibliothek unbrauchbar machen. Ohne den C- Start können nachfolgende Aufrufe der C- Bibliotheksfunktionen unerwartet fehlschlagen. Wenn Sie unter Linux kompilieren-nostartupfiles
und-static
feststellen, dass das Programm fehlerhaft ist. Es gibt C- Bibliotheken wie MUSL, für die keine Vorabinitialisierung erforderlich ist und die für diese Umgebung ausgelegt sind.Wenn in C Funktionen / Unterprogramme aufgerufen werden, wird der Stapel wie folgt gefüllt (in der Reihenfolge):
Als Startpunkt main () strukturiert ELF das Programm so, dass alle Anweisungen, die zuerst kommen, zuerst gepusht werden, in diesem Fall printfs.
Jetzt wird das Programm ohne Rücksprungadresse ODER abgeschnitten,
__end__
und tatsächlich wird davon ausgegangen, dass alles, was sich an diesem (__end__
) Speicherort auf dem Stapel befindet, die Rücksprungadresse ist, aber leider nicht und daher stürzt es ab.quelle