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
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
.
En C, lorsque des fonctions/sous-routines sont appelées, la pile est remplie comme suit (dans l’ordre):
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.