Reputation: 157
I have two questions about the EBP register.
I understand ESP and EIP. However, I don't really understand why one would use EBP.
In the code below, I push the EBP register (which is actually 0000000) to the stack. I then move the memory address of the stack to EBP so that ESP and EBP have the same data. This is the prolog. The there is some code which finishes with the syscall. Then I do the reverse (the epilog) as 'leave' indicates that I move EBP to ESP (these values are the same thanks to the prolog) then pop the last value of the stack (which is EBP which is 00000000) to EBP. This gives EBP the same value as happened before the prolog.
Why would anyone do this? What is the point? Please answer in a simple way! Remember that I do not grasp what EBP (the frame pointer) actually does.
EDIT: or is it that this is a way to effectively backup the stack (ESP) when in a function? In other words: the program can do what it does with the stack and the 'original stack' will always be there in EBP. Then when the program finishes, EBP is put back to how it was before. Is this correct? If so, the epilog is just a tidying up routine?
Also, AIUI, I can use 'enter' to replace 'push ebp / mov ebp, esp'. Yet when I try to compile in nasm, I get 'error: invalid combination of opcode and operands' 'leave' works fine; 'enter' does not. What is the correct syntax?
Thanks!
Example:
push ebp
mov, ebp, esp
[some code here]
int 0x80
leave
ret
Upvotes: 4
Views: 1156
Reputation: 111
The idea of EBP is indeed to form a fixed point of reference. Often you may fiddle about with the stack pointer (e.g. while pushing parameters onto the stack ready for a call) and find it a real pain to figure out where some piece of data is relative to the stack pointer. But relative to the base pointer it is always the same. Modern compilers have no difficulty working this out, but if you wanted to write a big piece of assembler code (by hand) that uses the stack for pushing and popping, you would find it easier to reference your local variables relative to a register (EBP) that does not change.
Upvotes: 1
Reputation: 25595
enter
also needs a number which is the amount of space to allocate, which is the key to your question: these instructions are made to set up space for your function's local variables.
Local variables are referred to through the EBP register. Let me show you an example:
import core.stdc.stdio;
void main() {
int a = 8;
a += 8;
printf("%d\n", 8);
}
(This is D code but that's not really relevant)
Disassembly of section .text._Dmain:
00000000 <_Dmain>:
0: 55 push ebp
1: 8b ec mov ebp,esp
3: 83 ec 04 sub esp,0x4
6: b8 08 00 00 00 mov eax,0x8
b: 89 45 fc mov DWORD PTR [ebp-0x4],eax
e: 01 45 fc add DWORD PTR [ebp-0x4],eax
11: 50 push eax
12: b9 00 00 00 00 mov ecx,"%d\n"
17: 51 push ecx
18: e8 fc ff ff ff call printf
1d: 31 c0 xor eax,eax
1f: 83 c4 08 add esp,0x8
22: c9 leave
23: c3 ret
Let's break this up into each part:
0: 55 push ebp
1: 8b ec mov ebp,esp
3: 83 ec 04 sub esp,0x4
This is the function prolog, setting up ebp. The sub esp, 0x4
pushed the stack 4 bytes away - this makes room for our local int a
variable, which is 4 bytes long.
The enter
instruction is rarely used, but I believe enter 4,0
does this same thing - enter a function with 4 bytes of local variable space. edit: The other 0 is the nesting level, I've never seen it used... enter is generally slower than just doing the steps yourself as the compiler does here. /edit
6: b8 08 00 00 00 mov eax,0x8
b: 89 45 fc mov DWORD PTR [ebp-0x4],eax
This is the a=8
line - the second line stores the value in the local variable's memory.
e: 01 45 fc add DWORD PTR [ebp-0x4],eax
Then, we add to it in a+=8
(the compiler reused eax here since it recognized that the
number is the same...)
After that, it calls printf by pushing its arguments to the stack, then zeroes out the eax register (xor eax, eax
), which is how D returns 0 from a function.
11: 50 push eax
12: b9 00 00 00 00 mov ecx,"%d\n"
17: 51 push ecx
18: e8 fc ff ff ff call printf
1d: 31 c0 xor eax,eax
1f: 83 c4 08 add esp,0x8
Note that the add esp, 0x8
here is part of the call to printf: the caller is responsible for cleaning up the arguments after calling the function. This is needed because only the caller knows how many args it actually sent - this is what enables printf's variable arguments.
Anyway, finally, we clean up our local variables and return from the function:
22: c9 leave
23: c3 ret
edit: leave
btw expands to mov esp, ebp; pop ebp;
- it is exactly the opposite of the setup instructions, and like Aki Suihkonen said in the other answer, a nice thing here is the stack is restored to how it was at function entrance, no matter what happened inside the function (well, unless the function totally destroyed the stack's contents, in which case your program will most likely soon crash). /edit
So, bottom line, the ebp stuff is all about your local variables. It uses esp to get started so it has a nice memory space that won't step on other functions (it is on the call stack), but moves it to ebp so your locals remain at a consistent offset throughout the function - the variable a
is ALWAYS [EBP-4] in this function, even as the stack is manipulated.
It is easiest to see in action by disassembling a function you write in C or something, like we did here. The linux command objdump -d -M intel somefile.o
is what I used (then I manually fixed up some minor things to make it more readable. If you disassemble a .o file not all library calls are resolved yet so it can look kinda weird, but that doesn't affect the local variable stuff!)
Upvotes: 0
Reputation: 20027
EBP
forms a fixed point of reference to variables in stack: mainly all parameters to a function, all local parameters of the function and finally the return address. With this fixed point a function can grow/alter it's stack almost randomly, jump to the function epilogue from where ever and restore the stack pointer to the original position.
The concept was next to mandatory, since original 8086 code didn't allow the stack pointer to be used with displacement as in mov ax, [sp + 10]
, but only with push
and pop
. Reference to anything else but the top element needed to be done with mov xx, [bp + 10]
.
Upvotes: 1