Efecto de la vinculación estática y dinámica en la dirección de inicio

8

Tengo un programa simple de C. 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 inspeccione 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 tiene la vinculación estática y dinámica en la dirección de inicio del programa? La dirección de inicio es la dirección de main(), ¿verdad?

ArunMKumar
fuente

Respuestas:

10

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 _startsí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 gccun 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.

Frederik Deweerdt
fuente