Programm ohne main() in C kompilieren und ausführen

Lesezeit: 4 Minuten

Benutzeravatar von msc
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 - Benutzeravatar von Unslander Monica
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

Benutzeravatar von Milind Deore
Milind Deore

Wenn Funktionen/Subroutinen in C aufgerufen werden, wird der Stack wie folgt gefüllt (in der Reihenfolge):

  1. Die Argumente,
  2. Absender,
  3. 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

1418110cookie-checkProgramm ohne main() in C kompilieren und ausführen

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

Privacy policy