¿Cómo escribir un programa en C para imprimir «Hola mundo» sin la función main()?
Al principio, parece poco práctico ejecutar un programa sin una función main() porque la función main() es el punto de entrada de cualquier programa.
Primero comprendamos qué sucede bajo el capó mientras se ejecuta un programa C en el sistema Linux, cómo se llama a main() y cómo ejecutar un programa sin main().
Se considera la siguiente configuración para la demostración.
- Sistema operativo Ubuntu 16.4 LTS
- Compilador GCC 5.4.0
- utilidad objdump
Desde la perspectiva de la programación C/C++, el punto de entrada del programa es la función main(). Sin embargo, desde la perspectiva de la ejecución del programa, no lo es. Antes del punto en que el flujo de ejecución llega a main(), se realizan llamadas a algunas otras funciones, que configuran argumentos, preparan variables de entorno para la ejecución del programa, etc.
El archivo ejecutable creado después de compilar un código fuente C es un archivo de formato ejecutable y vinculable (ELF) .
Cada archivo ELF tiene un encabezado ELF donde hay un campo e_entry que contiene la dirección de memoria del programa desde donde comenzará la ejecución del ejecutable. Esta dirección de memoria apunta a la función _start() .
Después de cargar el programa, el cargador busca el campo e_entry del encabezado del archivo ELF. El formato ejecutable y vinculable (ELF) es un formato de archivo estándar común utilizado en el sistema UNIX para archivos ejecutables, código de objeto, bibliotecas compartidas y volcados de núcleo.
Veamos esto usando un ejemplo. Estoy creando un archivo example.c para demostrar esto.
int main() { return(0); }
Ahora compilando esto usando los siguientes comandos
gcc -o example example.c
Ahora se crea un ejecutable de ejemplo , examinemos esto usando la utilidad objdump
objdump -f example
Esto genera la siguiente información crítica del ejecutable en mi máquina. Eche un vistazo a la dirección de inicio a continuación, esta es la dirección que apunta a la función _start().
example: file format elf64-x86-64 architecture: i386:x86-64, flags 0x00000112: EXEC_P, HAS_SYMS, D_PAGED start address 0x00000000004003e0
Podemos verificar esta dirección desensamblando el ejecutable, la salida es larga, así que solo estoy pegando la salida que muestra dónde apunta esta dirección 0x00000000004003e0
objdump --disassemble example
Producción :
00000000004003e0 <_start>: 4003e0: 31 ed xor %ebp,%ebp 4003e2: 49 89 d1 mov %rdx,%r9 4003e5: 5e pop %rsi 4003e6: 48 89 e2 mov %rsp,%rdx 4003e9: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp 4003ed: 50 push %rax 4003ee: 54 push %rsp 4003ef: 49 c7 c0 60 05 40 00 mov $0x400560,%r8 4003f6: 48 c7 c1 f0 04 40 00 mov $0x4004f0,%rcx 4003fd: 48 c7 c7 d6 04 40 00 mov $0x4004d6,%rdi 400404: e8 b7 ff ff ff callq 4003c0 400409: f4 hlt 40040a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
Como podemos ver claramente, esto apunta a la función _start().
El rol de la función _start()
La función _start() prepara los argumentos de entrada para otra función _libc_start_main() que se llamará a continuación. Este es un prototipo de la función _libc_start_main() . Aquí podemos ver los argumentos que fueron preparados por la función _start().
int __libc_start_main(int (*main) (int, char * *, char * *), /* address of main function*/ int argc, /* number of command line args*/ char ** ubp_av, /* command line arg array*/ void (*init) (void), /* address of init function*/ void (*fini) (void), /* address of fini function*/ void (*rtld_fini) (void), /* address of dynamic linker fini function */ void (* stack_end) /* end of the stack address*/ );
El rol de la función _libc_start_main()
El rol de la función _libc_start_main() es el siguiente:
- Preparación de variables de entorno para la ejecución del programa.
- Llama a la función _init() que realiza la inicialización antes de que comience la función main().
- Registre las funciones _fini() y _rtld_fini() para realizar la limpieza después de que termine el programa
- Una vez completadas todas las acciones de requisitos previos, _libc_start_main() llama a la función main().
Programa de escritura sin main()
Ahora sabemos cómo se realiza la llamada a main(). Para que quede claro, main() no es más que un término acordado para el código de inicio. Podemos tener cualquier nombre para el código de inicio, no necesariamente tiene que ser «principal». Como la función _start() por defecto llama a main(), tenemos que cambiarla si queremos ejecutar nuestro código de inicio personalizado. Podemos anular la función _start() para que llame a nuestro código de inicio personalizado y no a main(). Veamos un ejemplo, guárdelo como nomain.c –
#include<stdio.h> #include<stdlib.h> void _start() { int x = my_fun(); //calling custom main function exit (x); } int my_fun() // our custom main function { printf ( "Hello world!\n" ); return 0; } |
Ahora tenemos que obligar al compilador a no usar su propia implementación de _start(). En GCC podemos hacer esto usando -nostartfiles
gcc -nostartfiles -o nomain nomain.c
Ejecutar el nomain ejecutable
./nomain
Producción:
Hello world!
Referencias
- http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html
- Compilación avanzada de C/C++ por Milan Stevanovic
Este artículo es una contribución de Atul Kumar . Si le gusta GeeksforGeeks y le gustaría contribuir, también puede escribir un artículo usando contribuya.geeksforgeeks.org o envíe su artículo por correo a contribuya@geeksforgeeks.org. Vea su artículo que aparece en la página principal de GeeksforGeeks y ayude a otros Geeks.
Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.
Publicación traducida automáticamente
Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA