Estoy tratando de compilar y ejecutar el siguiente programa sin main()
función en C
. He compilado mi programa usando el siguiente comando.
gcc -nostartfiles nomain.c
Y el compilador da una advertencia
/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000400340
Está bien, no hay problema. luego, he ejecutado el archivo ejecutable (a.out), ambas printf
declaraciones se imprimen correctamente y luego obtengo una falla de segmentación .
Entonces, mi pregunta es, ¿Por qué falla la segmentación después de ejecutar con éxito declaraciones de impresión?
mi código:
#include <stdio.h>
void nomain()
{
printf("Hello World...\n");
printf("Successfully run without main...\n");
}
salida:
Hello World...
Successfully run without main...
Segmentation fault (core dumped)
Nota:
Aquí, el -nostartfiles
indicador gcc evita que el compilador use archivos de inicio estándar al vincular
main
, peroWinMain
owWinMain
.ld
sería una-e
opción, para el enlazador MSVC de Windows sería una/ENTRY
opción.Respuestas:
Echemos un vistazo al ensamblaje generado de su programa:
.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
Tenga en cuenta la
ret
declaración. El punto de entrada de su programa está determinado a sernomain
, todo está bien con eso. Pero una vez que la función regresa, intenta saltar a una dirección en la pila de llamadas ... que no está poblada. Eso es un acceso ilegal y sigue una falla de segmentación.Una solución rápida sería llamar
exit()
al final de su programa (y asumiendo C11 también podríamos marcar la función como_Noreturn
):#include <stdio.h> #include <stdlib.h> _Noreturn void nomain(void) { printf("Hello World...\n"); printf("Successfully run without main...\n"); exit(0); }
De hecho, ahora su función se comporta como una
main
función normal , ya que después de regresar demain
, laexit
función se llama conmain
el valor de retorno.fuente
-nostartfiles
también puede inutilizar la biblioteca C. Sin el inicio de C ejecutado, las llamadas posteriores a las funciones de la biblioteca de C pueden fallar inesperadamente. En Linux, si tuviera que compilar-nostartupfiles
y-static
puede descubrir que el programa fallará. Hay bibliotecas de C como MUSL que no requieren inicialización inicial que están diseñadas para funcionar en este entorno.En C, cuando se llaman funciones / subrutinas, la pila se completa como (en el orden):
siendo main () el punto de inicio, ELF estructura el programa de tal manera que las instrucciones que vienen primero se presionan primero, en este caso printfs.
Ahora, el programa está algo truncado sin la dirección de retorno O
__end__
y, de hecho, asume que lo que esté en la pila en esa__end__
ubicación ( ) es la dirección de retorno, pero desafortunadamente no lo es y, por lo tanto, se bloquea.fuente