Dans le TP précèdent, nous avons vu que les deux programmes hello1 et hello2 faisaient la même chose et pourtant avaient des tailles bien différentes. Dans ce TP nous allons voir pourquoi.
1h30
Dans ce TP nous allons voir pourquoi deux programmes faisant la même chose ont des tailles bien différentes. Nous allons voir en quoi consiste une compilation dynamique et une compilation statique.
Remarque : Il est possible que les messages d'erreur ne soient pas exactement les mêmes. En effet ce TP a été monté il y a quelques années et les bibliothèques des programmes écrits en langage C ont évolué. Alors soyez réactifs...
Lancer la commande :
file hello1
Vous devez obtenir le message suivant : (ou presque...)
hello1: ELF 32-bit LSB exécutable, Intel 80386, version 1, dynamically linked, not stripped
Lancer la commande :
$file hello2
Vous devez obtenir le message suivant : (ou presque...)
hello2: ELF 32-bit LSB exécutable, Intel 80386, version 1, statically linked, not stripped
Explications :
Pour le fichier hello1, lors de la compilation, un petit fragment de code qui permet l’exécution de ld.so
(éditeur de liens dynamiques, d'où le terme dynamically linked) y est ajouté.
Pour le fichier hello2, il n'y a pas ce code. On fait référence directement à une interruption
logicielle (du noyau) d'ou le terme statically linked.
En effet le code de puts pour hello1 est nécessaire à l’exécution de hello1, alors que pour hello2, on fait référence à l’appel système n°4 (eax=4) qui correspond à sys_write (voir la liste des appels systèmes.).
Pour s'en rendre compte, il suffit de lancer les commandes suivantes :
$ldd hello1
$ldd hello2
Pour hello1 vous devez obtenir la liste des bibliothèques partagées utilisées et pour
hello2 le message suivant : (ou presque...)
not a dynamic exécutable
Afin d'obtenir la table des symboles des deux programmes, lancer les commandes suivantes :
$nm hello1
$nm hello2
Désassembler les deux fichiers binaires en lançant les deux commandes suivantes :
$objdump -d hello1.o > hello1_obj.dis
$objdump -d hello2 o > hello2_obj.dis
Puis étudiez les deux fichiers hello1.dis et hello2.dis
Pour le programme hello2.o vous devez obtenir quelque chose comme :
hello2:file format elf32-i386 Disassembly of section .text: 00000000 <message>: |
|||
0: | 68 65 6c 6c 6f | pushl | $0x6f6c6c65 |
5: | 20 77 6f | andb | %dh,0x6f(%edi) |
8: | 72 6c | jb | <main+0x66> |
A: | 64 21 20 | andl | %esp,%fs:(%eax) |
D: | 0a 00 | orb | (%eax),%al |
F: | 90 | nop | |
00000010 <_pointdentree>: | |||
10: | b8 04 00 00 00 | movl | $0x4,%eax |
15: | bb 01 00 00 00 | movl | $0x1,%ebx |
1A: | b9 00 00 00 00 | movl | $0x0,%ecx |
1F: | ba 0f 00 00 00 | movl | $0xf,%edx |
24: | cd 80 | int | $0x80 |
26: | b8 01 00 00 00 | movl | $0x1,%eax |
2b: | bb 00 00 00 00 | movl | $0x0,%ebx |
30: | cd 80 | int | $0x80 |
Désassembler les deux fichiers exécutables en lançant les deux commandes suivantes :
$objdump -d hello1 > hello1_exe.dis
$objdump -d hello2 > hello2_exe.dis
Pour le programme hello2 vous devez obtenir quelque chose comme :
hello2:file format elf32-i386 Disassembly of section .text: 08048074 <message>: |
|||
8048074: | 68 65 6c 6c 6f | pushl | $0x6f6c6c65 |
8048079: | 20 77 6f | andb | %dh,0x6f(%edi) |
804807c: | 72 6c | jb | <main+0x66> |
804807e: | 64 21 20 | andl | %esp,%fs:(%eax) |
8048081: | 0a 00 | orb | (%eax),%al |
8048083: | 90 | nop | |
08048084 <_pointdentree>: | |||
8048084: | b8 04 00 00 00 | movl | $0x4,%eax |
8048089: | bb 01 00 00 00 | movl | $0x1,%ebx |
804808e: | b9 74 80 04 08 | movl | $0x8048074,%ecx |
8048093: | ba 0f 00 00 00 | movl | $0xf,%edx |
8048098: | cd 80 | int | $0x80 |
804809a: | b8 01 00 00 00 | movl | $0x1,%eax |
804809f: | bb 00 00 00 00 | movl | $0x0,%ebx |
80480a4: | cd 80 | int | $0x80 |
Après l’édition des liens, il y a eu modification des adresses. Par exemple l'adresse relative 10 à été remplacée par l’adresse absolue 8048084.
Ouvrez les fichiers hello1_exe.dis et hello2_exe.dis. Constatez qu'il contient le code du fichier hello2_exe.dis, plus de nombreuses lignes de codes qui ne seront pas expliquées dans ce tp.
Explications :
Sur le listing ci-dessus on peut constater la présence de 4 colonnes.
Traduction de b8 04 00 00 00 movl $0x4,%eax
b8correspond au code opératoire de
le registre eax reçoit une valeur immédiate sur 32 bits.
04 00 00 00correspond aux 4 octets de la valeur à affecter au registre. Attention : Les processeurs x86 sont des little-endian : le poids faible est socké en premier !
Nous verrons dans un tp ultérieur les différents modes d'adressage.
Par exemple, le code opératoire de movl $4,%eax
est diffèrent de celui de
movl (message),%eax
.
Vous pouvez essayer en ajoutant la ligne movl (message),%ax
juste avant
la ligne movl $0x4,%eax
(cela ne va pas modifier l’exécution du programme).
Ensuite, réalisez le fichier objet et désassemblez-le.
Notez les deux codes opératoires correspondants.
La partie <message>
du listing ci-dessus correspond à la partie données du programme,
mais le désassembleur, ne pouvait pas le savoir. Il l'a donc traduite en code source (qui fait tout et rien !).
On peut remarquer la valeur des codes ascii des différentes lettres utilisées pour afficher hello world!
et le code 00 final pour le caractère NULL (marqueur de fin de chaîne).
Questions :
nopqui veut dire
no opération, c'est-à-dire que le microprocesseur ne fait rien.
Z) n'est pas dans notre message ?
movl $0x8048074,%ecx? A quoi correspond la valeur 8048074 ?