Yet another guide on stack frames

How a function like this:

int main() {
return 0;
}

is implemented in assembly. Functions work inside of a stack by storing all it’s local variables in it. In order to do this, it sets up something called a stack frame where the base of the stack is saved and then it moves that base pointer of the stack to point at the top of the stack; thus giving the illusion that this function has it’s very own new empty stack to work with.
So the above code of int main returning a 0 would look like this in assembly:

push ebp      ;Save bottom of stack
mov ebp,esp   ;Start new stack at the top
mov eax,0     ;Return value stored in eax
mov esp,ebp   ;Restore old top of stack
pop ebp       ;Restore old bottom of stack
ret           ;Return from function call

Here’s another example, where we actually store and use variables in a function:

//C Code
void whatever() {
int a=1;
int b=58;
int c=2034;
}
;Assembly code
push ebp        ;Save bottom of stack
mov ebp,esp     ;Start new stack on top of old stack
sub esp,12      ;Allocate 12 bytes for variables (int=4 bytes)
mov [ebp-4],1   ;a=1
mov [ebp-8],58   ;b=58
mov [ebp-12],2034 ;c=2034
mov esp,ebp     ;Restore esp
pop ebp         ;Restore ebp
ret             ;Return from function

The main points of that is that I use esp to point to the offset total of it’s difference from the value ebp, thus giving me how much data is allocated to the stack. From there, I use ebp as a reference to address areas on the stack so I can manipulate that area of memory to change the values of the variables. Every address that’s less than ebp is (no more less than the value of esp) where the function’s local variables are stored. After that, I restore esp by giving it the value of the bottom of the stack, which is the top of the old stack below that. Then I pop ebp to restore the bottom of the old stack, then return from function.

Here’s a nice visualization of the stack when executing that above code:
* push ebp

+---+
| & |
+---+ 
^   ^
|   |
|   +---esp
+--ebp

mov ebp,esp

+---+
| & |
+---+ 
    ^
    |
    +---esp & esp

sub esp,12

+---+---+---+---+---+---+---+---+---+---+---+---+---+
| & |   |   |   |   |   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+---+---+---+---+
    ^                                               ^
   ebp                                             esp

mov [ebp-4],1

+---+---+---+---+---+---+---+---+---+---+---+---+---+
| & | 01| 00| 00| 00|   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+---+---+---+---+
    ^                                               ^
   ebp                                             esp

mov [ebp-8],58

+---+---+---+---+---+---+---+---+---+---+---+---+---+
| & | 01| 00| 00| 00| 58| 00| 00| 00|   |   |   |   |
+---+---+---+---+---+---+---+---+---+---+---+---+---+
    ^                                               ^
   ebp                                             esp

mov [ebp-12],2034

+---+---+---+---+---+---+---+---+---+---+---+---+---+
| & | 01| 00| 00| 00| 58| 00| 00| 00| 34| 20| 00| 00|
+---+---+---+---+---+---+---+---+---+---+---+---+---+
    ^                                               ^
   ebp                                             esp

Stack frames are used for creating an area to store local variables in a function. This is how many programming languages set up functions.

+-----------------+ -+
|                 |  |
| Local Variables |  |
|                 |  |-----Some other function
+-----------------+  |
| Return Address  |  |
+-----------------+  |
| Function Args   | -+
+-----------------+ 
| Local Variables | -+
|                 |  |
+-----------------+  +-----Main Function
| Return Address  |  |
+-----------------+  |
| Function args   |  |
+-----------------+ -+

From the caller, you first push your arguments to the stack. Then you call the address of the function which will push the address of the next instruction to the stack (the return address) and then jump to the specified address. So after that, the stack should look like this so far:

+-----------------+
| Return Address  |
+-----------------+
| Function args   |
+-----------------+

Then at the sub-procedure, you save BP and you would actually “create a new stack” by having the bottom of the stack (BP – Base Pointer) point to the top of the stack:

New simulated empty stack
for current function
                    
+-----------------+ <--- BP = new stack frame base;         
| Saved BP Addr   |      SP = top of old stack frame
+-----------------+         = bottom of new stack frame;
| Return Address  |
+-----------------+
| Function args   |
+-----------------+

Everything below the base of the new stack frame are variables belonging to other functions, with the exception of the first 3 elements being the Saved BP, the return address, and the function arguments. These elements can be addressed by BP + the offset number of the element. Everything above BP in the new stack frame are the function’s local variables, these variables can be addressed by BP – the offset number of the element.

Here’s a simple example in C:

void main() {
char c;
c=0x69;
}

In assembly, that would translate to:

call main
mov ah,4ch         ;Index of exit function
int 21h            ;Execute exit function
main:
push bp            ;Save bottom of stack
mov bp,sp          ;Start new stack at the top
sub sp,0001        ;Allocate 1 byte (char) of space
mov [bp-0001],69h  ;c = 0x69
mov sp,bp          ;restore sp
pop bp             ;restore bp
ret                ;return from main()

Check out this example in C:

void useless(short i) {
char c;
c=0x24;
}

void main() {
short i;
i=0x1234;
useless(i);
}

Pretty useless code, in assembly (fasm) it translates to:

call main
mov ah,4ch                 ;Index of exit function
int 21h                    ;Execute exit function
main:
push bp                    ;Save bottom of current stack
mov bp,sp                  ;Create new stack at top
sub sp,0002                ;Allocate space for (short i)
mov word [bp-0002],1234h   ;i=0x1234
mov dx,word [bp-0002]      ;get value of i
push dx                    ;push argument
call useless               ;and call useless function
mov sp,bp                  ;restore old sp
pop bp                     ;restore old bp
ret                        ;end of main()
useless:
push bp                    
mov bp,sp                  
sub sp,0003                ;Allocate space for (short i) and (char c)
mov dx,word [bp+0004]      ;Get argument i (bp+0=saved bp, bp+2=return address)
mov word [bp-0002],dx      ;argument i becomes saved as local variable
mov word [bp-0003],0024h   ;char c=0x24
mov sp,bp
pop bp
ret

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: