Rapport MI01 TP4 [PDF]

  • 0 0 0
  • Gefällt Ihnen dieses papier und der download? Sie können Ihre eigene PDF-Datei in wenigen Minuten kostenlos online veröffentlichen! Anmelden
Datei wird geladen, bitte warten...
Zitiervorschau

TP 4 MI01 Lefebvre Philippe Boucher Yann

.............. . . . . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. . ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ............... . . . . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. . . . . . . . . . . . . . . . . .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. . .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ...............

Introduction

1

Exercice 2 Partie A Question 1 Question 2 Partie B Question 1 Question 2

2 2 2 2 8 8 9

Exercice 1 Partie A Question 1 Question 2 Partie B Question 1 Partie C Question 1 Question 2 Question 3

9 9 9 10 12 12 13 13 15 15

Conclusion

16

Introduction Durant ce premier TP de programmation assembleur pour l’architecture x86_64, nous découvrirons l’environnement de développement et de déboguage Eclipse couplé avec GNU Assembler. Nous réaliserons quelques programmes simples pour nous familiariser avec le jeu d’instructions et les outils fournis.

1

Exercice 2 Partie A Question 1 Dans le fichier « hello1.S », définissez une variable msg, de type « chaîne de caractères » qui contient « Bonjour tout le monde! » et une variable longueur qui contient la longueur de la chaîne. Quelle doit être la taille de la zone mémoire à réserver pour longueur, sachant qu’elle sera comparée au registre r13 dans le programme ? Réponse : Pour déterminer la taille de longueur, nous étudions le fonctionnement du code et comment longueur est utilisé. cmp

r13, longueur[rip]

r13 est un registre 64-bit. Pour que l’instruction soit valide, il faut qu’une valeur de 64 bits soit récupérée depuis l’adresse longueur[rip]. Par conséquent, longueur doit correspondre à un entier 64-bits. Nous définissons les variables msg et longueur à l’aide des directives .ascii pour insérer une chaîne de caractères et .quad pour définir un entier 64-bits. 1 2

msg: .ascii "Bonjour tout le monde!" longueur: .quad 22

Le code complet de la réponse se situe dans le fichier ex1_1.asm.

Question 2 Décrivez le fonctionnement de ce programme en donnant l’algorithme utilisé. Que représente le registre r13 ? Réponse : L’algorithme utilisé dans le code est le suivant : 1: 𝑖 ← 0 2: repeat 3: 𝑝𝑡𝑟 ← &𝑚𝑠𝑔[𝑖] 4: 𝑖←𝑖+1 5: 𝑤𝑟𝑖𝑡𝑒(1, 𝑝𝑡𝑟, 1) 6: until 𝑖 < 𝑙𝑜𝑛𝑔𝑢𝑒𝑢𝑟 7: 𝑒𝑥𝑖𝑡(0) r13 est un index permettant de parcourir la chaîne msg caractère par caractère pour l’afficher.

2

Observez le déroulement de ce programme au moyen du débogueur. Réponse :

Affichage de la mémoire à l’adresse msg : contenu du message en représentation ASCII

3

Contenu de la mémoire à l’adresse longueur : la valeur 22 est encodée sur 8 octets en little-endian

4

Affichage de l’état des registres après l’exécution des instructions jusqu’au write

5

État du processeur après l’exécution d’une itération : l’index r13 vaut 1, le carry flag est set, le saut sera effectué

6

État du processeur après l’exécution de la dernière itération : l’index r13 vaut la longueur, le carry flag n’est pas set, le saut ne sera pas effectué

7

État du processeur avant exit

Partie B Question 1 On veut maintenant se passer de la longueur de la chaîne à afficher. À cet effet, on marque la fin de la chaîne de caractères par un caractère NUL (code ASCII 0). Modifiez la valeur initiale de la chaîne msg en conséquence et supprimez la variable longueur. Quel algorithme réaliser? Réponse : On supprime longueur, et on remplace msg par une chaîne ASCII délimitée par un NUL en utilisant .asciz. msg: .asciz "Bonjour tout le monde!" L’algorithme de la partie A peut être modifié afin que la condition de sortie de la boucle soit sur un test de la valeur du caractère au lieu d’une comparaison entre l’index et la longueur. On change aussi la boucle en un while afin de pouvoir gérer gracieusement le cas d’une chaîne vide. 1: 𝑖 ← 0 2: 𝑝𝑡𝑟 ← &𝑚𝑠𝑔[0] 3: while ∗𝑝𝑡𝑟 ≠ 0 do 8

4: 5: 6: 7:

𝑝𝑡𝑟 ← &𝑚𝑠𝑔[𝑖] 𝑖←𝑖+1 𝑤𝑟𝑖𝑡𝑒(1, 𝑝𝑡𝑟, 1) 𝑒𝑥𝑖𝑡(0)

Question 2 Modifiez le programme « hello2.S » pour implémenter cet algorithme. Réponse : La boucle d’affichage est désormais la suivante : mov mov cmp je

1 2 3 4 5 6 7 8

rdi, 1 rdx, 1 byte ptr [r12], 0 out

/* fd = 1 (stdout) /* 1 seul caractère

*/ */

/* Adresse du caractère /* Appel no 1 (write) /* Appel système

*/ */ */

suivant: lea rsi, [r12+r13] mov rax, 1 syscall

9 10 11 12

add cmp jnz

r13, 1 /* Passer au caractère suivant byte ptr [r12+r13], 0 /* Fin de la chaîne suivant /* Si non, passer au suivant

*/ */ */

Le code complet de la réponse se situe dans le fichier ex1_2.asm.

