La pile un élément essentiel Merci Laurent JEANPIERRE
Contenu du cours Définition Manipulations Appel de fonctions Gestion du résultat Gestion des variables Gestion des paramètres Exemple : Fibonacci Conclusion
Définition de la pile (stack) Structure de données Stratégie LIFO Last In First Out Dernier entré, premier sorti Analogie avec pile d’assiettes. Mémoire de stockage Temporaire Stockage bien parenthésé [({}{}{})()({})][]
La pile en mémoire Zone de mémoire séparée Registre de segment spécifique SS = Stack Segment Registre de position spécifique eSP = (extended) Stack Pointer ss:esp indique la dernière valeur empilée Sur x86, elle croît à l’envers… Sommet_pile ≤ Base_pile
Contenu du cours Définition Manipulations Appel de fonctions Gestion du résultat Gestion des variables Gestion des paramètres Exemple : Fibonacci Conclusion
Manipulations de la pile Trois possibilités : Modification de eSP movl %ebp,%esp # ebp esp addl $4,%esp # esp esp+4 Accès direct à la mémoire (voir cours sur modes d’adressage) movw 4(%esp), %ax # mem[esp+4] ax Instructions spécifiques pushw, pushl popw, popl
Instructions spécifiques PUSH = empiler Met une valeur sur la pile. Le registre esp diminue de la taille spécifiée L’opérande est stockée à l’adresse (%esp) POP = dépiler Retire une valeur de la pile. L’opérande est remplie avec le contenu de l’adresse (%esp) Le registre esp augmente de la taille spécifiée Spécificité : Pas d’opération sur 1 octet (processeur 16 bits)
Exemple de manipulation de pile (1) movl $0,%eax pushl $0x01234567 pushw $0x89AB pushw $0xCDEF movb (%esp),%al popl %ebx movw %ax,1(%esp) popl %eax
Exemple de manipulation de pile (2) movl $0,%eax pushl $0x01234567 pushw $0x89AB Pile esp Registres EAX EBX ESP ******** ???????? 10010 00000000
Exemple de manipulation de pile (2) movl $0,%eax pushl $0x01234567 pushw $0x89AB Pile esp 0123 4567 esp Registres EAX EBX ESP 00000000 ???????? 10010 9610
Exemple de manipulation de pile (2) pushl $0x01234567 pushw $0x89AB pushw $0xCDEF Pile 0123 4567 esp 89AB esp Registres EAX EBX ESP 00000000 ???????? 9610 9410
Exemple de manipulation de pile (2) pushw $0x89AB pushw $0xCDEF movb (esp),%al Pile 0123 4567 89AB esp Registres EAX EBX ESP 00000000 ???????? 9410 CDEF esp 9210
Exemple de manipulation de pile (2) pushw $0xCDEF movb (%esp),%al popl %ebx Pile 0123 4567 89AB Registres EAX EBX ESP 00000000 ???????? 9210 CDEF esp 000000EF
Exemple de manipulation de pile (2) movb (esp),%al popl %ebx movw %ax, 1(%esp) Pile 0123 4567 esp 89AB Registres EAX EBX ESP 000000EF ???????? 9210 CDEF esp 89ABCDEF 9610
Exemple de manipulation de pile (2) popl %ebx movw %ax, 1(%esp) popl %eax Pile 0100 0123 EF67 4567 esp 89AB Registres EAX EBX ESP 000000EF 89ABCDEF 9610 CDEF
Exemple de manipulation de pile (2) popl %ebx movw %ax,1(%esp) popl %eax Pile esp 0100 EF67 esp 89AB Registres EAX EBX ESP 000000EF 89ABCDEF 9610 CDEF 0100EF67 10010
Contenu du cours Définition Manipulations Appel de fonctions Gestion du résultat Gestion des variables Gestion des paramètres Exemple : Fibonacci Conclusion
Appel de fonctions Programme structuré fonctions Exemple : Fonction déplacer(Object o) Soulever(o) SeDéplacer() Poser(o) Fin Fonctions blocs bien parenthésés Utilisation naturelle de la pile
Appel de fonctions en assembleur Instruction : call <label> Empile EIP (compteur ordinal) EIP label La fonction s’exécute… Instruction : ret Dépile EIP Le programme principal reprend…
Contenu du cours Définition Manipulations Appel de fonctions Gestion du résultat Gestion des variables Gestion des paramètres Exemple : Fibonacci Conclusion
Le résultat de la fonction (1) Convention du langage C : Le résultat est renvoyé dans EAX Parfait pour des entiers ou des adresses Inadapté pour le reste Le résultat est stocké temporairement Dans une pseudo-variable locale Dans la dernière variable allouée
Le résultat de la fonction (2) Valeurs plus petites (char, short) Extension à 32 bits. Attention au signe ! (unsigned char ≠ char) Valeurs plus grandes Par adresse (pointeur dans eax) Cas des structures très délicat. Valeurs flottantes (float, single, double) Dans st(0) (pile FPU, voir cours sur la FPU)
Contenu du cours Définition Manipulations Appel de fonctions Gestion du résultat Gestion des variables Gestion des paramètres Exemple : Fibonacci Conclusion
Les variables Une fonction peut avoir des variables… int main() { int a = 3; int b = a+5; } A et B sont locales à la fonction main Comment les mémoriser ? Sur le tas Sur la pile
Variables globales : le tas .data a: .long 3 b: .long 0 .text main: movl (a), %eax addl $5, %eax movl %eax, (b) ret Très bien pour les variables globales… Mais pour les fonctions récursives ?
Fonctions récursives Définition : Une fonction est dite « récursive » si elle s’appelle elle-même, ou si elle utilise une autre fonction qui l’appelle (la 1ère). Exemple : la suite de fibonacci Fibo(0) = 0 Fibo(1) = 1 Fibo(n) = Fibo(n-1) + Fibo(n-2) n≥2 0 1 1 2 3 5 8 13 21 34 55 89 …
Exemple2 : Fibonacci int Fibo(int n) { int a,b; if (n==0) return 0; a = Fibo(n-1); b = Fibo(n-2); return a+b; } Chaque appel de Fibo a des valeurs de a et de b différentes… Impossible donc de les stocker en mémoire…
Variables locales : la pile La pile offre une mémoire Contextuelle Selon des blocs parenthésés Il suffit d’y stocker les variables locales Problème : ESP varie sans cesse… Comment retrouver les variables ? Solution : Le cadre de pile
Cadre de pile (ébauche) Utilisation du registre EBP : Mémorise la base de la pile pour la fonction active Accès direct aux variables via EBP Un registre EBP pour chaque fonction Sauvegarde de la valeur précédente… Sur la pile ! Chaque fonction commence donc par : Sauvegarde EBP Allocation du cadre de pile
Contenu du cours Définition Manipulations Appel de fonctions Gestion du résultat Gestion des variables Gestion des paramètres Exemple : Fibonacci Conclusion
Les paramètres des fonctions Une fonction peut avoir des paramètres printf ("hello %s!","World"); Un paramètre = une variable initialisée… Géré comme une variable locale Mais Initialisé par l’appelant A ce moment, pas encore de cadre de pile Position particulière dans le FUTUR cadre de pile
Cadre de pile (complet) Le cadre de pile contient donc : Paramètres de fonction Par l’appelant Adresse de retour Automatique (call) Sauvegarde EBP pushl %ebp movl %esp, %ebp Variables Locales subl $taille,%esp Paramètres (taille ?) @ retour (32b) Svg EBP (32b) ebp Var. Locales (taille ?) esp
Note spécifique 386 et plus… La pile est alignée sur 32 bits… Convention logicielle de gcc (et autres…) Pas de justification matérielle Tout paramètre prend n*32 bits ! int/void* 32 bits ok char/short 8/16 bits problème extension du signe ou bourrage si non signé création de variables locales de la bonne taille float/single 32 bits ok double 64 bits ok On ne s’intéressera qu’aux multiples de 32 bits Reste Pas au programme
Contenu du cours Définition Manipulations Appel de fonctions Gestion du résultat Gestion des variables Gestion des paramètres Exemple : Fibonacci Conclusion
Fibonacci, le résultat final (1) int fibo(int n) Locales : int a,b,r; _fibo: # sauvegarde EBP pushl %ebp # EBP <- ESP movl %esp, %ebp # Alloue 16 octets # sur la pile subl $16, %esp n (int) Paramètres @retour Contexte EBP0 ebp a (int) Variables locales b (int) r (int) * (32b) Spécial esp
Fibonacci, le résultat final (2) # if n==0 cmpl $0, 8(%ebp) # compare 0 et n (EBP+8) jne L10 # saute si différent movl $0, -12(%ebp) # mets 0 dans r (EBP-12) jmp L9 # saute à fin L10: # if n==1 cmpl $1, 8(%ebp) # compare 1 et n (EBP+8) jne L11 # saute si différent movl $1, -12(%ebp) # mets 1 dans r (EBP-12) L11:
Fibonacci, le résultat final (3) # a = fibo(n-1) movl 8(%ebp), %eax # eax <- n (EBP+8) decl %eax # eax <- eax-1 movl %eax, (%esp) # (ESP+0) <- eax # {Paramètre1 <- n-1} call _fibo # appelle fibo # {résultat dans eax} movl %eax, -4(%ebp) # mets eax dans a (EBP-4) # b = fibo(n-2) subl $2, %eax # eax <- eax-2 movl %eax, (%esp) # (ESP+0) <- eax # {Paramètre1 = n-2} call _fibo # appelle fibo {reseax} movl %eax, -8(%ebp) # mets eax dans b (EBP-8)
Fibonacci, le résultat final (4) # r = a+b movl -8(%ebp), %eax # eax <- b (EBP-8) addl -4(%ebp), %eax # eax <- eax + a (EBP-4) movl %eax, -12(%ebp) # mets eax dans r (EBP-12) L9: movl -12(%ebp), %eax # eax <- r=(EBP+12) movl %ebp, %esp # %esp <- %ebp popl %ebp # Restaure ebp ret # fin Ou encore… (autre solution) leave # Restaure le cadre de pile
Contenu du cours Définition Manipulations Appel de fonctions Gestion du résultat Gestion des variables Gestion des paramètres Exemple : Fibonacci Conclusion
Conclusion La pile est un élément essentiel Sur TOUS les processeurs Du plus petit (microcontrôleur spécialisé) Au plus gros (serveur de calculs) Gestion identique Empile, Dépile, Registre « pointeur de pile » Notion de cadre de pile (avec des variantes) Maîtriser la pile (et le tas) permet de Comprendre les « segmentation faults » Eviter les « core dump »