Effet de la liaison statique et dynamique sur l'adresse de début

8

J'ai un programme C simple. Je cours:

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

J'inspecte ensuite l'exécutable généré:

$  objdump -f Q1
Q1:     file format elf32-i386
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x080483b0

Ensuite, je le compile avec une liaison statique:

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

et inspectez à nouveau le fichier:

$ objdump -f Q1
Q1:     file format elf32-i386
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x08048e08

Quel effet la liaison statique et dynamique a-t-elle sur l'adresse de début du programme? L'adresse de départ est l'adresse de main(), non?

ArunMKumar
la source

Réponses:

10

L'adresse de départ est l'adresse de main(), non?

Pas vraiment: le début d'un programme ne l'est pas vraiment main(). Par défaut, GCC produira des exécutables dont l'adresse de début correspond au _startsymbole. Vous pouvez le voir en faisant un objdump --disassemble Q1. Voici le résultat sur un programme simple à moi qui ne fait que return 0;dans 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)

Comme vous pouvez le voir à l'adresse 400e54, _start()invoque à son tour __libc_start_main, ce qui initialise les éléments nécessaires (pthreads, atexit, ...) et appelle finalement main()avec les arguments appropriés (argc, argv et env).

D'accord, mais qu'est-ce que cela a à voir avec le changement d'adresse de départ?

Lorsque vous demandez un gcclien statique, cela signifie que toute l'initialisation que j'ai mentionnée ci-dessus doit être effectuée à l'aide de fonctions qui sont dans l'exécutable. Et en effet, si vous regardez la taille des deux exécutables, vous constaterez que la version statique est beaucoup plus grande. Sur mon test, la version statique est 800K tandis que la version partagée n'est que 6K.

Les fonctions supplémentaires se trouvent être placées avant _start(), d'où le changement d'adresse de départ. Voici la disposition de l'exécutable statique autour 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

Et voici la disposition de l'exécutable partagé:

00000000004003c0 T _start
00000000004003f0 t deregister_tm_clones
00000000004004b0 T main
00000000004004c0 T __libc_csu_init
00000000006008a0 B _end
0000000000400370 T _init

Par conséquent, j'obtiens des adresses de départ légèrement différentes: 0x400e30 dans le cas statique et 0x4003c0 dans le cas partagé.

Frederik Deweerdt
la source