Exercice 1 Partie A Question 1 On stocke les chiffres du nombre converti en base 10 dans une chaîne de n caractères, n à définir. Chaque caractère occupe exactement un octet en mémoire. Sachant que nombre est exprimé sur 64 bits au plus en base 2, donnez l’expression de la valeur de n en fonction du plus grande valeur possible de nombre. Quelle valeur utiliser dans le programme? Remplacez les deux «? », ligne 13 dans le programme, par la valeur appropriée. Réponse : Le nombre maximal représentable par un entier 64-bit est 264 − 1. En base 10, le nombre de caractères nécéssaires pour représenter une telle valeur est donc : ⌊log10 (264 − 1) + 1⌋ = 20 Il est donc nécéssaire de réserver 20 octets pour la chaîne. 9

Question 2 Proposez un programme en assembleur x86-64 qui réalise la conversion de nombre et stocke chaque caractère successif obtenu par le reste de la division à partir de l’adresse chaine. Vous prendrez garde à la condition d’arrêt des itérations ainsi qu’à la cohérence de taille des opérandes, en particulier lors de la division et de la conversion du reste en chiffre affichable : nombre est stocké sur 64 bits, chaque caractère de la chaîne est un octet et le code ASCII du caractère « 0 » est 0x30. Vérifiez le fonctionnement de votre programme au moyen du débogueur en visualisant le contenu de la mémoire à l’adresse chaine. Réponse : Le code de la boucle de conversion du nombre en chaîne est le suivant mov rbx, 10 /* base */ lea rsi, chaine[rip] /* ptr vers le début de chaine */

1 2 3 4 5 6 7 8 9 10 11

loop:

mov rax, nombre[rip] xor rdx, rdx /* div effectue une division de edx:eax, on clear edx */ div rbx /* quotient => rax, reste => rdx */ add dl, '0' mov byte ptr [rsi], dl inc rsi test rax, rax /* si le nombre après divison vaut zéro, on arrête les itérations */ jnz loop

Le code complet de la réponse se situe dans le fichier ex2_1.asm.

10

La chaîne a été effectivement écrite en mémoire à l’adresse souhaitée. On remarque cependant qu’elle est inversée, caractéristique qu’il faudrait prendre en compte pour l’affichage.

11

Partie B Question 1 Complétez le programme de conversion pour qu’il affiche la chaîne obtenue à l’écran dans le sens de lecture naturel. Réponse : Pour répondre à cette question, il est nécessaire de corriger le sens d’affichage de la chaîne généré. Afin de réduire le nombre total d’appels systèmes, nous avons fait le choix d’inverser la chaîne directement en mémoire, avant de l’afficher d’un coup avec un unique appel système write. /* A présent, on inverse le contenu de chaîne */ /* A ce moment, rsi pointe vers la fin de la chaîne, rsi pointe vers le début */ lea rdi, chaine[rip] mov rcx, rsi sub rcx, rdi /* rcx = longueur de la chaîne */ /* Algorithme : on inverse *rdi et *rsi jusqu'à ce que rdi >= rsi */ dec rsi /* Ajuste rsi pour qu'il pointe vers le dernier caracètre et non pas après la fin */

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

reverse: mov al, [rdi] xchg al, [rsi] mov [rdi], al /* On échange les valeurs à *rsi et *rdi */ inc rdi dec rsi cmp rdi, rsi jb reverse

16 17 18 19 20 21

mov mov lea mov syscall

rax, rdi, rsi, rdx,

1 1 chaine[rip] rcx

/* /* /* /* /*

Appel no 1 (write) fd = 1 (stdout) Adresse du caractère nb de caractères Appel système

Le code complet de la réponse se situe dans le fichier ex2_2.asm.

12

*/ */ */ */ */

Partie C Question 1 Comment convertir le reste de chaque division entière en caractère affichable au moyen de ce tableau? Modifiez votre programme en conséquence. Réponse : Pour convertir le reste de chaque division entière en caractère affichage, il suffit d’utiliser le reste comme un index dans le tableau de conversion, et de récupérer le caractère qui s’y trouve. mov rbx, 33 /* base */ lea rsi, chaine[rip] /* ptr vers le début de chaine */ lea rdi, chiffres[rip] /* ptr vers la lookup table des caractères selon la base */

1 2 3 4 5 6 7 8 9 10 11 12

loop:

mov rax, nombre[rip] xor rdx, rdx /* div effectue une division de edx:eax, on clear edx */ div rbx /* quotient => rax, reste => rdx */ mov dl, [rdi + rdx] mov byte ptr [rsi], dl inc rsi test rax, rax /* si le nombre après divison vaut zéro, on arrête les itérations */ jnz loop

Le code complet de la réponse se situe dans le fichier ex2_3.asm.

13

Affichage de la valeur donnée en hexadécimal.

14

Affichage de la valeur donnée en binaire.

Question 2 Quelle est maintenant la taille maximale de la chaîne nécessaire pour pouvoir stocker tous les chiffres quelle que soit la base choisie? Réponse : La base la plus petite possible est à présent la base 2. Le nombre de taille maximale étant 264 − 1, il est trivial de conclure que la quantité maximale de caractères nécéssaires est 64.

Question 3 Donnez au moyen de votre programme l’expression de nombre en base 33. Réponse : Le résultat de l’exécution du programme avec ce nombre en base 33 est ILOVEASM. Au moins l’un des auteurs du rapport approuve ce message.

15

Conclusion Au cours de ce TP, nous avons pu utiliser les outils de déboguage pour observer l’évolution de l’état du processeur et de la mémoire à chaque instruction. Nous avons mis en place des algorithmes simples en assembleur pour réaliser l’affichage de chaînes de caractères et la conversion de nombres en chaînes.

16