¿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 un principal() función porque el principal() La función es el punto de entrada de cualquier programa.

Primero entendamos qué sucede bajo el capó mientras se ejecuta un programa C en un sistema Linux, cómo se llama a main () y cómo ejecutar un programa sin main ().

La siguiente configuración se considera 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 al 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 en C es un Archivo de formato ejecutable y enlazable (ELF).
Cada archivo ELF tiene un encabezado ELF donde hay un e_entry campo que contiene la dirección de memoria del programa desde el cual se iniciará la ejecución del ejecutable. Esta dirección de memoria apunta al _comienzo() función.
Después de cargar el programa, el cargador busca el e_entry campo del encabezado del archivo ELF. Formato ejecutable y enlazable (ELF) es un formato de archivo estándar común utilizado en el sistema UNIX para archivos ejecutables, código objeto, bibliotecas compartidas y volcados de memoria.

Veamos esto usando un ejemplo. Estoy creando un ejemplo.c archivo para demostrar esto.

int main()

{

   return(0);

}

/ div>

Ahora compilando esto usando los siguientes comandos

gcc -o example example.c

Ahora un ejemplo se crea el ejecutable, 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 está esta dirección 0x00000000004003e0 está apuntando

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 _libc_start_main () función. Aquí podemos ver los argumentos que fueron preparados por la función _start ().

int __libc_start_main(int (*main) (int, char * *, char * *),

int argc,

char ** ubp_av,

void (*init) (void),

void (*fini) (void),

void (*rtld_fini) (void),

void (* stack_end)

);

El rol de la función _libc_start_main ()

El rol de la función _libs_start_main () es el siguiente:

  • Preparación de variables de entorno para la ejecución del programa
  • Llamadas _en eso() función que realiza la inicialización antes de que se inicie la función main ().
  • Registrarse _fini () y _rtld_fini () funciones para realizar la limpieza después de que finaliza 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, no a main (). Tengamos un ejemplo, guárdelo como nomain.c

      #include<stdio.h>

      #include<stdlib.h>

      void _start()

      {

          int x = my_fun();

          exit(x);

      }

       

      int my_fun()

      {

          printf("Hello world!
      "
      );

          return 0;

      }

      Ahora tenemos que forzar al compilador a no usar su propia implementación de _start (). En GCC podemos hacer esto usando -nostartfiles

gcc -nostartfiles -o nomain nomain.c

Ejecute el nomain ejecutable

./nomain

Producción:

Hello world!

Referencias

Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema discutido anteriormente.


También podría gustarte

Suscríbete a nuestro boletín

Usamos cookies para brindar y mejorar nuestros servicios. Al utilizar nuestro sitio, acepta nuestra Política de cookies.


Aprende más

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *