![Efecto de los enlaces estáticos y dinámicos en la dirección de inicio](https://rvso.com/image/31682/Efecto%20de%20los%20enlaces%20est%C3%A1ticos%20y%20din%C3%A1micos%20en%20la%20direcci%C3%B3n%20de%20inicio.png)
Tengo un programa C simple. Corro:
$ gcc Q1.c -Wall -save-temps -o Q1
Luego inspecciono el ejecutable generado:
$ objdump -f Q1
Q1: file format elf32-i386
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x080483b0
Luego lo compilo con enlaces estáticos:
$ gcc Q1.c -Wall -save-temps -static -o Q1
e inspeccionar el archivo nuevamente:
$ objdump -f Q1
Q1: file format elf32-i386
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x08048e08
¿Qué efecto tienen los enlaces estáticos y dinámicos en la dirección de inicio del programa? La dirección inicial es la dirección de main()
, ¿verdad?
Respuesta1
La dirección inicial es la dirección de main()
, ¿verdad?
Realmente no: el inicio de un programa no es realmente main()
... De forma predeterminada, GCC producirá ejecutables cuya dirección inicial corresponde al _start
símbolo. Puedes verlo haciendo un objdump --disassemble Q1
. Aquí está el resultado de un programa mío 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()
a su vez invoca __libc_start_main
, que inicializa lo necesario (pthreads, atexit,...) y finalmente llama main()
con los argumentos apropiados (argc, argv y env).
Bien, pero ¿qué tiene que ver con el cambio de dirección inicial?
Cuando pides gcc
vincular estáticamente, significa que toda la inicialización que mencioné anteriormente debe realizarse 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 inicial. 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.