kiddie
.:: @OREL ::.

Dans le bon temps, les binaires étaient liés statiquement et il faut bien dire 
que cela devait occuper quantités de mémoires ! Voyez plutôt : 

voidboy@voidsys:~/fun/assembly$ gcc -o static -w -static foo.c voidboy@voidsys:~/fun/assembly$ gcc -o nostatic -w foo.c voidboy@voidsys:~/fun/assembly$ du -h static nostatic 820K static 20K nostatic
Bon ok, avec vos 42GB de RAM et vos SSD PcI Express double turbos 8 soupapes de 1TB, 800KB, on aurait tendance à dire que c'est négligeable mais ça ne semblait pas être le cas à l'époque. Pour éviter d'injecter toutes les libraires dans le binaire et se retrouver avec X libc sur l'hôte, on peut faire appel aux librairies partagées(.so) et à l'édition de liens dynamiques. Sans l'édition de liens statique : Avec l'édition de liens dynamique : +-------------+ +-------------+ | a.out | +----> a.out <----+ +------+------+ | +-------------+ | | libc | libd | +----> b.out <----+ +------+------+ | +-------------+ | | | +--->+ c.out <----+ +-------------+ | +-------------+ | | b.out | | | | | +------+------+ | | | | | libc | libd | | +-------------+ | +------+------+ +----+ libc | | | | +-------------+ | +-------------+ | | | | c.out | +-------------+ | +------+------+ | libd +----+ | libc | libd | +-------------+ +------+------+ +-------------+ On peut grossièrement résumer la chose en disant que le système d'exploitation va charger une instance de la librairie en mémoire et la mapper virtuellement (via la mémoire virtuelle, le MMU..) dans chaque processus qui fera appelle à celle-ci. Ainsi, chaque processus aura "l'illusion" d'avoir sa propre instance de la librai- rie en mémoire alors qu'après translation, les adresses des librairies de chaque processus pointeront sur la même instance de la librairie. Avec cette méthode, on réduit considérablement l'empreinte mémoire générée par les multiples redondances des différentes librairies. Attention cepandant, tout n'est pas noir pour autant dans l'édition de liens statique, elle peut notamment être judicieuse s'il on souhaite maximiser la portabilité d'un programme. Par exemple en y injectant une version spécifique d'une librairie, cela permet de ne pas dépendre de celle de l'hôte qui sera potentiellement incompatible. De plus, il se pose la problèmatique suivante avec les liens dynamiques : Comment est-ce que mon programme récupère les adresses des fonctions externes sachant qu'elles sont mappées à l'exécution ? C'est le rôle de l'éditeur de liens qui va satisfaire les réadressages présent dans la section .rel.plt avec l'aide de la .plt pour Program Linkage Table et de la .GOT pour Global Offset Table. Par exemple, si on compile le programme suivant et qu'on affiche sa section .reloc :
voidboy@voidsys:~/fun/assembly$ cat foo.c void main(void) { puts("C magnifique"); } voidboy@voidsys:~/fun/assembly$ gcc -m32 -fno-pic -no-pie -w foo.c voidboy@voidsys:~/fun/assembly$ readelf -r a.out [...] Section de réadressage '.rel.plt' à l'adresse de décalage 0x2d8 contient 2 entrées: Décalage Info Type Val.-sym Noms-symboles 0804c00c 00000107 R_386_JUMP_SLOT 00000000 puts@GLIBC_2.0 0804c010 00000307 R_386_JUMP_SLOT 00000000 __libc_start_main@GLIBC_2.0
On a une entrée pour putc de type R_386_JUMP_SLOT, vous trouverez la définition de tous les types dans les sources de binutils(/elfcpp/i386.h), nous concernant notre type est accompagné du commentaire suivant :
R_386_NONE = 0, // No reloc R_386_32 = 1, // Direct 32 bit zero extended R_386_PC32 = 2, // PC relative 32 bit signed R_386_GOT32 = 3, // 32 bit GOT entry R_386_PLT32 = 4, // 32 bit PLT address R_386_COPY = 5, // Copy symbol at runtime R_386_GLOB_DAT = 6, // Create GOT entry R_386_JUMP_SLOT = 7, // Create PLT entry R_386_RELATIVE = 8, // Adjust by program base R_386_GOTOFF = 9, // 32-bit GOT offset R_386_GOTPC = 10, // 32-bit PC relative offset to GOT
La .got est une section permettant de stocker les adresses des fonctions, c'est une sorte de tableau de pointeurs accessible en écriture. Ci-dessous la .got de notre exemple :
voidboy@voidsys:~/fun/assembly$ objdump -j .got.plt -s a.out a.out: format de fichier elf32-i386 Contenu de la section .got.plt : 804c000 14bf0408 00000000 00000000 36900408 ............6... 804c010 46900408 F...
Celle-ci contient 5 entrées(stockées en little-endian), les 3 premières sont : .got[0] => adresse du segment dynamique(.dyn) de l'exécutable .got[1] => adresse de la structure link_map .got[2] => adresse de le fonction de résolution de symboles .got[1] et .got[2] se verront attribuer une valeur à l'exécution du programme. Les 2 valeurs restantes, .got[3] et got[4] devraient pointer sur les adresses des fonctions puts et main respectivement, or, elles pointent toutes deux sur la même section du binaire, la .plt :
voidboy@voidsys:~/fun/assembly$ objdump -j .plt -d a.out a.out: format de fichier elf32-i386 Déassemblage de la section .plt : 08049020 <.plt>: 8049020: ff 35 04 c0 04 08 pushl 0x804c004 8049026: ff 25 08 c0 04 08 jmp *0x804c008 804902c: 00 00 add %al,(%eax) ... 08049030 <puts@plt>: 8049030: ff 25 0c c0 04 08 jmp *0x804c00c 8049036: 68 00 00 00 00 push $0x0 804903b: e9 e0 ff ff ff jmp 8049020 <.plt> 08049040 <__libc_start_main@plt>: 8049040: ff 25 10 c0 04 08 jmp *0x804c010 8049046: 68 08 00 00 00 push $0x8 804904b: e9 d0 ff ff ff jmp 8049020 <.plt>
C'est elle qui va nous permettre de faire le lien avec la .got et ainsi réaliser la résolution des symboles. Au premier appel de puts@plt, un jmp sur l'adresse stockée dans la .got @ 0x804c00c va être effectué, on va donc jmp sur 0x8049036 qui correspond à l'instruction push 0x0, 0x0 étant l'index de puts dans la .got. Après ce push, on jmp @ 0x8049020, push .got[1] et jmp @ .got[2], l'adresse de fonction de résolution, si tout se passe bien, l'éditeur de lien devrait inscrire l'adresse de puts dans .got[3]. Démonstration avec gdb :
voidboy@voidsys:~/fun/assembly$ gdb -q a.out Reading symbols from a.out... (No debugging symbols found in a.out) (gdb) disas main Dump of assembler code for function main: 0x08049162 <+0>: lea 0x4(%esp),%ecx 0x08049166 <+4>: and $0xfffffff0,%esp 0x08049169 <+7>: pushl -0x4(%ecx) 0x0804916c <+10>: push %ebp 0x0804916d <+11>: mov %esp,%ebp 0x0804916f <+13>: push %ecx 0x08049170 <+14>: sub $0x4,%esp 0x08049173 <+17>: sub $0xc,%esp 0x08049176 <+20>: push $0x804a008 0x0804917b <+25>: call 0x8049030 <puts@plt> 0x08049180 <+30>: add $0x10,%esp 0x08049183 <+33>: nop 0x08049184 <+34>: mov -0x4(%ebp),%ecx 0x08049187 <+37>: leave 0x08049188 <+38>: lea -0x4(%ecx),%esp 0x0804918b <+41>: ret End of assembler dump. (gdb) b *main+25 Breakpoint 1 at 0x804917b (gdb) r Starting program: /home/voidboy/fun/assembly/a.out Breakpoint 1, 0x0804917b in main () (gdb) si 0x08049030 in puts@plt () (gdb) x/3i $eip => 0x8049030 <puts@plt>: jmp *0x804c00c 0x8049036 <puts@plt+6>: push $0x0 0x804903b <puts@plt+11>: jmp 0x8049020 (gdb) maintenance info section Exec file: `/home/voidboy/fun/assembly/a.out', file type elf32-i386. [...] [21] 0x804c000->0x804c014 at 0x00003000: .got.plt ALLOC LOAD DATA HAS_CONTENTS [...] (gdb) x/5wx 0x804c000 0x804c000: 0x0804bf14 0xf7ffd950 0xf7fe9700 0x08049036 0x804c010 <__libc_start_main@got.plt>: 0xf7df9660 (gdb) si 0x08049036 in puts@plt () (gdb) 0x0804903b in puts@plt () (gdb) 0x08049020 in ?? () (gdb) 0x08049026 in ?? () (gdb) 0xf7fe9700 in ?? () from /lib/ld-linux.so.2 (gdb) finish Run till exit from #0 0xf7fe9700 in ?? () from /lib/ld-linux.so.2 C magnifique 0x08049180 in main () (gdb) x/5wx 0x804c000 0x804c000: 0x0804bf14 0xf7ffd950 0xf7fe9700 0xf7e47470 0x804c010 <__libc_start_main@got.plt>: 0xf7df9660 (gdb) x/i 0xf7e47470 0xf7e47470 <puts>: push %ebp
On notera que sous Linux, la méthode de résolution de liens par défaut est le "lazy linking" la résolution ne sera faite qu'au premier appel de la fonction contrairement à Windows, où tous les symboles sont résolus à l'initialisation du programme.

Tout est faux tout est conforme.