![Efeito da vinculação estática e dinâmica no endereço inicial](https://rvso.com/image/31682/Efeito%20da%20vincula%C3%A7%C3%A3o%20est%C3%A1tica%20e%20din%C3%A2mica%20no%20endere%C3%A7o%20inicial.png)
Eu tenho um programa C simples. Eu corro:
$ gcc Q1.c -Wall -save-temps -o Q1
Então eu inspeciono o executável gerado:
$ objdump -f Q1
Q1: file format elf32-i386
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x080483b0
Então eu compilo com link estático:
$ gcc Q1.c -Wall -save-temps -static -o Q1
e inspecione o arquivo novamente:
$ objdump -f Q1
Q1: file format elf32-i386
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x08048e08
Que efeito a vinculação estática e dinâmica tem no endereço inicial do programa? O endereço inicial é o endereço de main()
, certo?
Responder1
O endereço inicial é o endereço de main()
, certo?
Na verdade não: o início de um programa não é realmente main()
. Por padrão, o GCC produzirá executáveis cujo endereço inicial corresponde ao _start
símbolo. Você pode ver isso fazendo um objdump --disassemble Q1
. Aqui está a saída de um programa simples meu que só faz return 0;
in 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 você pode ver em address 400e54
, _start()
por sua vez invoca __libc_start_main
, que inicializa o material necessário (pthreads, atexit,...) e finalmente chama main()
com os argumentos apropriados (argc, argv e env).
Ok, mas o que isso tem a ver com a mudança do endereço inicial?
Quando você pede gcc
para vincular estaticamente, significa que toda a inicialização que mencionei acima tem que ser feita utilizando funções que estão no executável. E, de fato, se você observar os tamanhos de ambos os executáveis, descobrirá que a versão estática é muito maior. No meu teste, a versão estática tem 800K, enquanto a versão compartilhada tem apenas 6K.
As funções extras são colocadas antes _start()
, daí a mudança no endereço inicial. Aqui está o layout do executável 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
E aqui está o layout do executável compartilhado:
00000000004003c0 T _start
00000000004003f0 t deregister_tm_clones
00000000004004b0 T main
00000000004004c0 T __libc_csu_init
00000000006008a0 B _end
0000000000400370 T _init
Como resultado, recebo endereços iniciais ligeiramente diferentes: 0x400e30 no caso estático e 0x4003c0 no caso compartilhado.