Et maintenant : l`assembleur 80x86

publicité
Et maintenant :
l’assembleur 80x86
Architecture des ordinateurs – p. 1
Les registres (1)
Les registres sont des «variables» du
processeur, dont le nom est fixé.
Appartenant au CPU, leur utilisation est très
rapide (beaucoup plus que la mémoire
centrale).
Sur les 80x86, on en distingue plusieurs types :
Les registres généraux
Les registres d’index
Les registres de segment
Les registres MMX et SSE
...
Architecture des ordinateurs – p. 2
Les registres (2)
Nous reverrons chacun des registres dans leur
usage propre par la suite. La présentation
« détaillée » suivante n’a pour but que de vous
présenter les noms que vous allez rencontrer
très rapidement en TP.
Retenez cependant l’usage principal de
chacun de ces registres.
Architecture des ordinateurs – p. 3
Les registres (3)
EAX (32 bits)
AX (16 bits)
AH (8 bits)
AL (8 bits)
Accumulateur
EBX (32 bits)
BX (16 bits)
BH (8 bits)
BL (8 bits)
Base
ECX (32 bits)
CX (16 bits)
CH (8 bits)
CL (8 bits)
Compteur
EDX (32 bits)
DX (16 bits)
DH (8 bits)
DL (8 bits)
Données
EDI (32 bits)
Destination Index
ESI (32 bits)
Source Index
ESP (32 bits)
Pointeur de pile
EBP (32 bits)
Pointeur de base
Architecture des ordinateurs – p. 4
Les registres (4)
Si EAX contient (en hexa) 0xA512E6FF (32 bits = 4 octets = 1
double-mot (dword)), alors :
AX contient 0xE6FF (16 bits = 2 octets = 1 mot (word))
AH contient 0xE6 (8 bits=1 octet (byte))
AL contient 0xFF (8 bits = 1 octets (byte))
Ce comportement est identique pour les registres EBX, ECX et
EDX. Il s’agit d’un héritage des premiers processeurs Intel 80x86,
qui fonctionnaient en 8 bits puis en 16 bits.
Certains processeurs Intel ou AMD récents (Intel Itanium, AMD
Opteron) fonctionnent en 64 bits, ainsi que les processeurs plus
anciens Sun UltraSparc, DEC Alpha, . . .
Architecture des ordinateurs – p. 5
Les registres (5)
EAX :
Ce registre est utilisé pour récupérer le résultat
ou comme paramètre de sous-programmes.
Il sert de paramètre aux fonctions
mathématiques existantes.
EBX :
Ce registre sert de base pour l’adressage
indexé (accès à la mémoire). Adressage que
nous étudierons plus tard.
Architecture des ordinateurs – p. 6
Les registres (6)
ECX :
Ce registre est un registre compteur. Il est
utilisé pour gérer des boucles du type :
Pour i allant de 10 à 1
Afficher i
Nous verrons un peu plus tard comment gérer
cette boucle en assembleur.
EDX :
Registre utilisé pour certaines multiplications /
divisions.
Architecture des ordinateurs – p. 7
Les registres (7)
Le registre ESP est le registre de pile, son
usage en tant que variable est donc
strictement interdit.
Le registre EBP sert comme EBX pour
l’adressage indexé (pour accéder à la pile).
EDI est le registre de destination, utilisé pour
la gestion des chaînes.
ESI est l’équivalent de EDI mais cette fois-ci
c’est la source.
Architecture des ordinateurs – p. 8
Un premier programme
en assembleur
Architecture des ordinateurs – p. 9
Une première version (1)
.intel_syntax noprefix
# choix de la syntaxe intel
.data
# Partie Données
hello:
long:
.ascii
.int
"Hello, world\n"
13
.global main
# une chaı̂ne de caractères
# longueur de la chaı̂ne
# Partie programme
main:
mov
mov
mov
mov
int
eax,4
ebx,1
ecx,offset hello
edx,long
0x80
# affichage de la chaı̂ne
mov
mov
int
eax,1
ebx,0
0x80
# on quitte le programme
Architecture des ordinateurs – p. 10
Une première version (2)
.intel_syntax noprefix
On choisit la syntaxe Intel avec l’option
«noprefix». Ceci permet d’écrire mov eax,4
(signifie eax=4) plutôt que movl $4,%eax.
Architecture des ordinateurs – p. 11
Une première version (3)
.data
hello:
long:
.ascii
.int
"Hello, world\n"
13
Dans la partie .data, on peut définir des
variables globales initialisées avec un nom
symbolique. On définit ici une chaîne de
caractères hello terminée par un caractère de
saut de ligne et un entier long représentant la
longueur de la chaîne.
Architecture des ordinateurs – p. 12
Une première version (4)
mov
mov
mov
mov
int
eax,4
ebx,1
ecx,offset hello
edx,long
0x80
On va appeler avec l’instruction int la fonction 4 de l’interruption
0x80 du kernel (noyau linux). Elle nécessite 4 paramètres :
4 dans eax : la fonction (affichage de chaîne)
1 dans ebx : fichier de sortie (ici stdout)
adresse de la chaîne dans ecx
longueur de la chaîne dans edx
Architecture des ordinateurs – p. 13
Une première version (5)
mov
mov
int
eax,1
ebx,0
0x80
On appelle à nouveau l’instruction int pour la
fonction 1 de l’interruption 0x80 du kernel. Elle
nécessite 2 paramètres :
1 dans eax : la fonction (sortie de programme)
0 dans ebx : code de retour du programme
Architecture des ordinateurs – p. 14
Une deuxième version (1)
.intel_syntax noprefix
# choix de la syntaxe intel
.data
# Partie Données
hello:
.ascii
"Hello, world\n\0"
.global main
# une chaı̂ne de caractères
# Partie programme
main:
push
call
add
offset hello
printf
esp,4
# affichage de la chaı̂ne
push
call
0
exit
# on quitte le programme
# on dépile l’argument (4 octets)
Architecture des ordinateurs – p. 15
Une deuxième version (2)
On a remplacé les appels directs au noyau par des appels aux
fonctions de la librairie C disponible sur tous les systèmes Linux
(Unix en général). Les arguments sont passés par la pile.
Avantage : on a accès à toutes les fonctions prédéfinies de
cette librairie, et de manière générale à toutes les
fonctionnalités des librairies installées sur le système ! Et c’est
plus rapide à écrire. . .
Inconvénient : on ne sait pas comment sont codées ces
librairies, et on utilise la pile. Si on souhaite de la rapidité
d’exécution, il est déconseillé de les utiliser (et ce n’est pas
dans l’«esprit assembleur»).
Architecture des ordinateurs – p. 16
Les déclarations de variables
en assembleur
Architecture des ordinateurs – p. 17
Format général
La déclaration de constantes et de variables se
fait au moyen de directives précisant le type des
données. Les formats les plus courants sont :
.equ <nom_constante>,<valeur>
<label>: <directive> <v1>,<v2>, ...,<vn>
Le label correspond à une adresse mémoire qui
sera utilisée pour accéder aux données stockées
en mémoire.
Valeur immédiate : entier, caractère, constante
Architecture des ordinateurs – p. 18
Les directives
.byte : entiers sur un octet (8 bits)
.word : entiers sur un mot (16 bits)
.int : entiers sur un double-mot (32 bits)
.ascii : chaînes de caractère
.asciz : chaînes de caractère terminées par
l’octet 0
.space <rep>,<val> : répète <rep> fois la
valeur <val> codée sur 1 octet
.fill <rep>,<taille>,<val> : répète
<rep> fois la valeur <val> codée sur <taille>
octets
Architecture des ordinateurs – p. 19
Exemples (1)
# une constante
.equ
N,10
# un octet (8 bits)
n:
.byte
12
# un tableau de 5 octets
tab1:
.byte
0,123,24,36,255
# un mot (16 bits)
x:
.word
23456
# un tableau de 3 mots
tab2:
.word
876,1336,65535
Architecture des ordinateurs – p. 20
Exemples (2)
# une chaı̂ne de caractères
ch1:
.ascii "Hello World"
# équivalent à :
ch1:
.ascii "Hello ","World"
# une chaı̂ne se terminant par 0
ch2:
.asciz "Bonjour monde\n"
# équivalent à :
ch3:
.ascii "Bonjour monde\n\0"
Architecture des ordinateurs – p. 21
Exemples (3)
# un tableau de 15 octets initialisés
# à 0
tab3:
.space 15,0
# équivalent à :
ch4:
.fill
15,1,0
# un tableau de 120 caractères
# initialisés à ’x’
tab3:
.space 120,’x’
# un tableau de 200 entiers (double# -mots) initialisés à 100000
ch2:
.fill
200,4,100000
Architecture des ordinateurs – p. 22
Les principales instructions
en assembleur
Architecture des ordinateurs – p. 23
Généralités
La plupart des instructions assembleur (aussi
appelées mnémoniques) ont 0, 1 ou 2
arguments (opérandes).
Ces opérandes sont soit un label (pour les
instructions de saut uniquement), soit un
registre, soit un accès à la mémoire (suivant
les différents modes d’adressage).
En général pour une même instruction, il ne
peut y avoir plus d’un accès à la mémoire.
En général les opérandes doivent être de
même taille (1, 2 ou 4 octets).
Architecture des ordinateurs – p. 24
L’instruction mov (1)
mov <destination>,<source>
Cette instruction sert à faire des transferts
d’information entre :
un registre et une valeur immédiate
la mémoire et une valeur immédiate
deux registres
un registre et la mémoire
la mémoire et un registre
Architecture des ordinateurs – p. 25
Accès à la mémoire : modes d’adressage (1)
Format général :
var[r1*f1+r2*f2+r3*f3+c]
Où :
var est une variable (son label de définition)
r1,r2 et r3 sont des registres (eax, ebx, ecx,
edx, edi, esi, ebp)
f1,f2 et f3 sont des entiers représentant un
facteur d’échelle (val. possibles : 0, 1, 2, 4, 8)
c est une valeur immédiate (entier ou
constante)
Architecture des ordinateurs – p. 26
Accès à la mémoire : modes d’adressage (2)
Lorsqu’on fait un accès à la mémoire, il peut être nécessaire de
préfixer cet accès pour indiquer la taille des données
considérées :
byte ptr var[...] : on considère une donnée sur un octet
(8 bits)
word ptr var[...] : on considère une donnée sur deux
octets (16 bits)
dword ptr var[...] : on considère une donnée sur quatre
octets (32 bits)
Ce ne sera pas nécessaire lorsque le compilateur pourra
déterminer tout seul cette taille (si un autre opérande est un
registre par ex.).
Architecture des ordinateurs – p. 27
Accès à la mémoire : modes d’adressage (3)
Nous allons voir quelques exemples. Soit la
déclaration de variables suivante :
x:
y:
chaine:
tab:
.int
.int
.ascii
.word
345
15
"Hello world"
12,65,43,98,2
Architecture des ordinateurs – p. 28
Accès à la mémoire : modes d’adressage (4)
mov eax,12
Met dans le registre eax la valeur 12 (eax=12).
mov ebx,x
# ou bien (équivalent)
mov ebx,x[0]
Met dans le registre ebx la valeur contenue dans
la variable x, càd 345.
Architecture des ordinateurs – p. 29
Accès à la mémoire : modes d’adressage (5)
mov eax,ebx
Met dans le registre eax la valeur contenue dans
ebx (eax=ebx).
mov al,chaine[4]
Met dans le registre 8 bits al la valeur contenue
dans la cinquième case de la variable chaine
(chaine de caractères=tableau de
caractères=tableau d’octets), càd ’o’.
Architecture des ordinateurs – p. 30
Accès à la mémoire : modes d’adressage (6)
mov ebx,4
mov al,chaine[ebx]
Met dans al la valeur contenue dans la
cinquième case de la variable chaine, càd ’o’.
mov ebx,2
mov al,chaine[ebx*4]
Met dans al la valeur contenue dans la
neuvième case de la variable chaine, càd ’r’.
Architecture des ordinateurs – p. 31
Accès à la mémoire : modes d’adressage (7)
mov eax,4
mov ebx,2
mov al,chaine[eax+ebx*2]
Met dans al la valeur contenue dans la
neuvième case de la variable chaine, càd ’r’.
mov eax,1
mov al,chaine[eax+eax*2+eax*4+1]
Met dans al la valeur contenue dans la
neuvième case de la variable chaine, càd ’r’.
Architecture des ordinateurs – p. 32
Accès à la mémoire : modes d’adressage (8)
mov ax,tab[3*2]
Met dans le registre 16 bits ax la valeur contenue
dans la quatrième case de la variable tab
(tableau d’entiers de 2 octets chacun), càd 98.
mov ebx,2
mov ax,tab[ebx*2]
Met dans le registre 16 bits ax la valeur
contenue dans la troisième case de la variable
tab, càd 43.
Architecture des ordinateurs – p. 33
Accès à la mémoire : modes d’adressage (9)
mov dword ptr y,234
Met dans la variable y la valeur 234. Il est
nécessaire ici de préfixer y, pour indiquer que la
donnée est sur 4 octets.
mov ax,20
mov tab[4],ax
Met dans la troisième case du tableau tab la
valeur 20. Il n’est pas nécessaire de préfixer tab,
car ax est un registre 16 bits.
Architecture des ordinateurs – p. 34
Accès à la mémoire : modes d’adressage (10)
mov al,’A’
mov ebx,3
mov chaine[ebx+1],al
Met dans la cinquième case de la variable
chaine le caractère ’A’. Il n’est pas nécessaire de
préfixer chaine, car al est un registre 8 bits.
# ceci est interdit !!!!!
mov chaine[1],chaine[2]
On est obligé d’utiliser des registres pour faire
des transferts de mémoire avec mov.
Architecture des ordinateurs – p. 35
Intel et les petits indiens. . . (1)
. . . ou la difficile traduction de little endian, par
opposition à big endian, middle endian. . .
Il s’agit de la façon dont les données d’un type
de base sont rangées dans la mémoire. Sur les
processeurs Intel, les octets de poids faible sont
rangés en premier dans la mémoire (dans les
adresses les plus faibles).
Architecture des ordinateurs – p. 36
Intel et les petits indiens. . . (2)
Par exemple :
# variable sur 16 bits
var:
.word
0
# mise à jour de var
mov word ptr var,0x4142
L’octet de poids faible est 0x42 (code ascii de
’B’). Il est rangé dans var[0].
L’octet de poids fort est 0x41 (code ascii de
’A’). Il est rangé dans var[1].
La variable var contient donc la chaîne "BA".
Architecture des ordinateurs – p. 37
Intel et les petits indiens. . . (3)
# variable sur 16 bits
var:
.int
0
# mise à jour de var
mov dword ptr var,0x41424344
# est donc équivalent à
mov byte ptr var[0],0x44
mov byte ptr var[1],0x43
mov byte ptr var[2],0x42
mov byte ptr var[3],0x41
La variable var contient donc la chaîne "DCBA”.
Architecture des ordinateurs – p. 38
Les instructions add et inc (1)
add <destination>,<source>
Cette instruction effectue l’addition des deux
opérandes, et place le résultat dans destination.
Source et destination peuvent être des registres
ou des accès à la mémoire, source peut être
aussi une valeur immédiate.
inc <source>
Cette instruction incrémente (ajoute 1) à source,
qui est obligatoirement un registre.
Architecture des ordinateurs – p. 39
Les instructions add et inc (2)
Exemple :
.data
x:
.int
25
main:
mov
eax,x
mov
ebx,10
add
eax,ebx
# eax contient 35
inc
eax
add
x,eax
# x contient 61
Architecture des ordinateurs – p. 40
Les instructions sub et dec (1)
sub <destination>,<source>
Cette instruction soustrait source de destination,
et place le résultat dans destination. Source et
destination peuvent être des registres ou des
accès à la mémoire, source peut être aussi une
valeur immédiate.
dec <source>
Cette instruction décrémente (enlève 1) à
source, qui est obligatoirement un registre.
Architecture des ordinateurs – p. 41
Les instructions sub et dec (2)
Exemple :
.data
x:
.int
25
main:
mov
eax,x
mov
ebx,10
sub
eax,ebx
# eax contient 15
dec
eax
sub
x,eax
# x contient 11
Architecture des ordinateurs – p. 42
L’instruction mul (1)
mul <source>
Cette instruction multiplie (avec source étant un
registre ou une opérande memoire) :
eax par source si source est sur 32 bits. Le
résultat est dans edx+eax.
ax par source si source est sur 16 bits. Le
résultat est dans dx+ax.
al par source si source est sur 8 bits. Le
résultat est dans ah+al=ax.
Architecture des ordinateurs – p. 43
L’instruction mul (2)
Exemple :
.data
x:
.int
25
main:
mov
eax,x
mov
ebx,10
mul
ebx
# eax contient 250
# edx contient 0
Architecture des ordinateurs – p. 44
L’instruction div (1)
div <source>
Cette instruction divise (avec source étant un
registre ou une opérande memoire) :
edx+eax par source si source est sur 32 bits.
Le quotient est dans eax et le reste dans edx.
ax+dx par source si source est sur 16 bits. Le
quotient est dans ax et le reste dans dx.
ax par source si source est sur 8 bits. Le
quotient est dans al et le reste dans ah.
Architecture des ordinateurs – p. 45
L’instruction div (2)
Exemple :
.data
x:
.int
25
main:
mov
eax,x
mov
edx,0 # essentiel !
mov
ebx,10
div
ebx
# eax contient 2 (quotient)
# edx contient 5 (reste)
Architecture des ordinateurs – p. 46
L’instruction and
and <destination>,<source>
Cette instruction effectue un “et” logique entre
destination et source, et place le résultat dans
destination.
Exemple :
mov
ah,0b11001101
mov
al,0b01100100
and
ah,al
#ah contient 0b01000100
Architecture des ordinateurs – p. 47
L’instruction or
or <destination>,<source>
Cette instruction effectue un “ou” logique entre
destination et source, et place le résultat dans
destination.
Exemple :
mov
ah,0b11001101
mov
al,0b01100100
or
ah,al
#ah contient 0b11101101
Architecture des ordinateurs – p. 48
L’instruction xor
xor <destination>,<source>
Cette instruction effectue un “ou” exclusif entre
destination et source, et place le résultat dans
destination.
Exemple :
mov
ah,0b11001101
mov
al,0b01100100
xor
ah,al
#ah contient 0b10101001
Architecture des ordinateurs – p. 49
L’instruction not
not <source>
Cette instruction effectue un “non” logique sur
destination et place le résultat dans destination.
Exemple :
mov
ah,0b11001101
not
ah
#ah contient 0b00110010
Architecture des ordinateurs – p. 50
L’instruction push
push <source>
Cette instruction place une donnée contenue
dans un registre ou une opérande mémoire sur
la pile et décrémente esp :
de 4 octets si source est un registre 32 bits
(eax, ebx, . . . )
de 2 octets si source est un registre 16 bits
(ax, bx, . . . )
On ne peut pas placer directement sur la pile un
registre 8 bits.
Architecture des ordinateurs – p. 51
L’instruction pusha
pusha
Cette instruction place les registres suivants sur
la pile, dans l’ordre : eax, ecx, edx, ebx, esp
(valeur avant le début de l’instruction), ebp, esi,
edi.
Après l’exécution de l’instruction, esp est donc
décrémenté de 8 × 4 = 32 octets.
Architecture des ordinateurs – p. 52
L’instruction pop
pop <destination>
Cette instruction récupère la donnée placée au
sommet de la pile dans un registre et incrémente
esp :
de 4 octets si destination est un registre 32 bits
(eax, ebx, . . . )
de 2 octets si destination est un registre 16 bits
(ax, bx, . . . )
On ne peut pas retirer directement de la pile une
donnée sur 8 bits.
Architecture des ordinateurs – p. 53
L’instruction popa
popa
Cette instruction restaure le contenu des
registres qui avaient été placés sur la pile par
exemple par l’instruction pusha, dans l’ordre :
esi, edi, ebp, esp, ebx, edx, ebx, eax.
Après l’exécution de l’instruction, esp est donc
incrémenté de 8 × 4 = 32 octets.
Architecture des ordinateurs – p. 54
L’instruction cmp
cmp <destination>,<source>
Cette instruction compare les deux opérandes et
place le résultat de la comparaison dans les bits
CF et ZF du registre eflags (nous étudierons
plus tard en détail ce registre particulier).
Le résultat de cette comparaison sera en
particulier utilisé par les instructions de saut
conditionnel.
Architecture des ordinateurs – p. 55
L’instruction jmp
jmp <label>
Cette instruction effectue un saut vers le label
passé en argument : la prochaine instruction à
exécuter sera celle qui suit ce label.
Le saut est dit inconditionnel car il est effectué
dès que l’instruction jmp est rencontrée.
Architecture des ordinateurs – p. 56
Les instructions jcond
jcond <label>
Ces instructions effectuent un saut conditionnel vers le label
passé en argument, en fonction des valeurs CF et ZF du registre
eflags. Les conditions possibles sont :
je : saut si égalité
jne : saut si différent
jl : saut si inférieur strict
jle : saut si inférieur ou égal
jg : saut si supérieur strict
jge : saut si supérieur ou égal
Architecture des ordinateurs – p. 57
L’instruction loop (1)
loop <label>
Cette instruction décrémente le registre ecx et
effectue un saut vers le label passé en
argument, si la valeur contenue dans ecx est
differente de 0.
Cette instruction permet de simplifier l’écriture
des boucles, en effectuant automatiquement la
décrémentation, la comparaison, et le saut
conditionnel.
Architecture des ordinateurs – p. 58
L’instruction loop (2)
Exemple : la somme des 10 premiers carrés.
mov
ebx,0
# somme
mov
ecx,10 # compteur
mov
edx,0
# pour la mult.
boucle:
mov
eax,ecx
mul
ecx
add
ebx,eax
loop
boucle
# ebx contient la somme
# des 10 premiers carrés
Architecture des ordinateurs – p. 59
Les sous-programmes
Architecture des ordinateurs – p. 60
Deux possibilités
On peut définir deux types de sous-programmes,
les macros et les procédures :
Les macros ne sont pas réellement des
sous-programmes : ils permettent de simplifier
l’écriture d’un programme et disparaissent
après la compilation.
Les procédures utilisent le mécanisme des
procédures/fonctions des langages de haut
niveau au moyen d’instructions du
microprocesseur particulières.
Architecture des ordinateurs – p. 61
Les macros (1)
Le contenu d’une macro est recopié à l’endroit
ou on l’appelle.
La macro peut être placée n’importe où dans
le code, mais doit précéder l’endroit où on
l’appelle.
Le code de la macro disparaît après la
compilation.
Architecture des ordinateurs – p. 62
Les macros (2)
Une macro possède un nom, et accepte des
arguments. Forme générale d’une macro :
.macro nom_macro arg1,arg2,...,argn
.........
Instructions
.........
.endm
Architecture des ordinateurs – p. 63
Les macros (3)
Les arguments peuvent être ce que l’on veut : ce
qui est passé en paramètre sera simplement
recopié dans le code à l’endroit spécifié.
Ainsi, on peut faire passer en paramètre un
registre, une opérande mémoire, voire même
une instruction assembleur !
Attention : dans le corps de la macro, les
paramètres doivent être précédés du
caractère “\”.
Architecture des ordinateurs – p. 64
Les macros (4)
Exemple : une macro qui effectue l’addition de
trois valeurs dans le registre eax.
.macro add3 val1,val2,val3
mov
eax,\val1
add
eax,\val2
add
eax,\val3
.endm
.........
add3
10,15,30
add3
eax,notes[10],45
Architecture des ordinateurs – p. 65
Les macros (5)
Le code sera en réalité remplacé par :
mov
add
add
mov
add
add
eax,10
eax,15
eax,30
eax,eax
eax,notes[10]
eax,45
Architecture des ordinateurs – p. 66
Les macros (6)
Même chose, mais avec un argument
supplémentaire indiquant dans quel registre doit
être placé le résultat :
.macro add3 reg,val1,val2,val3
mov
\reg,\val1
add
\reg,\val2
add
\reg,\val3
.endm
.........
add3
eax,10,15,30
add3
ebx,eax,notes[10],45
Architecture des ordinateurs – p. 67
Les macros (7)
Le code sera en réalité remplacé par :
mov
add
add
mov
add
add
eax,10
eax,15
eax,30
ebx,eax
ebx,notes[10]
ebx,45
Architecture des ordinateurs – p. 68
Les macros (8)
Exemple : additionner le contenu de deux
variables, placer le résultat dans une troisième.
.macro addv var1,var2,var3
push
eax
mov
eax,\var2
add
eax,\var3
mov
\var1,eax
pop
eax
.endm
Architecture des ordinateurs – p. 69
Les macros (9)
.data
x
y
res
.........
addv
.int
.int
.int
4
5
0
res,x,y
est remplacé par :
push
mov
add
mov
pop
eax
eax,x
eax,y
z,eax
eax
Architecture des ordinateurs – p. 70
Les procédures (1)
Les trois composantes essentielles d’une
procédure sont :
Un label définissant le point d’entrée de la
procédure,
L’instruction ret permettant de quitter la
procédure,
L’instruction call permettant de faire un appel
à la procédure.
Architecture des ordinateurs – p. 71
Les procédures (2)
Une procédure peut-être placée n’importe où
dans le code.
Mais attention : si elle est placée au milieu de
lignes de code, par exemple dans la partie
principale, rien n’empêche que l’on y entre. . .
Conseil : placez-les soit au début du
programme (avant le label main :), soit aprés
les instructions de terminaison du programme
(call exit).
Architecture des ordinateurs – p. 72
Les procédures (3)
Squelette d’un appel à une procédure :
main:
.........
call
sous-prog
.........
call
exit
sous_prog:
.........
ret
Architecture des ordinateurs – p. 73
Les procédures (4)
Fonctionnement de l’instruction call :
L’instruction call empile le contenu du
registre eip, qui représente l’adresse de la
prochaine instruction à exécuter.
Elle effectue ensuite un saut inconditionnel
vers le label d’entrée de la procédure.
Architecture des ordinateurs – p. 74
Les procédures (5)
Fonctionnement de l’instruction ret :
L’instruction ret dépile la valeur qui est au
sommet de la pile, qui doit normalement être la
valeur sauvegardée de eip.
Elle effectue un saut inconditionnel vers un
label virtuel placé devant cette instruction.
Architecture des ordinateurs – p. 75
Les procédures : passage des paramètres (1)
Les deux méthodes principales de passage des
arguments sont :
Par la pile : semblable à ce qui a été vu en
C–. C’est la façon classique de faire des
languages de haut niveau.
Par les registres : plus simple à mettre en
oeuvre, mais plus difficile à utiliser. En effet, il
faut prévoir quels registres utiliser pour les
calculs et quels registres utiliser pour les
paramètres.
Architecture des ordinateurs – p. 76
Passage des paramètres par la pile (1)
Le passage des paramètres par la pile s’effectue
de la façon suivante :
Avant l’appel : on empile les paramètres avec
l’instruction push.
Pendant l’appel : on accède aux paramètres
au moyen des registres de pile esp et ebp.
Après l’appel : on dépile les paramètres avec
pop ou en incrémentant le pointeur de pile
esp.
Architecture des ordinateurs – p. 77
Passage des paramètres par la pile (2)
Attention : Lorsqu’on incrémente le registre esp
pour libérer la place occupée sur la pile par les
arguments, on doit indiquer le nombre d’octets
que l’on libère, et non pas le nombre
d’arguments.
Par exemple, si on avait empilé les registres eax,
ebx et ecx, on incrémentera esp de 12 car ces
trois registres font chacun 4 octets.
Si on avait empilé les registres eax, bx et cx, on
incrémentera la pile de 8 car eax fait 4 octets, bx
en fait 2, cx en fait 2.
Architecture des ordinateurs – p. 78
Passage des paramètres par la pile (3)
L’accés aux arguments se fait au moyen des
registres esp et ebp. On prendra soin d’écrire :
sous_prog:
push
ebp
mov
ebp,esp
.........
pop
ebp
ret
afin que si on utilise la pile dans cette procédure,
les arguments soient repérés par le même
déplacement par rapport à ebp.
Architecture des ordinateurs – p. 79
Passage des paramètres par la pile (4)
A l’adresse ebp+4, se trouve le contenu du registre eip
sauvegardé par call.
Le premier paramètre p1 (le dernier a avoir été empilé) se
trouve à l’adresse ebp+8.
Le deuxième paramètre p2 se trouve à l’adresse ebp+12 si p1
est codé sur 4 octets, sinon à l’adresse ebp+10 si p1 est codé
sur deux octets.
Le troisième paramètre p3 se trouve à l’adresse ebp+16 si p1
et p2 sont codés sur 4 octets, sinon à l’adresse ebp+14 si p1
est codé sur deux octets et p2 sur 4 octets (ou inversement),
sinon à l’adresse ebp+12 si p1 et p2 sont codés sur deux
octets.
.........
Architecture des ordinateurs – p. 80
Passage des paramètres par la pile (5)
esp contient l’adresse mémoire du sommet de la
pile. ebp contient l’adresse mémoire du sommet
de la pile au moment où on est entré dans la
procédure.
Les registres esp et ebp contenant des
adresses mémoire, pour accéder aux
paramètres, on doit procéder ainsi :
mov
eax,[ebp+8]
# charge le premier paramètre
# dans eax
Architecture des ordinateurs – p. 81
Passage des paramètres par registres (1)
C’est le mode le plus simple car on n’utilise
pas la pile.
Les paramètres sont simplement stockés dans
les registres avant l’appel du sous-programme.
Ils sont récupérés par le sous-programme
directement dans les registres et
éventuellement modifiés en sortie si besoin.
Inconvénients : nombre limité des regitres et
problème des effets de bord.
Architecture des ordinateurs – p. 82
Passage des paramètres par registres (2)
Squelette d’un appel à une procédure :
main:
.........
mov
eax,4
call
sous_prog
.........
sous_prog:
# dans eax : valeur 4
.........
ret
Architecture des ordinateurs – p. 83
Les variables locales (1)
Comme en C–, on utilise la pile pour créer des
variables locales.
On décrémente esp d’autant d’octets que l’on
a besoin, ou on utilise push pour initialiser les
variables.
On utilise ebp pour accéder aux variables sur
la pile. Attention au déplacement, si on a
sauvegardé des registres pour les restaurer en
sortie du sous-programme.
Architecture des ordinateurs – p. 84
Les variables locales (2)
sous_prog:
push
ebp
mov
ebp,esp
push
eax # sauvegarde eax
push
ebx # sauvegarde ebx
sub
esp,8 # réserve 8 octets
mov
[ebp-12],21 # 1ère var locale
mov
[ebp-16],34 # 2ème var locale
.........
add
esp,8 # libère les vars locales
pop
ebx # récupère ebx
pop
eax # récupère eax
pop
ebp # récupère ebp
ret
Architecture des ordinateurs – p. 85
Les variables locales (3)
34
21
old ebx
old eax
old ebp
old eip
Paramètre 2
Paramètre 1
←−
←−
←−
←−
←−
←−
←−
←−
esp
esp+4
esp+8
esp+12
esp+16
esp+20
esp+24
esp+28
←−
←−
←−
←−
←−
←−
←−
←−
ebp-16
ebp-12
ebp-8
ebp-4
ebp
ebp+4
ebp+8
ebp+12
Architecture des ordinateurs – p. 86
Téléchargement