La dirección de inicio es la dirección de main()
, ¿verdad?
En realidad no: el inicio de un programa no es realmente main()
. Por defecto, GCC producirá ejecutables cuya dirección de inicio corresponde al _start
símbolo. Puedes ver eso haciendo un objdump --disassemble Q1
. Aquí está la salida de un programa simple que solo funciona return 0;
en main()
:
0000000000400e30 <_start>:
400e30: 31 ed xor %ebp,%ebp
400e32: 49 89 d1 mov %rdx,%r9
400e35: 5e pop %rsi
400e36: 48 89 e2 mov %rsp,%rdx
400e39: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
400e3d: 50 push %rax
400e3e: 54 push %rsp
400e3f: 49 c7 c0 a0 15 40 00 mov $0x4015a0,%r8
400e46: 48 c7 c1 10 15 40 00 mov $0x401510,%rcx
400e4d: 48 c7 c7 40 0f 40 00 mov $0x400f40,%rdi
400e54: e8 f7 00 00 00 callq 400f50 <__libc_start_main>
400e59: f4 hlt
400e5a: 66 90 xchg %ax,%ax
400e5c: 0f 1f 40 00 nopl 0x0(%rax)
Como puede ver en la dirección 400e54
, _start()
invoca a su vez __libc_start_main
, que inicializa las cosas necesarias (pthreads, atexit, ...) y finalmente llama main()
con los argumentos apropiados (argc, argv y env).
De acuerdo, pero ¿qué tiene que ver con el cambio de dirección de inicio?
Cuando solicita gcc
un enlace estático, significa que toda la inicialización que mencioné anteriormente debe hacerse utilizando funciones que están en el ejecutable. Y, de hecho, si observa los tamaños de ambos ejecutables, encontrará que la versión estática es mucho más grande. En mi prueba, la versión estática es de 800K, mientras que la versión compartida es de solo 6K.
Las funciones adicionales se colocan antes _start()
, de ahí el cambio en la dirección de inicio. Aquí está el diseño del ejecutable estático start()
:
000000000049e960 r translit_from_tbl
0000000000400a76 t _i18n_number_rewrite
0000000000400bc0 t fini
0000000000400bd0 t init_cacheinfo
0000000000400e30 T _start
0000000000400e60 t deregister_tm_clones
0000000000400e90 t register_tm_clones
0000000000400ed0 t __do_global_dtors_aux
Y aquí está el diseño del ejecutable compartido:
00000000004003c0 T _start
00000000004003f0 t deregister_tm_clones
00000000004004b0 T main
00000000004004c0 T __libc_csu_init
00000000006008a0 B _end
0000000000400370 T _init
Como resultado, obtengo direcciones de inicio ligeramente diferentes: 0x400e30 en el caso estático y 0x4003c0 en el caso compartido.