enter
also requires a number, which is the space for allocation, which is the key to your question: these instructions are for setting the location for your local function.
Local variables are passed 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 it is not very important)
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 me break it down into each part:
0: 55 push ebp 1: 8b ec mov ebp,esp 3: 83 ec 04 sub esp,0x4
This is a prolog function, setting up ebp. sub esp, 0x4
deleted the 4 byte stack - this frees up space for our local variable int a
4 bytes long.
The enter
instruction is rarely used, but I believe that enter 4,0
does the same thing - introduces a function with 4 bytes of the local variable space. edit: Another 0 is the nesting level, I have never seen it used ... input is usually slower than just following the steps themselves, as the compiler does. / Edit
6: b8 08 00 00 00 mov eax,0x8 b: 89 45 fc mov DWORD PTR [ebp-0x4],eax
This is line a=8
- the second line stores the value in the local memory of variables.
e: 01 45 fc add DWORD PTR [ebp-0x4],eax
Then add to it in a+=8
(the compiler reused eax here, as it recognized that the number is the same ...)
After that, it calls printf, pushing its arguments onto the stack, then zeros the eax register ( xor eax, eax
), since D returns 0 from the 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 add esp, 0x8
is part of the printf call here: the caller is responsible for clearing the arguments after the function call. This is necessary because only the caller knows how many arguments he sent - this is what the arguments to the printf variable allow.
In any case, finally, we clear the local variables and return from the function:
22: c9 leave 23: c3 ret
edit: leave
btw expands to mov esp, ebp; pop ebp;
mov esp, ebp; pop ebp;
- this is exactly the opposite of the installation instructions, and, as Aki Suikonen said in another answer, the good thing here is that the stack is restored as it was with the input function, regardless of what happened inside the function (well, if the function did not completely destroy the contents stack, in which case your program will most likely crash soon) ./ Change
So, on the 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 will not step on other functions (it is on the call stack), but moves it to ebp so that your locals stay at a constant offset throughout the function - variable a
ALWAYS [EBP-4] in this function, even when the stack is being manipulated.
The easiest way to see it in action is by examining the function you are writing in C or something like what we did here. The linux command objdump -d -M intel somefile.o
is what I used (then I manually fixed some minor things to make it more readable. If you break the .o file, all the library calls have not been resolved yet, so it can look weird, but itโs not, t affect local variables!)