Friday, June 11, 2010

Stack Frame Illustration

/**
* IMPORTANT NOTE: This is an illustrative program to show
* some light on stack frame in x86 architecture. According
* to C99 standard the behaviour of this program is undefined,
* and it sure is! Hence don't attempt to do anything similar
* inside your regular program...!!
*
* OUTPUT: This program shows how the function "clobber" finds the
* location of it's caller's first local variable and modifies it!
* In our case it modifies the variable "a" inside function "simple_func"
* purely by walking through the frame pointer stored in stack frame!
*
* The following are the basic requirements for you to understand
* this program
* 1. Enough English knowledge to understand my comments!
* (You may be a litrature graduate in Oxford university but they
* don't teach "my" english there! Do they?? :-D )
*
* 2. Good knowledge in C programming language and usage of pointers
* (If you have questions like, "In your comments you say add address
* by 4 but you add 1 to the pointer why??", then go read a
* good "C" book!)
*
* 3. Good understanding of processors and memory!
* (Atleast x86 varients like 8086, 80386 etc..)
*
* WARNING: This code is tested only on gcc compiler v4.2.4,
* though it might work on others too but I'm not very sure
* about it!! Remember, format of stack frame is highly
* dependent on the compiler that generates the code, so,
* this code is niether portable across architecture nor
* portable across compilers!!
*
* In gcc, this code seems to work with
* 1. Optimization level 0
* 2. Optimization levels 1, 2, s with -fno-omit-frame-pointer
* 3. Architectures x86 (32-bit), x86_64/amd64 (64-bit)
*
* It will fail to run when compiled with/for
* 1. -fomit-frame-pointer (This program highly depends on "ebp"
* register which is indeed the frame
* pointer for x86 architecture)
* 2. Optimization level 3 (At this level gcc doesn't save
* frame pointer of main)
* 3. Architectures other than x86, amd64 (Don't try on
* ARM, MIPS, PPC etc)
*
* EXPERIMENTS TODO:
* 1. Make clobber or simple_func as a static and check what are
* the optimization levels the program works!!
*
* 2. Remove the volatile keyword and check what are the optimization
* levels this program works! (Specifically on borland compilers)
*
* 3. Try to make this program work for optimization level 3
*
* 4. Try to make this program work on other architectures like ARM
*
* 5. Check if it works with microsoft visual studio (Debug & Retail builds)
*
* 6. Check with Borland-32 compiler (with various optimization level)
*
* DISCLIMER: This program is only for illustrative purpose and
* I will not be responsible for any damage it causes to your brain
* or to your computer's! blah... blah...
**/

/* Just to avoid #include <stdio.h> */


extern
int printf(const char *, ...);

/**
* int f2(blah blah)
* {
* ......
* .........
* .......
* }
*
* int f1(blah blah)
* {
* .............
* ..............
* blah = f2(blah blah);
* ..............
* }
*
* A typical stack frame for the above code construct will
* look like something shown below
*
* __ |_________________________________________|
* " | |
* "" | |
* | | Saved register's content |
* | | |
* | | |
* | |-----------------------------------------|
* " | Memory for final local variable |
* Stack frame "" |-----------------------------------------|
* of < | . |
* function f2 "" | . |
* | | . |
* | |-----------------------------------------|
* | | Memory for second local variable | / Starting
* | |-----------------------------------------| <-- | Address of
* "" | Memory for first local variable | \ locar var
* " |-----------------------------------------| <-- Current FP
* | | Saved Frame Pointer (of prev fn.) |------+
* | |-----------------------------------------| |
* | | Return address | |
* _\ |_________________________________________| |
* " | | |
* "" | | |
* | | Saved register's content | |
* | | | |
* | | | |
* | |-----------------------------------------| |
* " | Memory for final local variable | |
* Stack frame "" |-----------------------------------------| |
* of < | . | |
* function f1 "" | . | |
* | | . | |
* | |-----------------------------------------| |
* | | Memory for second local variable | |
* | |-----------------------------------------| |
* "" | Memory for first local variable | |
* " |-----------------------------------------|<-----+
* | | Saved Frame Pointer (of prev fn.) |
* | |-----------------------------------------|
* | | Return address |
* Bottom of Stack---> ""\|_________________________________________|
**/


void
clobber(void)
{

/**
* If you carefully examine the prologue of a function
* it is most likely that you'll see the first
* instruction being saving of frame pointer
* (ebp in our case) to some place (stack in our
* case) so that it can be restored when the funtion
* returns!
*
* In the following code we try to locate the saved
* frame pointer (which will give access to stack area
* of main!!!
**/

/**
* NOTE_01: If you carefully look at the above diagram current
* frame pointer minus sizeof(int) will give you the
* first variable's address. So, if you add sizeof(int)
* to the first varible's address you'll get the current
* frame pointer!! i.e, addrof(x) = fp - 4; (in 32-bit machine)
* that is what I did in the following line!
**/

long
**x = (long **) (&x + 1);

/* At this point content of x is frame pointer of main!!! */

/**
* From NOTE_02 we know that address of 'a' inside main
* will be at (main's fp) - 4!
**/

*(*
x - 1) = 25;
}


int
simple_func(void)
{

/**
* To make sure the compiler allocates a memory
* and not just assign a register we need to say
* our variable of interest as volatile!
*
* TODO: Try to remove type qualifier 'volatile'
* and try to compile this program with various
* optimization levels and check what happens ;-)
**/

volatile
long a = 12;

clobber();
printf ("a = %d\r\n", a);
return
0;
}


/* The entry point */

int
main()
{

return
simple_func();
}

No comments: