37
votes

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?

2
It pops ebp .user253751
I'm voting to close this because it's confusing what's being asked. You specifically ask about pop %ebp and that has a meaning outside of LEAVE making this question very compound. Your question on LEAVE is also a definite dupe. So we need to know what exactly is the question here and to keep it atomic.Evan Carroll
Interestingly this is almost a direct dupe of this question stackoverflow.com/questions/41907672/…Evan Carroll

2 Answers

70
votes

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.

5
votes

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).