Initiation `a l’assembleur x86...

44
Initiation ` a l’assembleur x86 (AT&T) J.-L. Bienvenu Ecole Nationale Sup´ erieure d’Electronique, Informatique et Radiocommunications de Bordeaux 1

Transcript of Initiation `a l’assembleur x86...

Page 1: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

Initiation a l’assembleur x86 (AT&T)

J.-L. Bienvenu

Ecole Nationale Superieure d’Electronique, Informatique et Radiocommunications de Bordeaux

1

Page 2: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

Table des matieres

1 Introduction 3

1.1 Qu’est-ce que l’assembleur ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

1.2 Mais alors, pourquoi l’assembleur ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

1.3 Avant de commencer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

1.4 Avertissement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

2 Les instructions 4

2.1 Le processeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

2.2 Types d’instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

2.3 Registre d’etat et instructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

3 Premier exemple 6

4 Les interruptions 7

5 Un peu de calcul 8

5.1 Operateurs arithmetiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

5.2 Operateurs bit a bit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

6 Le choix 10

7 La repetition 11

8 Donnees 14

9 Adressage 16

9.1 Adressage indirect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

9.2 Adressage indirect par registre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

2

Page 3: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

9.3 Adressage indirect indexe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

9.4 Exemples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

10 Instructions particulieres d’adressage 19

11 Pile et passage de parametres 21

11.1 La pile pour l’adresse de retour . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

11.2 La pile pour sauvegarder des registres . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

11.3 La pile pour les variables locales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

11.4 La pile pour le passage des parametres . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

12 Passage des parametres 23

13 Cas ou la pile est indispensable 25

13.1 Nombre quelconque de parametres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

13.2 Fonctions recursives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

14 Fichiers standard 28

14.1 Ecriture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

14.2 Lecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

15 Fichiers quelconques 33

15.1 Lecture et ecriture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

15.2 Ouverture et fermeture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

15.3 Programme d’illustration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

A Utilisation du debogueur gdb 40

A.1 Deroulement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

A.2 Affichage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

3

Page 4: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

B Creer une bibliotheque 41

4

Page 5: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

1 Introduction

1.1 Qu’est-ce que l’assembleur ?

L’assembleur (ou plus exactement le langage d’assemblage) est le langage de programmation de plusbas niveau. Chaque instruction (egalement appelee mnemonique) correspond exactement a un codecompris par la machine. La consequence est qu’il n’existe pas un seul langage, mais autant de langagesque de types de processeur. De plus, il n’y a aucune normalisation : dans le cas qui nous interesse(famille des processeurs Intel 32bits), il y a deux syntaxes dites “Intel” et “AT&T”.Pour les processeurs Motorola, il faut apprendre un autre langage, meme si les instructions presententdes similitudes. Ces processeurs font partie de la famille CISC ( complex instuction set computer).L’autre famille (RISC : reduced instruction set computer ) possede elle aussi des langages (pas plussimples pour autant)

1.2 Mais alors, pourquoi l’assembleur ?

Reponse : pour les cas ou un programme doit gerer un dispositif particulier (peripherique particulierou systeme autre qu’un ordinateur possedant un micro-processeur) et qu’un langage de haut niveau(comme le C) est difficile (voire imposible) a utiliser.Il se dit aussi que le code obtenu est plus compact (c’est vrai) et plus efficace (cela peut etre vrai,mais pas toujours). Ces arguments pouvaient etre decisifs dans un temps ou les machines avaient defaibles performances en memoire et en vitesse, mais ce n’est plus le cas.Autre reponse : La pratique de l’assembleur permet de comprendre plus intimement le fonctionnementd’un micro-processeur.

1.3 Avant de commencer

On supposera connue la pratique de la programmation dans un langage quelconque (si possible C ouC++). Il faudra disposer une machine a processeur Intel (posterieur au 80386) muni d’un sysemed’exploitation Linux.

1.4 Avertissement

Dans tout ce qui suit, les exemples de programmes ne realiseront que des operations simples quipourraient etre realisees par quelques lignes de C. (ce qui contredit les affirmations precedentes). Lebut n’est pas de programmer un systeme particulier, mais de comprendre les concepts mis en jeu. Cesexemples n’ont donc qu’un but purement pedagogique et presentent l’avantage de ne necessiter qu’unordinateur. De plus, il ne s’agit que d’une initiation : seules les instructions les plus importantesseront utilises et decrites de facon minimale. La description du jeu d’instructions complet avec latotalite des specifications devra etre cherchee dans un ouvrage de reference. A defaut, on peut trouverl’information a l’adresse :http://docs.sun.com/app/docs/doc/806-3773/6jct9o0cb?l=fr&a=view

5

Page 6: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

2 Les instructions

2.1 Le processeur

Avant de parler des instruction, il est necessaire de savoir a quoi elles s’appliquent. Il s’agit prin-cipalement de la memoire et des registres. La memoire vive (ou RAM) peut etre vue comme unesuite d’octets numerotes. Les registres sont des “cases” memoire situees dans le processeur. Elles sontd’acces plus rapide que la memoire et ont une taille de 4 octets (=32 bits) dans le cas qui nous interesse.

Ces registres portent un nom permettant de les adresser. Ces registres sont

• Les registres de donnees : EAX, EBX, ECX, EDX , dans lesquels on peut adresser les 32 bits,mais aussi les 16 bits de poids faible (notes respectivement AX, BX, CX et DX) et ces demi-registres peuvent a leur tour etre decomposes en leurs 8 bits de poids fort (AH,BH,CH et DH)et 8 bits de poids faible (Al, BL, CL et DL)

• Les registres d’adresse :ESI et EDI (il existe aussi SI et DI)

• Les registres de pile : ESP et EBP

Ces registres sont les seuls qui seront manipules explicitement. Il existe aussi:

• Le compteur d’instructions (ou compteur ordinal) qui contient a chaque instant l’adresse del’instruction decodee par le processeur.

• le registre d’etat (status register). debordement, retenue . . . ).

• Des registres de segment ( qu’on a pas besoin de manipuler si on se contente de 4 Go de memoire!)

• et bien d’autres encore . . .

Il est a noter que rien n’interdit a un registre d’adresse de contenir une donnee (et vice-versa), maiscertaines instructions ne peuvent utiliser qu’un registre particulier (ex: la multiplication place sonresulat dans EDX et EAX).

2.2 Types d’instructions

Comme dans la plupart des processeurs, l’ensemble des instructions permet les operations suivantes

• deplacement

chargement d’une valeur dans un registre

transfert d’un registre vers la memoire (et reciproquement)

6

Page 7: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

• calcul

les 4 operations arithmetiques

les operations bit a bit (or, and ,xor, not, decalages)

• les comparaisons entre registres ou entre registres et memoire

• les ruptures

saut a un autre emplacement du programme.

appel de fonction (qui est aussi un saut avec memorisation de l’adresse de l’instructionsuivante)

On remarquera que les noms des instructions se terminent souvent par la lettre l. Celle ci indique quel’instruction s’applique a une valeur sur 32 bits. Pour les versions 16 bits de processeur, l’instructionde transfert, par exemple s’appelait mov ; elle s’appelle maintenant movl et le transfert de 16 bitsutilise l’instruction movw.

2.3 Registre d’etat et instructions

Chaque bit de ce registre est un indicateur detat. Parmi les bits les plus importants, on trouve :

- ZF (zero flag) : positionne par une operation arithmetique ou une comparaison en cas d’egalite(mais pas par un deplacement).

- CF (carry flag) : positionne par une operation arithmetique lorsque le resultat depasse la taillede registre concerne.exemple : si on ajoute 2 registres de 8 bits, contenant 0xFF et 1, le resultat (en theorie 0x100)vaut 0 donc CF est positionne (ZF aussi)

- SF (sign flag) : positionne par une operation arithmetique dont le resultat est negatif ou unecomparaison dans le cas ou le eroperande est le plus grand (mais pas par un deplacement).exemple : si on soustrait 2 registres de 8 bits, contenant 0x1F et 0x20, le resultat ( -1) vautOxFF donc SF est positionne.

• OF (overflow flag) indique qu’un calcul a deborde sur le bit de signe. exemple : si on ajoute2 registres de 8 bits, contenant 0x7A et 0x21, le resultat vaut OxFF (donc -1) ; OF est alorspositionne. (SF aussi). Si on considere que les nombres sont signes, OF indique bien un probleme,si on les considere non signes, OF est inutile.

7

Page 8: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

3 Premier exemple

#exit.s version 1

.section .text

.globl _start

_start:

movl $101, %ebx # nombre a retourner au shell

movl $1, %eax # appel a la fonction 1 du noyau

int $0x80 # = terminer programme

Ce programme contient 3 instructions en assembleur : movl $101, %ebx , movl $1, %eax et int$0x80.A quoi sert le reste ?Les indications suivant la caractere # sont des commentaires. Il faut absolument mettre autant decommentaires que necessaire a la comprehension du programme (le langage en lui-meme est peu ex-pressif)La directive .section text indique a l’assembleur qu’il s’agit d’une zone de programme. Cettedirective est valable jusqu’a la prochaine directive .section.La directive .globl indique au compilateur qu’un symbole est global. Le mot start est une etiquette(ou label) qui represente une adresse. Cette adresse peut selon les cas, etre l’adresse d’une donnee,d’un saut, d’une fonction . . .Dans le cas present, start est une fonction. C’est meme la seule fonction dont la presence estobligatoire si on veut produire un programme executable (et le symbole start doit, dans ce cas, etreglobal).La directive .type start,@function indique au compilateur qu’il doit traiter l’etiquette comme unefonction.

L’instruction movl $101, %ebx place la valeur 101 dans le registre EBX, et movl $1, %eax place 1dans EAX. La notation exige qu’une constante soit prefixee par le symbole $. Les constantes en base16 commencent par 0x et les constantes en binaire par 0b

int $0x80 est ce qu’on nomme un appel systeme. Le systeme d’exploitation met a la disposition duprogrammeur un ensemble de routines pour que chacun ne soit pas oblige de les re-ecrire a chaquefois. La valeur placee dans EAX determine le choix de la routine utilisee. Ici la valeur 1 provoquel’appel de la routine de retour au systeme d’exploitation, le code retour du programme etant la valeurplacee dans EBX.

On peut alors assembler avec la commande as exit.s -o exit.o puis linker avec ld exit.o -o

exit puis executer exit et verifier le code retour avec la commande echo $?

On pourrait remarquer que gcc est capable de faire l’assemblage, mais si on tape gcc exit.s

-o exit, on obtient un message contenant In function ‘ start’ : multiple definition of

8

Page 9: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

‘ start’ et In function ‘ start’ : undefined reference to ‘main’. Le code est correct, maisle linker ne s’y retrouve pas. En fait, gcc cree sa propre fonction start (ce qui en fait 2) qui appellela fonction main (qui n’est pas definie).Un solution serait de renommer la fonction start en main (ca marche), mais on ne pourrait plusassembler avec as . La bonne solution est d’utiliser la commande gcc exit.s -nostdlib -o exit

pour que gcc ne prenne plus d’initatives.

Comme dans tout langage de programmation, il faut decouper le programme en fonctions. Dans lecas present, la sortie du programme devient une fonction. Pour l’utiliser, on place la valeur de retourdans EBX, puis on appelle exit avec l’instruction call .

# exit.s version2

.section .text

.globl _start

#---------------------------------------------------------------------------

exit:

.type exit,@function

movl %eax, %ebx # recuperation parametre = code retour

movl $1, %eax # appel a la fonction 1 du noyau

int $0x80 # = terminer programme

#---------------------------------------------------------------------------

_start:

.type _start,@function

movl $102,%eax # nombre a retourner au shell

call exit

4 Les interruptions

N.B. Ce paragraphe n’est pas indispensable pour la suite.

Dans le programme precedent, on a utilise une instruction int , qui correspond a une interruption.Un exemple permettra d’illustrer le mecanisme d’interruption. Pendant l’execution d’un programme,l’utilisateur appuie sur une touche du clavier. Or, il se peut que le programme n’ait pas besoin ducaractere frappe. Pour prendre en compte cet evenement, le clavier declenche une interruption, qui vaprovoquer l’execution d’une routine. Celle-ci va sauvegarder les registres (y compris le registre d’etat),s’executer, et restaurer les registres pour reprendre le programme a l’endroit ou il avait ete suspendu.Les adresses des routines correspondant a toutes les interruptions sont stockees dans une zone de lamemoire dite “table des vecteurs d’interruption”. Chaque type d’interruption est associe a une casede cette table qui contient simplement l’adresse de la routine a executer.Une interruption peut aussi etre provoquee par le processeur lui-meme (par exemple : en cas dedivision par 0, l’interruption affiche un message d’erreur et arrete le programme).

9

Page 10: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

Le systeme d’exploitation profite de cette table en se reservant une adresse de la table pour les appelssysteme, . L’instruction int $0x80 va chercher dans la table l’adresse de la routine correspondante.Dans ce cas, l’execution est provoquee par le programme et peut etre comparee a un appel indirectde fonction.

5 Un peu de calcul

5.1 Operateurs arithmetiques

Les operations d’incrementation et de decrementation s’appellent respectivement incl et decl ;l’operation de changement de signe est negl.les operations d’addition et de soustraction s’appellent respectivement addl et subl , la multiplica-tion et la division imull et idivl pour les operations signees, mull et divl pour les operationsnon signees.La syntaxe de incl et decl est :

incl <destination> et decl <destination>

ou <destination> ne peut etre qu’un registre ou une case memoire (idem pour negl).la syntaxe de addl est :

addl <operande> <destination>

ou <operande> peut etre une valeur immediate (i.e. une constante), un registre ou une case memoire,<destinaton> peut etre qu’un registre ou une case memoire, mais les deux ne peuvent pas etresimultanement une case memoire. (idem pour subl )Pour les multiplications, les choses se compliquent. D’abord parce que le produit de 2 nombres de 32bits occupe 64 bits. Dans tous les cas, les 32 bits de poids fort seront places dans EDX. Ensuite lamultiplication peut etre signee ou non signee.Pour la multiplication non signee, la syntaxe est:

mull <operande>

L’operande est multiplie par EAX et le resultat est place dans EDX:EAXPour la multiplication signee, il y a deux syntaxes:

imull <operande> ou imull <operande>, <destination>

dans le 2eme cas, on peut preciser quel registre contiendra le produit (ou plutot sa partie faible)

Pour la division, on ne precise que le diviseur, le dividende etant necessairement le double motEDX:EAX (si EDX est nul et EAX negatif, il faut etendre son signe a EDX avec l’instruction cltd.

10

Page 11: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

Le resultat est place dans EDX pour le reste et EAX pour le quotient. Si le diviseur est nul ou si lequotient est trop grand, une exception survient et le programme est arrete.

Pour experimenter les operations aritthmetiques, nous allons ecrire une fonction qui calcule les valeursde x2/4 − 5 ∗ x+ 7 pour une valeur donnee de x.

.section .text

.globl _start, exit

#-------------------------------------------------------------------------------

exit:

........

#-------------------------------------------------------------------------------

# fontion calculant x^2/4-5*x+7

calcul:

.type _start,@function

# entree : %eax contient la valeur

# sortie : %eax

# utilisation des registres pendant le calcul

# %eax et %edx sont utilises pour les operations * et /

# %ebx contient le resultat partiel

# %ecx conserve une copie de x

movl %eax, %ecx

imull %eax # multiplication de %eax par lui-meme

movl $0, %edx # important : le dividende est %edx:%eax, si

# on n’utilise que %eax, il faut : %edx=0

movl $4, %ebx

idivl %ebx

movl %eax, %ebx # %ebx=x^2/4

movl $5,%eax

imull %ecx # calcul de 5*x

subl %eax, %ebx # %ebx=x^2/4-5*x

addl $7, %ebx # %ebx=(x/4-5)*x+7

movl %ebx, %eax # valeur retournee dans %eax

ret

#-------------------------------------------------------------------------------

_start:

.type _start,@function

movl $10, %eax

call calcul # resultat dans %eax (transmis a exit)

call exit

11

Page 12: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

La transmission des parametres se fait par registre (ici un seul suffit) et le retour se fait par l’intermediairede EAX. Le langage C utilise la meme convention. Cette methode sera utilisee dans toute la suite.En ce qui concerne l’utilisation des registres, le programme ne fait pas tout a fait ce qui est annonce.Lorsque x2 a ete calcule, il est stocke dans EAX. Il faut alors le diviser par 4. Mais EDX doit etremis a 0 pour la division, ECX contient x pour la suite des calculs. Il ne reste que EBX pour placer lavaleur 4 ( le mode immediat n’existe pas pour la division). EBX est cense contenir le resultat partiel,mais rien n’a encore ete calcule . . .Il vaut mieux eviter ce genre d’astuce qui risque de rendre le programme incomprehensible (on auraitpu utiliser ESI par exemple). On verra par la suite qu’on pourra memoriser des donnees dans des“variables locales”On peut aussi remarquer que la fonction calcul modifie des registres autres que EAX, ces registrespouvaient contenir des resultats et etre detruits lors de l’appel a calcul. Ce probleme sera lui aussiresolu ulterieurement.

5.2 Operateurs bit a bit

• les operateurs logiques : andl, orl, xorl et not : effectuent respectivement les operations et, ou,ou exclusif et non avec les memes specifications que addl (pour les trois premieres) et negl pourla derniere)Il existe aussi l’operateur test qui effectue un et entre ses operandes sans les modifier (seuls lesflags sont positionnes).

• Les decalages : vers la gauche (shll, sall) vers ou la droite (shrl, sarl). leur syntaxe est : (parexemple pour shll

shll <operande> <destination>

ou<destination> peut etre un registre ou une case memoire et<operande> une valeur immediateou le registre CL.Les deux premieres instructions sont identiques : tous les bits sont decales vers la gauche et lebit de poids faible est mis a 0.shrl et sarl decalent les bits vers la droite, mais dans le cas de sarl, le bit de poids fort resteinchange, alors qu’il est mis a 0 par shrl.

• Les rotations roll et rorl (memes specifications que pour les decalages) decalent egalement lesbits, mais cette fois, le bit sortant est replace a l’autre extremite.

6 Le choix

Il est possible de comparer des valeurs et de faire des choix en fonction du resultat de la comparaison.Ce choix consiste en general a faire un saut vers un zone de code. L’instruction explicite de comparaisonest cmpl cette instruction positionne certains bits du registre d’etat qui vont permettre de prendreune decision.les sauts conditionnels les plus courants sont :

je (jump if equal) jne (jump if not equal)

12

Page 13: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

jg (ou jnle ) (jump if greater ( ou if not lower equal) ),jge (ou jnl ), jl (ou jnge ), jne(ou jng ) pour les comparaisons entre nombres signes

ja ( ou jnbe ) (jump if above (ou if not below or equal)) jae ( ou jnb ), jb ( ou jnae ), jbe( ou jna ) pour les comparaisons entre nombres non signes.les operations arithmetiques positionnant egalement le registre d’etat (mais pas les transferts) ,il est egalement possible d’utiliser ensuite les sauts conditionnels; jz (jump if zero) est iden-tique a je (de meme pour jnz et jne ).Il existe aussi d’autres tests concernant le debordement, la retenue, la parite . . .

L’exemple suivant montre l’utilisation de la comparaison associee a un saut pour calculer le maximumde deux nombres.

.section .text

.globl _start

......

max2:

.type max2,@function

# entree : X dans %eax

# Y dans %ebx

#sortie : max(X,Y) dans %eax

# et acessoirement min(X,Y) dans %ebx

cmpl %eax, %ebx

jl fin_max2 # if %ebx < %eax goto fin_max2

xchgl %ebx, %eax

fin_max2:

ret

#--------------------------------------------------------------------------------

_start:

.type _start,@function

movl $192,%eax

movl $217, %ebx

call max2

call exit

7 La repetition

Pour repeter des operations, il suffit d’utiliser les instructions vues precedemment, et le saut incondi-tionnel jmp.Dans l’exemple suivant, on calcule de facon classique le pgcd a 2 entiers

13

Page 14: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

.section .text

.globl _start

#--------------------------------------------------------------------------------

pgcd:

.type pgcd, @function

# calcule iterativement pgcd(a,b)

# a dans %eax

# b dans %ebx

boucle_pgcd:

cmpl $0, %ebx

je fin_pgcd

mov $0, %edx

idivl %ebx, %eax

movl %ebx, %eax # a = b

movl %edx, %ebx # b = a%b

jmp boucle_pgcd

fin_pgcd:

ret

#--------------------------------------------------------------------------------

_start:

.type _start,@function

movl $192,%eax

movl $111,%ebx

call pgcd

call exit

Lorsque le nombre de repetitions est connu, on peut utiliser le registre ECX pour le compteur, etl’instruction particuliere jexcz qui lui est associee. cette instruction effectue un saut si ce reg-istre a pour valeur 0. L’exemple suivant utilise cette instruction pour le calcul de la puissance.

14

Page 15: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

puissance:

.type puissance,@function

# entree : X dans %eax

# n dans %ebx

# sortie : X^n dans %eax

movl %ebx,%ecx # %ecx = compteur

movl %eax, %ebx # liberation de %eax qui sera utilise par

# la multiplication

movl $1,%eax # valeur initiale 1 dans %eax

prod_suiv:

jecxz fin_puissance

imull %ebx,%eax

decl %ecx

jmp prod_suiv

fin_puissance:

ret

#--------------------------------------------------------------------------------

_start:

.type _start,@function

movl $2,%eax

movl $7, %ebx

call puissance

call exit

La decrementation etant toujours faite dans de type de boucle, il y a meme une instruction quidecremente ECX et qui effectue le saut s’il est non nul : loop. Il y a encore plus sophistique :l’intruction loopz effectue le saut si ECX est non nul et si le drapeau ZF est mis (idem pour loopnzlorsque ZF n’est pas mis)

L’exemple suivant calcule la factorielle en utilsant loop

15

Page 16: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

....

factorielle:

# calcul de n! (on suppose n>=0)

movl %eax,%ecx # n dans %ecx

movl $1,%eax # resultat dans %eax

prod_suiv:

imull %ecx

loop prod_suiv

ret

#--------------------------------------------------------------------------------

_start:

.type _start,@function

movl $5,%eax # le resultat doit etre <256 pour le shell

call factorielle

call exit

8 Donnees

Pour declarer des donnees, il faut definir une nouvelle section. Si ces donnees sont initialisees , le nomde la section sera .data (ou .rodata = lecture seule ), sinon, ce sera .bss. Comme pour le code, chaquedonnee sera designee par une etiquette. S’il s’agit d’une donnee initialisee, elle devra etre preceded’un mot-cle precisant sa nature (donc sa taille). Ce mot-cle est .byte pour les octets, .long pour lesentiers (32bits), .short (ou .hword ) pour les mots de 16 bits, .string (ou .asciz ) pour les chaınesterminees par un 0, .ascii pour les chaınes non terminees par un 0. ( on trouve aussi : .quad pourles entiers sur 64 bits (les long long du C), .float (ou .single ) pour les reels en simple precision (lesfloats du C) et .double pour les reels en double precision (les double du C)S’il s’agit d’une donnee non initialisee, elle devra etre precede du mot-cle lcomm et specifiee par dataille en octets.

Exemple :

16

Page 17: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

....

.section .data

a: .long 12345

parfaits: .long 6,28,496,8128

premiers: .byte 2,3,5,7,11,13,17,19

separateurs: .byte ’ ’,’\t’,’\n’,’;’

erreur: .string "Valeur incorrecte\n"

.section .bss

buffer: .lcomm 1000

Remarque : les donnees initialisees sont integrees au programme executable et chargees en memoirelors de l’execution, alors que les donnees non initialisees n’occupent aucune place dans l’executable;leur place est seulement reservee lors de l’execution.

Exemple d’utilisation:

......

_start:

.type _start,@function

movl nombre1, %eax

movl nombre2, %ebx

call max2

call exit

#--------------------------------------------------------------------------------

.section .data

nombre1:

.long 192

nombre2:

.long 217

On remarque ici que les symboles sont traites comme les valeurs qu’ils designent, ce qui ne permetd’acceder qu’a la premiere valeur d’une liste. Pour obtenir l’adresse, il faudra utiliser une autresyntaxe.

17

Page 18: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

9 Adressage

9.1 Adressage indirect

Les seuls modes d’adressage examines jusqu’ici sont l’adressage immediat (placer une valeur constante)et l’adressage de registre. Il est aussi possible d’utiliser l’adressage indirect, c’est a dire utiliser uneadresse pour y stocker une valeur (constante ou registre).Exemple :

_start:

.type _start,@function

movl entier, %eax

incl %eax

movl %eax, entier

call exit

#--------------------------------------------------------------------------------

.section .data

entier:

.long 100

On constate ici que entier equivaut a la constante 100 dans l’instruction movl entier, %eax , maisne peut etre considere de la meme facon dans movl %eax, entier ( impossible de copier une valeurdans une constante ).

9.2 Adressage indirect par registre

L’adressage peut aussi se faire grace a une adresse contenue dans un registre. Dans ce cas , il fautparentheser le registres pour indiquer qu’il s’agit d’un adressage indirect .Exemples :

movl (%esi), %eax copie dans EAX la valeur pointee par ESI.

movb %bl, (%edi) copie l’octet BL a l’adresse contenue dans EDI.

On peut combiner adresse constante et adressage par registre en prefixant le registre par une constante.

Exemples :

18

Page 19: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

movb tableau(%esi), %al copie dans AL l’octet pointe par l’adresse tableau augmentee de lavaleur de ESI. (utile pour parcourir un tableau)

movl 8(%ebp), %eax copie dans EAX la valeur situee a l’adresse contenue dans EBP. ( souventutilise pour acceder aux parametres dans la pile (voir plus loin)

9.3 Adressage indirect indexe

On peut aussi utiliser un adressant une donnee (tableau par exemple), on ne peut pas chargerson adresse dans un registre (EDI par exemple) avec l’instruction movl tableau, %edi puisque celaeffectue la copie de la valeur et pas de l’adresse. L’assembleur autorise, pour le faire, la syntaxe : movl$tableau, %edi. Cependant, cette methode est deconseillee a cause du risque de confusion. Il estpreferable d’utiliser l’instruction leal (load effective adress), qui est plus explicite et permet en outred’utiliser toutes les formes d’adressage.Exemple : leal tableau(%ecx,4) %esi

9.4 Exemples

On va utiliser ces notions pour realiser deux operations simples :

1/ calcul de la longueur d’une chaıne de caracteres.

19

Page 20: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

longueur:

.type longueur, @function

# calcule la longueur d’une chaine

# entree : adresse de la chaine dans %eax

# sortie : longueur dans %eax

movl %eax, %esi # adresse chaine dans %esi

movl $0, %eax # longueur dans %eax (a calculer)

undeplus:

cmpb $0, (%esi,%eax)

je fin_longueur

incl %eax

jmp undeplus

fin_longueur:

ret

#--------------------------------------------------------------------------------

_start:

.type _start,@function

leal message, %eax

call longueur

call exit

#--------------------------------------------------------------------------------

.section .data

message:

.string "Hello World !\n","AAA"

2/ determination du maximum d’une liste d’entiers.

20

Page 21: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

max:

.type max, @function

# calcule le maximum d’une liste de

# nombres positifs termin\’ee par 0

# entree : adresse le la liste dans %eax

# sortie : maximum dans %eax

movl %eax, %esi

movl (%esi), %ebx # valeur courante dans %ebx

movl %ebx, %eax # maximum dans %eax

max_suiv:

cmpl $0, %ebx # fin de liste ?

je fin_max

addl $4, %esi # adresse valeur suivante

movl (%esi), %ebx

cmpl %eax, %ebx # comparaison avec max

jle max_suiv # pas mieux

movl %ebx, %eax # remplacement de max

jmp max_suiv

fin_max:

ret

#-------------------------------------------------------------------------------

_start:

.type start, @function

# sortie : maximum dans %eax

leal liste, %eax

call max

call exit

#-------------------------------------------------------------------------------

.section .data

liste:

.long 31,8,55,21,59,71,13,129,64,9,25,102,87,0

10 Instructions particulieres d’adressage

N.B. Ce paragraphe traite de certaines instructions particulieres aux processeurs x86 et n’est pas in-dispensable pour comprendre la suite.

Les tableaux etant frequemment utilises, le jeu d’instructions offre quelques operations dediees a leurtraitement (recherche, consultation, modification, copie, comparaison). les instructions correspon-dantes s’appellent respectivement scasl, lodsl, stosl, movsl et cmpsl s’il s’agit de tableaux de nombres

21

Page 22: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

de 32 bits. Pour 16 bits, il faut remplacer le l final par w et pour 8 bits par b. Une particularite deces instructions est de ne pas necessiter d’operande. Les adresses de tableau sont, selon les cas, dansESI ou EDI (ou les deux pour la copie). Il faut aussi noter que chacune de ces instructions modifiecelui (ou ceux) de ces registres d’adresse en vue de la prochaine operation (donc: decalage de 4 pourl, de 2 pour w et de 1 pour b. De plus, le decalage peut etre positif ou negatif selon la valeur du bitde direction du registre d’etat. L’instruction cld annule ce bit pour que le decalage soit positif (et stdle positionne pour qu’il soit negatif)

• scas{lwb} Compare l’element d’adresse EDI avec un EAX (ou AX ou AL) et modifie le registred’etat, puis decale EDI.Exemple : calcul de la longueur d’une chaıne terminee par 0.

# %edi et %esi contiennent l’adresse de la chaine

# on cherche le 0 avec scasb ; la longueur est la

# difference %edi - %esi -1

....

movb $0, %al

cherche0:

scasb

jne cherche0

subl %esi, %edi

decl %edi

....

• lods{lwb} Charge l’element d’adresse ESI dans EAX (ou AX ou AL), puis decale ESI.

• stos{lwb} Place EAX (ou AX ou AL) a l’adresse EDI, puis decale EDI

• cmps{lwb} Compare les elements d’adresse ESI et EDI, puis decale ces registres.

• movs{lwb} Copie l’element d’adresse ESI vers l’adresse EDI, puis decale ces registres. C’est laseule instruction qui realise un dplacement de memoire a memoire.

Pour appliquer ces operations a l’ensemble du tableau, ces instructions elementaires sont utiliseesconjointement avec les operations de repetition rep, repe (ou repz) et repne (ou repnz). Ces troisinstructions decrementent ECX jusqu’a ce qui celui-ci atteigne la valeur 0. repz et (respectivementrepnz) interrompt la repetition avant que ECX soit nul si ZF est positionne (respectivement annule).

Exemples :

1/ Copie d’une zone memoire de 1000 octets vers une autre

22

Page 23: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

# %esi contient l’adresse de la zone source

# %edi, celle de la destination

movl, $1000, %ecx

rep movsb

....

2/ Copie d’un tableau d’entiers de 16 bits termine par un 0

# %esi contient l’adresse de la zone source

# %edi, celle de la destination

copie: lodsw

stosw

cmpw $0, %ax

jne copie

....

11 Pile et passage de parametres

11.1 La pile pour l’adresse de retour

On a vu qu’il existe un registre de pile (ESP), mais il n’a jamais ete utilise pour l’instant, du moinsen apparence. La valeur de ce registre est fixee au demarrage par le systeme d’exploitation. Il ne doitdonc jamais etre initialise. Chaque fois qu’une fonction est appelee, l’instruction call place sur la pilel’adresse de la prochaine instruction a executer et soustrait 4 a ESP (les adresses dans la pile croissentvers le bas !) . l’instruction ret terminant la fonction ajoute 4 a ESP. Il est clair que la valeur deESP doit toujours etre correcte, sans quoi l’instruction ret replace une adresse erronee sur le registred’instruction et le programme devient imprevisible . . .

11.2 La pile pour sauvegarder des registres

Dans une fonction comme pgcd, on utilise le registre EDX pendant le calcul, alors qu’il pouvait conteniravant l’appel une valeur utile. Pour preserver les contenus d’un registre, il suffit de les placer sur lapile et de la depiler (en general dans le registre d’origine, mais ce n’est pas obligatoire) lorsque on ena besoin. Les instructions pour empiler et depiler sont respectivement pushl et popl . pushl prendune valeur en argument ( immediate, registre ou indirecte) et popl un registre.Pour sauvegarder les registres, il y a deux options :- empiler les registres avant l’appel et les depiler apres.- empiler dans la fonction les registres qui vont etre detruits et les depiler avant le retourL’avantage de la premiere methode est de ne sauvegarder que ce qui est nessaire, la deuxieme est de

23

Page 24: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

ne plus prendre le risque d’oublier cette sauvegarde.

11.3 La pile pour les variables locales

Si les registres ne suffisent pas pour un calcul donne, il est possible de reserver de la place dans la pile.Il suffit de diminuer le pointeur de pile du nombre d’octets suffisant.Exemple : les registres EAX,EBX et ECX contiennent des valeurs a utiliser ulterieurement et vontetre utilises pour d’autres calculs.

.....

subl $8, %esp

movl %eax , (%esp)

movl %ebx, 4(%esp)

# on utilise les registres pour un calcul; une fois fini,

# on retablit les valeurs des registres, puis %esp

movl (%esp), %eax

movl 4(%esp), %ebx

addl $8, %esp

11.4 La pile pour le passage des parametres

Dans les exemples precedents, les parametres ont ete transmis aux fonctions par les registres. Cettemethode est couramment utilisee dans la programmation sur processeur RISC car ils disposent engeneral d’un plus grand nombre de registres. On utilisera de preference la pile pour transmettre lesparametres, d’autant que c’est la methode utilisee par le langage C. Pour illustrer ce passage, on vatransmettre une chaıne de caracteres a la fonction puts de la bibliotheque C pour qu’elle l’affiche (onne sait pas encore le faire)

24

Page 25: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

.section .text

.globl _start

.extern puts

......

_start:

.type _start,@function

pushl $message

call puts

addl $4, %esp

call exit

#-------------------------------------------------------------------------------

.section .data

message:

.string "Ce message est affiche par puts"

On peut alors assembler avec la commande as affiche.s -o affiche.o puis linker (c’est plus com-plique) avec : ld -dynamic-linker /lib/ld-linux.so.2 -lc -o affiche affiche.o.Cet exemple montre qu’on peut utiliser une fonction du C dans un programme en assembleur. Dansla pratique, on ecrit plutot (si l’environnement s’y prete) un programme en C contenant des appels ades fonctions en assembleur.

12 Passage des parametres

Le passage des parametres a une fonction se fait simplement en empilant ceux-ci avant l’appel. Dansle cas des fonctions du C, ces parametres sont empiles en commencant par le dernier, ce qui estjudicieux, puisque l’ajout d’un parametre (a la fin de la liste) ne modifie pas la position des autres (qui se trouvent plus “haut” dans la pile ).Pour la recuperation de ces parametres a l’interieur de la fonction, on peut utiliser le pointeur de pileESP, mais celui-ci peut varier (par exemple parce qu’on reserve de la place pour les variables locales).On prefere utiliser le pointeur de pile auxiliaire EBP de la facon suivante :Des l’entree dans la fonction :

- On empile EBP (pour sauvegarder sa valeur)

- On recopie ESP dans EBP, qui sera le seul utilise pour acceder aux parametres (et aux variableslocales)

Avant de sortir de la fonction :

- On recopie EBP dans ESP

25

Page 26: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

- On depile EBP parametres

Dans le tableau qui suit, on suppose etre dans le contexte d’une fonction appelee avec n parametreset reservant la place pour p variables locales. (pour simplifier, on suppose qu’ils ont tous une taille de4 octets, si ce n’est pas le cas, il faudra en tenir compte dans le calcul des adresses). On supposeraaussi que les registres EBX, ECX et EDX ont ete empiles. La representation de la pile est :

. . . . . .

parametre n 4+4*n(%ebp)

. . . . . .

parametre 3 16(%ebp)

parametre 2 12(%ebp)

parametre 1 8(%ebp)

adresse de retour 4(%ebp)

sauvegarde de EBP (%ebp)

sauvegarde de EAX -4(%ebp)

sauvegarde de EBX -8(%ebp)

sauvegarde de ECX -12(%ebp)

variable locale 1 -16(%ebp)

variable locale 2 -20(%ebp)

. . . . . .

variable locale p -12-4*p(%ebp) ( ou (%esp) )

. . . . . .

Exemple : un nouvelle version de la fonction max2

max2:

.type max2,@function

# entree : X et Y dans la pile

#sortie : max(X,Y) dans %eax

pushl %ebp

movl %esp, %ebp

push %ebx

movl 8(%ebp), %eax

movl 12(%ebp), %ebx

cmpl %eax, %ebx

jl fin_max2 # if %ebx < %eax goto fin_max2

movl %ebx, %eax

fin_max2:

popl %ebx

movl %ebp, %esp

popl %ebp

ret

26

Page 27: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

Exemple d’appel de cette fonction :

....

_start:

.type _start,@function

pushl $217

pushl $192

call max2

add $8, %esp

call exit

On peut aussi integrer cette fonction dans un programme en C (afficheMax.c)

#include <stdio.h>

main()

{

printf("%d\n",max2(192,217));

}

il faut ajouter dans le fichier en assembleur : .globl max2 , assembler le fichier : as max2.s -o

max2.o, puis gcc afficheMax.c max2.o -o afficheMax

Attention, le fichier assembleur ne doit pas contenir de fonction start !

13 Cas ou la pile est indispensable

Dans la plupart des cas, s’il n’est pas besoin d’interfacer le C (ou autre) avec l’assembleur, on peutchoisir de passer les parametres par registre ou par la pile, mais dans certains cas, seule la pile estutilisable

13.1 Nombre quelconque de parametres

Si le nombre de parametres passes a une fonction varie selon les appels, les registres risquent d’etreinsuffisants et de plus, ils ne se pretent pas a un parcours indexe. Il y a principalement 2 methodespour passer des parametres en nombre quelconque:

• la methode ”argc/argv” : tous les parametres sont de meme type et le 1erargument indique lanombre de valeurs a prendre en compte (variante : on ne donne pas le nombre, mais on ajouteapres le dernier parametre une valeur speciale de meme type (ex: une suite d’entiers se terminantpar 0 )

27

Page 28: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

• la methode ”printf” : les parametres ne sont pas tous de meme type. On ajoute un 1erparametrequi decrit les parametres qui suivent.

Un exemple trivial de la 1ere methode est justement le traitement des arguments de la ligne de com-mande. Le shell memorise les mot sur la ligne de commande et place les adresses sur la pile enterminant par 0 (le nombre d’arguments n’est pas transmis)

.section .text

.globl _start

.extern puts

........

_start:

.type _start, @function

push %ebp

movl %esp, %ebp

movl $0, %ecx # %ecx compte les arguments

arg_suiv:

movl 8(%ebp,%ecx,4),%esi

# adressage indexe dans la pile

cmpl $0,%esi # si l’adresse est 0

je args_exit # c’est fini

pushl %ecx # puts peut utiliser %ecx

pushl %esi # parametre pour puts

call puts

popl %esi

popl %ecx

incl %ecx

jmp arg_suiv

args_exit:

movl %ebp, %esp

pop %ebp

movl %ecx, %eax # retourne le nombre d’arguments trouves

call exit

Remarque : le parcours de la pile s’arrete lorque une adresse 0 est trouvee. Si on continue apres avoir”saute” cette adresse , on trouve les chaınes d’environnement du shell.

28

Page 29: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

13.2 Fonctions recursives

(Juste pour le plaisir)

Le PGCD :

.section .text

.globl pgcd

pgcd:

# calcule recursivement pgcd(a,b)

pushl %ebp

movl %esp, %ebp

movl 8(%ebp), %eax # a dans %eax

movl 12(%ebp), %ebx # b dans %ebx

next:

cmpl $0, %ebx # if (b==0) return a ;

je terminal

movl $0, %edx # else

idivl %ebx, %eax # return pgcd(b, a%b)

pushl %edx

pushl %ebx

call pgcd

terminal:

movl %ebp, %esp

popl %ebp

ret

et la factorielle :

29

Page 30: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

.section .text

.globl fact

#--------------------------------------------------------------------------------

fact:

pushl %ebp

movl %esp, %ebp

movl 8(%ebp), %ebx # n dans %ebx

# resultat dans %eax

cmpl $0, %ebx # if (n<=0)

jle terminaison # return 1

decl %ebx # else

pushl %ebx # return fact(n-1)

call fact # ...

popl %ebx # ...

incl %ebx # ...

mull %ebx # * n

jmp fini

terminaison:

movl $1, %eax

fini:

movl %ebp,%esp

popl %ebp

ret

14 Fichiers standard

Pour acceder aux fichiers, il faudra utiliser des appels au systeme par l’intermediaire le l’interruption0x80. pour le systeme, un fichier est un entier (dit descripteur de fichier), dont le numero est obtenulors de l’operation d’ouverture, a l’exception de certains fichiers qui sont ouverts par defaut (dont :l’entree standard (0), la sortie standard (1) et la sortie d’erreur standard (2)). Le plus simple est decommencer par ceux-ci, ce qui permettra deja de faire des saisies et des affichages.

14.1 Ecriture

Pour ecrire, il faut utiliser la fonction 4 de l’interruption. Les specifications d’utilisation sont les suiv-antes :

30

Page 31: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

nom EAX EBX ECX EDX

write 4 descripteur adresse taille

La fonction d’ecriture d’une chaıne de caracteres sur la sortie standard ne presente aucune diffculte :il suffit de calculer la longueur et faire l’appel systeme (on se limitera a l’ecriture sur la sortie standard).

print:

.type print, @function

# affiche une chaine de caracteres

pushl %ebp

movl %esp, %ebp

cld

movl 8(%ebp), %edi

movl %edi, %ecx # duplique l’adresse dans %ecx pour le

# calcul de la longueur

movb $0,%al

len:

scasb

jne len

subl %ecx, %edi

decl %edi

movl %edi, %edx # longueur dans %edx

# adresse chaine dans %ecx

movl $1, %ebx # descripteur 1 = sortie standard

movl $4, %eax # fontion write

int $0x80

movl %ebp, %esp

popl %ebp

ret

#--------------------------------------------------------------------------------

_start:

.type _start,@function

pushl $message

call print

addl $4, %esp

call exit

#--------------------------------------------------------------------------------

.section .data

message:

.string "Hello World !\n"

Ce programme a pour l’equivalent en C :

31

Page 32: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

#include <stdio.h>

main()

{

puts("Hello World !\n");

}

la compilation de ce programme donne un executable de 4847 octets. Le programme en assembleur(construit avec as et ld ne fait que 707 octets. On peut meme le faire ”maigrir” en lui appliquant lacommande strip qui supprime les symbole (on obtient alors 380 octets).

14.2 Lecture

Pour lire, il faut utiliser la fonction 4 de l’interruption. Les specifications sont identiques a celles del’appel write :

nom EAX EBX ECX EDX

read 3 descripteur adresse taille

La encore, on se limitera a la lecture sur l’entree standard, mais tout se complique, puisqu’il fautpreciser la taille du buffer d’entree, alors que les donnees ne sont pas encore entrees . . . .Le plus simple est de commencer par ecrire une fonction getchar qui ne preleve qu’un caractere a lafoisIl reste un petit probeme : lorque l’utilisateur tape control d pour indiquer la fin des entrees, aucuncaractere n’est depose par read. Pour resoudre ce probleme, on placera la valeur -1 (0xFF) avantl’appel et c’est cette valeur qui sera retournee par getchar si rien n’est depose.

32

Page 33: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

getchar:

.type getchar, @function

pushl %ebx

pushl %ecx

pushl %edx

subl $1, %esp # buffer de taille 1 dans la pile

movl $3, %eax # appel systeme read

movl $0, %ebx # descripteur 0 = entree standard

movl %esp, %ecx # adresse buffer

movb $0xFF, (%ecx)

movl $1, %edx # taille buffer

int $0x80

movb (%ecx), %al

addl $1,%esp

popl %edx

popl %ecx

popl %ebx

ret

Pour saisir une chaıne, on cree une fonction gets a laquelle on transmet les informations necessaires al’appel read ( la taille du buffer et son adresse). Cette fonction appelle getchar jusqu’a ce que celle-cirenvoie ’\ n’ ou -1 (qui ne seront pas ajoutes). chaque caractere autre est ajoute a condition que lataille limite ne soit pas atteinte. (on choisira de ne pas arreter les appels a getchar pour consommertous les caracteres excedentaires).

33

Page 34: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

get:

.type get, @function

pushl %ebp

movl %esp, %ebp

pushl %ebx

pushl %ecx

pushl %edx

pushl %edi

movl 8(%ebp), %edi # adresse chaine dans %edi

movl 12(%ebp), %edx # taille maxi dans %edx

decl %edx # garder une place pour le 0 terminal

movl $0, %ecx # taille reelle dans %ecx

get_suiv:

call getchar

cmpb $0xFF, %al

jne autre

movl $-1, %eax # EOF ?

jmp fin_get # oui : fini

autre:

cmpb $10, %al # ’\n’ ?

je fin_chn # oui : fini

cmpl %ecx, %edx # taille limite atteinte ?

je get_suiv # oui : on passe a la lecture suivante

stosb # non : on stocke

incl %ecx

jmp get_suiv

fin_chn: # ajout du 0 terminal

movb $0,(%edi)

fin_get:

popl %edi

popl %edx

popl %ecx

popl %ebx

movl %ebp, %esp

popl %ebp

ret

34

Page 35: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

15 Fichiers quelconques

15.1 Lecture et ecriture

Si le descripteur est connu, il suffit de proceder comme precedemment en le passant comme argumentsupplementaire, ce qui donne, pour la lecture :

read:

.type read, @function

pushl %ebp

movl %esp, %ebp

pushl %ebx

pushl %ecx

pushl %edx

movl $3, %eax # appel systeme read

movl 8(%ebp), %ebx # descripteur

movl 12(%ebp), %ecx # adresse buffer

movl 16(%ebp), %edx # taille buffer

int $0x80

# le nombre d’octets lus est place dans %eax

popl %edx

popl %ecx

popl %ebx

movl %ebp, %esp

popl %ebp

ret

On peut remarquer que cette fonction est similaire a la fonction read du C : int read(int fd, const void*buf, size t count); .Pour la fonction write, on procede de meme.

15.2 Ouverture et fermeture

Pour les fichiers nommes (comme les fichiers sur disque), il faut obtenir un descripteur avec l’appelsysteme open

nom EAX EBX ECX EDX

open 5 nom mode permissions

close 6 descripteur

Le mode d’ouverture est 0 (lecture seule), 1 (ecriture seule) ou 2 (lecture et ecriture). Pour les

35

Page 36: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

ouvertures autorisant l’ecriture, on peut combiner la valeur (1 ou 2) avec

• 0100 : creer le fichier s’il n’existe pas (O CREAT en C)

• 0200 : ne pas creer le fichier s’il existe (O EXCL en C)

• 01000 : ramener le fichier a une taille nulle s’il existe (O TRUNC en C)

• 02000 : ecrire seulement a la fin du fichier (O APPEND en C)

Les constantes sont en octal (prefixe 0). Dans le cas ou il peut y avoir creation, le 3eme parametre fixeles droits du fichier (classiquement 0644). Pour eviter de memoriser ces valeurs, il est possible de lesdeclarer de la facon suivante :

.equ O_RDONLY, 0

.equ O_WRONLY, 1

.equ O_RDWR, 2

.equ O_CREAT, 0100

.equ O_EXCL, 0200

.equ O_TRUNC, 01000

.equ O_APPEND, 02000

.equ DROITS, 0644

La fonction open peut s’ecrire ainsi :

open:

.type open, @function

# ouvre un fichier

# 1er argument : nom

# 2e argument : mode d’ouverture

# retourne : descripteur dans %eax

# ou valeur n\’egative si esrreur

pushl %ebp

movl %esp, %ebp

pushl %ebx

pushl %ecx

pushl %edx

movl $5, %eax

movl 8(%ebp), %ebx # nom

movl 12(%ebp), %ecx # mode

movl $DROITS, %edx # droits

int $0x80

popl %edx

popl %ecx

popl %ebx

movl %ebp,%esp

popl %ebp

ret

36

Page 37: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

L’ecriture de la fonction close ne presente aucune difficulte . . .

15.3 Programme d’illustration

Pour illustrer l’utilisation des fichiers, voici un petit programme qui va copier un fichier vers un autreen mettant son contenu en majuscules. En plus des fonctions deja citees, il suffit d’ecrire une fonctioncopie maj prenant en argument deux descripteurs de fichier, l’un en lecture, l’autre en ecriture. Pourles operations de lecture et d’ecriture, on definit, dans la zone des donnees non initialisees, un bufferpour le transfert des donnees.

37

Page 38: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

copie_maj:

.type copie_maj, @function

pushl %ebp

movl %esp, %ebp

pushl %ebx

pushl %ecx

pushl %edx

pushl %esi

pushl %edi

copie_suiv:

# chargement d’un bloc de donnees

pushl $MAX # taille

pushl $buffer

movl 8(%ebp), %eax # descripteur source

pushl %eax

call read

addl $12, %esp

cmpl $0, %eax # echec lecture ?

je copie_fin # on sort

leal buffer, %esi

movl %esi, %edi

movl %eax, %ecx # nombre d’octets lus dans %ecx

movl %eax, %ebx # et memorises dans %ebx

maj:

lodsb

cmpb $’a’, %al

jl range

cmpb $’z’,%al

jg range

andb $0b11011111, %al # passage en majuscule

range:

stosb

loop maj

# ecriture du buffer

pushl %ebx # taille

pushl $buffer

movl 12(%ebp), %eax # descripteur destination

pushl %eax

call write

add $12, %esp

jmp copie_suiv

copie_fin:

popl %edi

popl %esi

popl %edx

popl %ecx

popl %ebx

movl %ebp, %esp

popl %ebp

ret

#-------------------------------------------------------------------------------

.section .bss

.equ MAX, 4000

.lcomm buffer, MAX

38

Page 39: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

Il reste a ecrire la fonction principale. Celle-ci va verifier que deux arguments ont ete transmis par leshell, ouvrir le 1eren lecture, le 2eme en ecriture et afficher les messages d’erreur en cas de probleme.

39

Page 40: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

.globl _start

_start:

.type _start, @function

push %ebp

movl %esp, %ebp

movl 12(%ebp), %esi # 1er argument ?

cmpl $0, %esi # si absent , exit

je err_arg

pushl $O_RDONLY # ouverture fichier source

pushl %esi

call open

addl $8, %esp

cmpl $0, %eax # resultat <0 : on sort

jle err_r_open

movl %eax, %ecx # %ecx : descripteur source

movl 16(%ebp), %esi # 2e argument ?

cmpl $0, %esi # si absent , exit

je err_arg

pushl $O_WRONLY|O_EXCL|O_CREAT # ouverture fichier destination

pushl %esi

call open

addl $8, %esp

cmpl $0, %eax # resultat <0 : on sort

jle err_w_open

movl %eax, %edx % edx : descripteur destination

pushl %edx

pushl %ecx

call copie_maj # appel a la fonction de copie

addl $8, %esp

pushl %ecx # fermetures

call close

pushl %edx

call close

addl $8, %esp

jmp fini

err_r_open:

pushl %esi # message : erreur ouverture source

call print

pushl $msg_r_open

call print

addl $8, %esp

jmp fini

err_w_open:

pushl %esi # message : erreur ouverture destination

call print

pushl $msg_w_open

call print

addl $8, %esp

jmp fini

40

Page 41: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

err_arg: # message : pas assez d’arguments

pushl $usage

call print

movl 8(%ebp), %esi

pushl %esi

call print

pushl $msg_args

call print

addl $12, %esp

fini:

movl %ebp, %esp

pop %ebp

call exit

#--------------------------------------------------------------------------------

.section .data

msg_r_open:

.string " : lecture impossible\n"

msg_w_open:

.string " : ecriture impossible\n"

usage:

.string "Usage : "

msg_args:

.string " <source> <destination>\n"

41

Page 42: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

A Utilisation du debogueur gdb

gdb permet de controler de deroulement d’un programme et de visuliser l’etat des registres et desdonnees. Pour pouvoir utiliser gdb, il faut d’abord assembler avec l’option –gstabs ( ou compiler com-piler avec l’option -ggdb de gcc.

~ > as --gstabs truc.s -o truc.o; ld truc.o -o truc

~ > gdb truc

Il est egalement possible d’utiliser gdb depuis Emacs avec la commande Esc x gdb

A.1 Deroulement

La premiere chose a faire pour examiner le deroulement du programme par gdb est de le demarreravec la commande r (run). il est possible de preciser les arguments de la ligne de commande ex: rdonnees.txt 5Dans ce cas, le programme s’execute d’une traite. Pour l’executer pas a pas, il faut poser des pointsd’arret (breakpoints) avec la commande b (break) en precisant le nom de la fonction ou le numero dela ligne. Dans le cas d’un programme multifichiers, il faut eventuellement preciser en plus le nom dufichier avec la sytaxe b <fichier>:<fonction> ou b <fichier>:<ligne>

Lorsque le programme demarre, il s’execute jusqu’au premier point d’arret. Il est alors possibled’executer les instructions une a une en tapant n (next). Dans ce cas, un appel de fonction est comptecomme une instruction. Pour entrer dans la fonction, il faut taper s (step).

Un numero est attribue a chaque point d’arret. Le point d’arret n peut etre desactive avec dis n(disable) et reactive avec ena n (enable). Pour voir la liste des points d’arret, taper info breakpointsenfin, pour supprimer un point d’arret, utiliser cl (clear) en precisant le nom de la fonction ou lenumero de la ligne et pour tous les supprimer, taper del (delete).

Pour reprendre l’execution jusqu’au prochain point d’arret, taper c (continue).

Il est egalement possible de sauter a la fin d’une boucle avec u (until) ou la fin d’une fonction avec f(finish)

On peut definir une suite de commandes <C1>, <C2>, .. <Ck> au point d’arret <n> de la faconsuivante :

commands <n>

<C1>

<C2>

..

<Ck>

end

42

Page 43: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

Pour quitter gdb, taper q (quit)

A.2 Affichage

On peut afficher un registre (en prefixant en nom par $), ou une donnee avec la commande p (print).On peut imposer un format en le prefixant par /. Ce format peut etre : d (decimal), x (hexadecimal),t (binaire), c (caractere), a (adresse), f (float). Par exemple :p/t $eax affiche eax en base 2 et p /c

liste affiche le contenu de l’adresse liste sous forme de caractere. Pour avoir les 10 premiers elementsde liste, il suffit de taper p /c liste @10 .Pour un symbole X, on peut obtenir son adresse avec p &(X). Pour un registre contenant une adresse(esi par exemple, on peut visualiser ce qui se trouve a cette adresse avec p *($esi) @20

Si on veut connaıtre une (ou des) valeur(s)a tout moment, on peut utiliser disp (display) a la placede print. Dans ce cas, l’affichage se fera a chaque arret de l’execution (et donc a chaque instructionsi on utilise step ou next). Comme pour les points d’arret, on peut desactiver, reactiver ou annuler lacommande display avec les commandes disable, display, enable, display et undisplay.

Il est egalement possible d’afficher la pile d’appel avec la commande bt (backtrace)

Pour les donnees en memoire, on peut aussi utiliser x (examine) avec la syntaxe x /nft ou n estle nombre d’elements, f le format et t la taille. Le format peut etre : d=decimal, u=decimal nonsigne, x=hexa, t=binaire, f=float, a=adresse, s=chaıne (pas de taille a preciser). La taille peut etre :b=octet, h=demi-mot, w=mot, g=double mot (exemple : x /12uh &liste.

Pour afficher tous les registres, il suffit d’utiliser info registers

B Creer une bibliotheque

Il est possible de regrouper tous les fichiers objet dans une bibliotheque. Pour cela, il suffit d’utiliserla commande ar (archive) avec la syntaxe.

ar cr <fichier bibliotheque> <fichier objet> ...

Exemple : ar cr libperso.a *.o. (Si on veut utiliser une bibliotheque nommee xxx, il faudra ap-peler le fichier libxxx.a)Pour que le linker trouve plus facilement les symboles (toutes les etiquettes placees dans les fichierssource), on utilise la commande ranlib (ranlib <fichier bibliotheque>) qui ajoute un fichier d’index

On peut connaıtre les symboles d’un fichier objet ou d’une bibliotheque avec la commande nm nm<fichier>). Chaque symbole est marque par une lettre : T pour les fonctions globales, U pour les

43

Page 44: Initiation `a l’assembleur x86 (AT&T)bornat.vvv.enseirb.fr/wiki/lib/exe/fetch.php?media=mi102:... · 2016. 11. 30. · Initiation `a l’assembleur x86(AT&T) J.-L. Bienvenu Ecole

symboles externes, t pour les etiquettes locales, B,D ou R pour les donnees globales (des sections bss,data et rodata), b,d ou r pour les donnees locales.Remarque : pour ne visualiser que les fonctions globales ou externes referencees, il suffit de filtrer avecnm | cut -c 10- | grep ^[TU]

Pour utiliser la bibliotheque libperso.a ( supposee situee dans le repertoire ~/Lib86) pour linkeravec un fichier objet prog.o par exemple), il suffit de taper la commande :ld -L~/Lib86 prog.o -lperso (l’option -l doit etre a la fin)

44