web-dev-qa-db-fra.com

Compiler et exécuter le programme sans main () en C

J'essaye de compiler et d'exécuter le programme suivant sans main() dans C. J'ai compilé mon programme en utilisant la commande suivante.

gcc -nostartfiles nomain.c

Et le compilateur donne un avertissement

/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000400340

Ok, pas de problème. Ensuite, j'ai exécuté le fichier exécutable (a.out). Les deux instructions printf sont imprimées avec succès, puis une erreur de segmentation .

Ma question est donc la suivante: Pourquoi une erreur de segmentation après une exécution réussie des instructions d’impression?

mon code:

#include <stdio.h>

void nomain()
{
        printf("Hello World...\n");
        printf("Successfully run without main...\n");
}

sortie:

Hello World...
Successfully run without main...
Segmentation fault (core dumped)

Remarque:

Ici, -nostartfiles gcc flag empêche le compilateur d’utiliser des fichiers de démarrage standard lors de la liaison

78
msc

Jetons un coup d'oeil au Assembly généré de votre programme:

.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

Notez l'instruction ret. Le point d’entrée de votre programme est déterminé comme étant nomain, tout va bien avec cela. Mais une fois que la fonction est revenue, elle tente de sauter dans une adresse de la pile d'appels ... qui n'est pas renseignée. C'est un accès illégal et une erreur de segmentation s'ensuit.

Une solution rapide consisterait à appeler exit() à la fin de votre programme (et en supposant que C11, nous pourrions aussi bien marquer la fonction comme _Noreturn):

#include <stdio.h>
#include <stdlib.h>

_Noreturn void nomain(void)
{
    printf("Hello World...\n");
    printf("Successfully run without main...\n");
    exit(0);
}

En fait, maintenant, votre fonction se comporte quasiment comme une fonction normale main, puisqu’après le retour de main, la fonction exit est appelée avec la valeur de retour de main .

129
StoryTeller

En C, lorsque des fonctions/sous-routines sont appelées, la pile est remplie comme suit (dans l’ordre):

  1. Les arguments,
  2. Adresse de retour,
  3. Variables locales, -> sommet de la pile

main () étant le point de départ, ELF structure le programme de manière à ce que les instructions qui arrivent en premier arrivent en premier, dans ce cas les printfs sont.

Maintenant, le programme est en quelque sorte tronqué sans adresse de retour OR __end__ et qu’il suppose que tout ce qui est sur la pile à ce moment-là (__end__) location est l'adresse de retour, mais malheureusement ce n'est pas le cas et par conséquent, il se bloque.

21
Milind Deore