Quel est l'effet de ces deux instructions sur le code d'assemblage généré par gcc pour les machines x86:
Push %ebp
movl %esp, %ebp
l’explication de undind est la vérité littérale (malgré une erreur de direction mineure), mais n’explique pas pourquoi.
%ebp
est le "pointeur de base" de votre cadre de pile. C'est le pointeur utilisé par le runtime C pour accéder aux variables et paramètres locaux de la pile. Voici quelques exemples de code de prologue de fonction générés par GCC (g ++ pour être précis) Commençons par la source C++.
// junk.c++
int addtwo(int a)
{
int x = 2;
return a + x;
}
Cela génère l'assembleur suivant.
.file "junk.c++"
.text
.globl _Z6addtwoi
.type _Z6addtwoi, @function
_Z6addtwoi:
.LFB2:
pushl %ebp
.LCFI0:
movl %esp, %ebp
.LCFI1:
subl $16, %esp
.LCFI2:
movl $2, -4(%ebp)
movl -4(%ebp), %edx
movl 8(%ebp), %eax
addl %edx, %eax
leave
ret
.LFE2:
.size _Z6addtwoi, .-_Z6addtwoi
.ident "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
.section .note.GNU-stack,"",@progbits
Maintenant, pour expliquer ce code de prologue (tout ce qui précède .LCFI2:
), d’abord:
pushl %ebp
stocke le cadre de pile de la fonction appelant sur la pile.movl %esp, %ebp
prend le pointeur de pile actuel et l'utilise comme cadre pour la fonction appelée .subl $16, %esp
laisse de la place pour les variables locales.Maintenant, votre fonction est prête pour les affaires. Toutes les références avec un décalage négatif du registre %ebp%
sont vos variables locales (x
dans cet exemple). Toutes les références avec un décalage positif du registre %ebp%
sont vos paramètres transmis.
Le dernier point d’intérêt est l’instruction leave
, qui est une instruction assembleur x86 qui permet de restaurer le cadre de pile de la fonction appelante. Ceci est généralement optimisé dans les séquences plus rapides move %ebp %esp
et pop %ebp%
en code C. Cependant, à des fins d'illustration, je n'ai pas compilé avec aucune optimisation.
C'est le code typique que vous voyez au début d'une fonction.
Il enregistre le contenu du registre EBP sur la pile, puis stocke le contenu du pointeur de pile actuel dans EBP.
La pile est utilisée lors d’un appel de fonction pour stocker les arguments locaux. Mais dans la fonction, le pointeur de pile peut changer car des valeurs sont stockées dans la pile.
Si vous enregistrez la valeur d'origine de la pile, vous pouvez vous référer aux arguments stockés via le registre EBP, tout en utilisant (ajouter des valeurs à) la pile.
A la fin de la fonction, vous verrez probablement la commande
pop %ebp ; restore original value
ret ; return
Push %ebp
Cela poussera le registre du pointeur de base 32 bits (étendu) sur la pile, c'est-à-dire que le pointeur de la pile (% esp) sera soustrait de quatre, puis la valeur de% ebp sera copiée à l'emplacement indiqué par le pointeur de la pile.
movl %esp, %ebp
Ceci copie le registre de pointeur de pile dans le registre de pointeur de base.
Le but de la copie du pointeur de pile sur le pointeur de base est de créer un cadre de pile, c’est-à-dire une zone de la pile où un sous-programme peut stocker des données locales. Le code dans le sous-programme utiliserait le pointeur de base pour référencer les données.
Cela fait partie de ce qu'on appelle le prologue function .
Il enregistre le pointeur de base actuel à récupérer à la fin de la fonction et définit le nouveau PBP au début du nouveau cadre.
Je pense aussi qu’il est important de noter que souvent après Push %ebp
et movl %esp, %ebp
L’assemblée aura le Push %ebx
ou le Push %edx
. Ce sont des sauvegardes d’appelants des registres %ebx
et %edx
. À la fin de l'appel de routine, les registres seront restaurés avec leurs valeurs d'origine.
Aussi - %ebx, %esi, %edi
sont tous des registres de sauvegarde appelés. Donc, si vous voulez les écraser, vous devez d'abord les enregistrer, puis les restaurer.
Le morceau de code configure la pile pour votre programme.
En x86, les informations de pile sont conservées par deux registres.
Base pointer (bp): Holds starting address of the stack
Stack pointer (sp): Holds the address in which next value will be stored
Ces registres ont des noms différents selon les modes:
Base pointer Stack pointer
Mode réel 16 bits: bp sp
Mode protégé 32 bits: ebp (% ebp) esp (% esp)
Mode 64 bits: rbp rsp
</ Code>
Lorsque vous configurez une pile, le pointeur de pile et le pointeur de base obtiennent la même adresse au début.
Maintenant pour expliquer votre code,
Push %ebp
Ce code pousse l'adresse de la pile actuelle dans la pile afin que la fonction puisse "quitter" ou "revenir" correctement.
movl %esp, %ebp
Ce code configure la pile pour votre fonction.
Pour plus d’informations, référez-vous à cette question .
J'espère que cette aide!