Programm ohne main() in C kompilieren und ausführen
Lesezeit: 4 Minuten
msc
Ich versuche, folgendes Programm ohne zu kompilieren und auszuführen main() Funktion ein 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 die ausführbare Datei (a.out) ausgeführt, beides printf Anweisungen werden erfolgreich gedruckt und dann abgerufen Segmentierungsfehler.
Meine Frage ist 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");
}
Ausgang:
Hello World...
Successfully run without main...
Segmentation fault (core dumped)
Notiz:
Hier, -nostartfiles Das gcc-Flag verhindert, dass der Compiler beim Linken Standard-Startdateien verwendet
Ich bin überrascht, dass das überhaupt funktioniert. Ehrlich gesagt halte ich diese Behandlung durch den Linker für falsch (oder zumindest für eine schlechte Sache): Es gab keinen Einstiegspunkt, also halluzinierte der Linker es einfach von irgendeiner Funktion, die praktisch war. Blech.
– imallett
20. Februar 2017 um 7:55 Uhr
@imallett, zumindest war der Linker so freundlich, mit einer Warnung darauf aufmerksam zu machen und zu erklären, welche Fallback-Aktion er ergriffen hat! Sie haben Recht, dass dies jedoch besser als Fehler und nicht nur als Warnung sein könnte.
– Toby Speight
20. Februar 2017 um 11:59 Uhr
Warum würdest du keine Hauptleitung verwenden?
– Peter B
20. Februar 2017 um 12:12 Uhr
@PieterB – Für eine Diskussion über Unices nicht übermäßig relevant, aber der Einstiegspunkt für Windows-Programme ist nicht unbedingt erforderlich mainaber WinMain oder wWinMain.
– StoryTeller – Unslander Monica
20. Februar 2017 um 12:19 Uhr
@StoryTeller Tatsächlich können Sie sowohl unter Windows als auch unter Linux einen beliebigen Einstiegspunkt festlegen: für Linux ld es wäre -e Option, für den MSVC-Linker von Windows wäre es /ENTRY Möglichkeit.
– Ruslan
27. September 2017 um 9:27 Uhr
StoryTeller – Unslander Monica
Werfen wir einen Blick auf die generierten Montage 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 das ret Aussage. Der Einstiegspunkt Ihres Programms ist bestimmt nomain, damit ist alles in Ordnung. Aber sobald die Funktion zurückkehrt, versucht sie, in eine Adresse auf dem Aufrufstapel zu springen … die nicht besetzt ist. Das ist ein illegaler Zugriff und ein Segmentierungsfehler folgt.
Eine schnelle Lösung wäre ein Anruf exit() am Ende Ihres Programms (und unter der Annahme von C11 könnten wir die Funktion genauso gut markieren als _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 genau wie eine normale main Funktion, da nach der Rückkehr von maindas exit Funktion wird mit aufgerufen mainDer Rückgabewert von .
Ich denke, es gibt einige Kombinationen aus Architektur und Betriebssystem, bei denen Sie einfach aus einem Programm “zurückkehren” können. Ausführbare MS-DOS .COM-Dateien? Wie auch immer, wir beschäftigen uns intensiv mit implementierungsspezifischem Verhalten.
– pjc50
20. Februar 2017 um 10:08 Uhr
@pjc50 – Das sind wir in der Tat. Obwohl der Pfad im OP eine Unix-Variante nahelegte. In Verbindung mit der Popularität bestimmter Architekturen und Befehlssätze war dies der einzige Grund, warum ich mich wohl fühlte, generierte Assembly in der Antwort zu präsentieren.
– StoryTeller – Unslander Monica
20. Februar 2017 um 10:14 Uhr
Nur eine Beobachtung. -nostartfiles kann auch die C-Bibliothek unbrauchbar machen. Ohne das C Startup ausgeführt nachfolgende Aufrufe an die C Bibliotheksfunktionen können unerwartet fehlschlagen. Unter Linux würde man mit kompilieren -nostartupfiles und -static Sie können feststellen, dass das Programm fehlerhaft ist. Es gibt C Bibliotheken wie MUSL, die keine Vorabinitialisierung erfordern, die für die Arbeit in dieser Umgebung ausgelegt sind.
– Michael Petsch
3. Januar 2018 um 9:35 Uhr
Milind Deore
Wenn Funktionen/Subroutinen in C aufgerufen werden, wird der Stack wie folgt gefüllt (in der Reihenfolge):
Die Argumente,
Absender,
Lokale Variablen, –> oben auf dem Stapel
Da main() der Startpunkt ist, strukturiert ELF das Programm so, dass alle Anweisungen, die zuerst kommen, zuerst gepusht werden, in diesem Fall sind es printfs.
Jetzt wird das Programm ohne Rücksprungadresse ODER abgeschnitten __end__ und tatsächlich geht es davon aus, dass das, was auch immer auf dem Stapel ist (__end__) location ist die Absenderadresse, aber leider nicht und stürzt daher ab.
Ist die Reihenfolge der Stapeldaten durch den C-Standard definiert? Ich dachte, es liegt an der Systemarchitektur
– Délisson Junio
19. Februar 2017 um 18:10 Uhr
Deshalb habe ich ELF (ausführbares und verknüpfbares Dateiformat) erwähnt, das durch Cross-Compiling für einen bestimmten ARCH-Typ auf dem erforderlichen Betriebssystem generiert wird.
– Milind Deore
19. Februar 2017 um 18:25 Uhr
Um wählerisch zu sein, können Sie das ELF-Format sogar auf Systemen ohne Stack verwenden. Ein Beispiel für ein solches System ist Freescale RS08 mit dem Codewarrior-Compiler, der ELF-Linker-Dateien generiert.
– Ludin
11. Oktober 2017 um 12:47 Uhr
14181100cookie-checkProgramm ohne main() in C kompilieren und ausführenyes
Ich bin überrascht, dass das überhaupt funktioniert. Ehrlich gesagt halte ich diese Behandlung durch den Linker für falsch (oder zumindest für eine schlechte Sache): Es gab keinen Einstiegspunkt, also halluzinierte der Linker es einfach von irgendeiner Funktion, die praktisch war. Blech.
– imallett
20. Februar 2017 um 7:55 Uhr
@imallett, zumindest war der Linker so freundlich, mit einer Warnung darauf aufmerksam zu machen und zu erklären, welche Fallback-Aktion er ergriffen hat! Sie haben Recht, dass dies jedoch besser als Fehler und nicht nur als Warnung sein könnte.
– Toby Speight
20. Februar 2017 um 11:59 Uhr
Warum würdest du keine Hauptleitung verwenden?
– Peter B
20. Februar 2017 um 12:12 Uhr
@PieterB – Für eine Diskussion über Unices nicht übermäßig relevant, aber der Einstiegspunkt für Windows-Programme ist nicht unbedingt erforderlich
main
aberWinMain
oderwWinMain
.– StoryTeller – Unslander Monica
20. Februar 2017 um 12:19 Uhr
@StoryTeller Tatsächlich können Sie sowohl unter Windows als auch unter Linux einen beliebigen Einstiegspunkt festlegen: für Linux
ld
es wäre-e
Option, für den MSVC-Linker von Windows wäre es/ENTRY
Möglichkeit.– Ruslan
27. September 2017 um 9:27 Uhr