On dit que l'instruction "congé" est similaire à:
movl %ebp, %esp
popl %ebp
Je comprends le movl %ebp, %esp
part, et qu'il agit pour libérer de la mémoire stockée (comme discuté dans cette question ).
Mais quel est le but du popl %ebp
code?
LEAVE
est l'équivalent de ENTER
. L'instruction ENTER
configure un cadre de pile en poussant d'abord EBP
sur la pile, puis copie ESP
dans EBP
, donc LEAVE
a pour faire le contraire, c'est-à-dire copier EBP
vers ESP
puis restaurer l'ancien EBP
à partir de la pile.
Voir la section nommée APPELS DE PROCÉDURE POUR LES LANGUES STRUCTURÉES EN BLOC dans Manuel du développeur du logiciel Intel Vol 1 si vous voulez en savoir plus sur la façon dont ENTER
et LEAVE
travail.
enter n,0
est exactement équivalent à (et doit être remplacé par)
Push %ebp
mov %esp, %ebp # ebp = esp, mov ebp,esp in Intel syntax
sub $n, %esp # allocate space on the stack. Omit if n=0
leave
est exactement équivalent à
mov %ebp, %esp # esp = ebp, mov esp,ebp in Intel syntax
pop %ebp
enter
est très lent et les compilateurs ne l'utilisent pas, mais leave
est très bien. ( http://agner.org/optimize ). Les compilateurs utilisent leave
s'ils créent un cadre de pile (du moins gcc le fait). Mais si esp
est déjà égal à ebp
, il est plus efficace de simplement pop ebp
.
L'instruction popl
restaure le pointeur de base et l'instruction movl
restaure le pointeur de pile. Le pointeur de base est le bas de la pile et le pointeur de la pile est le haut. Avant l'instruction de congé, la pile ressemble à ceci:
----Bottom of Caller's stack----
...
Caller's
Variables
...
Function Parameters
----Top of Caller's Stack/Bottom of Callee's stack---- (%ebp)
...
Callee's
Variables
...
---Bottom of Callee's stack---- (%esp)
Après le movl %ebp %esp
, qui désalloue la pile de l'appelé, la pile ressemble à ceci:
----Bottom of Caller's stack----
...
Caller's
Variables
...
Function Parameters
----Top of Caller's Stack/Bottom of Callee's stack---- (%ebp) and (%esp)
Après le popl %ebp
, qui restaure la pile de l'appelant, la pile ressemble à ceci:
----Bottom of Caller's stack---- (%ebp)
...
Caller's
Variables
...
Function Parameters
----Top of Caller's Stack---- (%esp)
L'instruction enter
enregistre le bas de la pile de l'appelant et définit le pointeur de base afin que l'appelé puisse allouer sa pile.
Notez également que, si la plupart des compilateurs C allouent la pile de cette façon (au moins avec l'optimisation désactivée), si vous écrivez une fonction de langage d'assemblage, vous pouvez simplement utiliser le même cadre de pile si vous le souhaitez, mais vous devez assurez-vous de pop
tout ce qui se trouve sur la pile que vous Push
dessus, sinon vous passerez à une adresse indésirable à votre retour (c'est parce que call <somewhere>
veux dire Push <ret address>
[ou Push %eip
], jmp <somewhere>
et ret
signifie sauter à l'adresse en haut de la pile [ou pop %eip
]. %eip
est le registre qui contient l'adresse de l'instruction en cours).