Reputation: 619
It's said that the "leave" instruction is similar to:
movl %ebp, %esp
popl %ebp
I understand the movl %ebp, %esp
part, and that it acts to release stored up memory (as discussed in this question).
But what is the purpose of the popl %ebp
code?
Upvotes: 62
Views: 115961
Reputation: 523
The popl
instruction restores the base pointer, and the movl
instruction restores the stack pointer. The base pointer is the bottom of the stack, and the stack pointer is the top. Before the leave instruction, the stack looks like this:
----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)
After the movl %ebp %esp
, which deallocates the callee's stack, the stack looks like this:
----Bottom of Caller's stack----
...
Caller's
Variables
...
Function Parameters
----Top of Caller's Stack/Bottom of Callee's stack---- (%ebp) and (%esp)
After the popl %ebp
, which restores the caller's stack, the stack looks like this:
----Bottom of Caller's stack---- (%ebp)
...
Caller's
Variables
...
Function Parameters
----Top of Caller's Stack---- (%esp)
The enter
instruction saves the bottom of the caller's stack and sets the base pointer so that the callee can allocate their stack.
Also note, that, while most C compilers allocate the stack this way(at least with optimization turn'd off), if you write an assembly language function, you can just use the same stack frame if you want to, but you have to be sure to pop
everything off the stack that you push
on it or else you'll jump to a junk address when you return(this is because call <somewhere>
means push <ret address>
[or push %eip
], jmp <somewhere>
, and ret
means jump to the address on the top of the stack[or pop %eip
]. %eip
is the register that holds the address of the current instruction).
Upvotes: 9
Reputation: 58487
LEAVE
is the counterpart to ENTER
. The ENTER
instruction sets up a stack frame by first pushing EBP
onto the stack and then copies ESP
into EBP
, so LEAVE
has to do the opposite, i.e. copy EBP
to ESP
and then restore the old EBP
from the stack.
See the section named PROCEDURE CALLS FOR BLOCK-STRUCTURED LANGUAGES in Intel's Software Developer's Manual Vol 1 if you want to read more about how ENTER
and LEAVE
work.
enter n,0
is exactly equivalent to (and should be replaced with)
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
is exactly equivalent to
mov %ebp, %esp # esp = ebp, mov esp,ebp in Intel syntax
pop %ebp
enter
is very slow and compilers don't use it, but leave
is fine. (http://agner.org/optimize). Compilers do use leave
if they make a stack frame at all (at least gcc does). But if esp
is already equal to ebp
, it's most efficient to just pop ebp
.
Upvotes: 114