Efeito da ligação estática e dinâmica no endereço inicial

8

Eu tenho um programa simples em C. Eu corro:

$ gcc Q1.c -Wall -save-temps -o Q1

Depois, 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

Depois 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

Qual 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?

    
por ArunMKumar 04.05.2013 / 14:58

1 resposta

10

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 símbolo _start . 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 no endereço 400e54 , _start() 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ê solicita gcc para vincular estaticamente, isso significa que toda a inicialização que eu mencionei acima deve ser feita usando funções que estão no executável. E, de fato, se você observar os tamanhos dos dois executáveis, verá que a versão estática é muito maior. No meu teste, a versão estática é 800K, enquanto a versão compartilhada é de apenas 6K.

As funções extras são colocadas antes de _start() , daí a mudança no endereço inicial. Aqui está o layout do executável estático em torno de 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, obtenho endereços iniciais ligeiramente diferentes: 0x400e30 no caso estático e 0x4003c0 no caso compartilhado.

    
por 04.05.2013 / 16:51