When you call a function in C, you need to ensure mainly 5 things:
In C, first 4 features are implemented using stack.
Salient Features of Stack:
1. It grows downwards in memory. That is the top of the stack moves down with each push to the stack.
_______________ <---- Bottom of the stack (fixed) | | Higher memory address | | | | |_______________|<---- Top of the stack (moving down) | Lower memory address | V Grows downwards in memory
2. Top of stack (pointer to the last pushed vlaue) is stored in the register ESP
3. Only operations allowed are push and pop.
Thus, **ESP alone represents the current status of the stack.
By convention, procedures will take their parameters from stack. If a procedure returns a value that will fit in a register, it will be returned in AL, AX, EAX, depending on its size. A procedure will in general have local variables on stack as well.
A standard method of accessing both local variables and parameters is to "mark" a place on the stack, and then address both parameters and local variables by their offsets from the "mark". This mark or reference point is called "FRAME POINTER" , because all the information that is pushed into the stack at the time of procedure call is called a frame for that procedure.
Frame Pointer (FP) for the running procedure is always stored in EBP.
1. Push the parameters in the reverse order (right to left).
2. "Call" function now. It implicitly pushes the return address into STACK.
[[ call func ]] ------ Now enters the called procedure ------
3. Push the current EBP ie. Frame Pointer (FP) of calling function into stack. We need to do this to continue with the execution of calling function after return from the called function.
[[ pushl %ebp ]]
4. Copy the ESP to EBP. (yes, this location will be new FRAME POINTER)
[[ movl %esp, %ebp ]]
5. Allocate space on stack for local variables. It's done by decrementing ESP.
[[ subl $4, %esp ]] ------ Do some processing ------
6. Put the value to be returned in EAX.
----- Start unwinding STACK ------
7. Copy current EBP to ESP, it will reduce stack's size. Now we have old FP at the top of the stack.
[[ movl %ebp, %esp]]
8. Pop a 32 bit value (which is old frame pointer) and stuff it into EBP. (undoing Step 3)
[[ popl %ebp ]]
9. The "ret" instruction pops a 32 bit value from the stack and stuffs into the program counter.
[[ ret ]]
** steps 7 and 8 are combined in single instruction "leave".
Example C Code:
static int a = 8; /*Static Storage*/ extern c; static int func1 (int x) { int t = 8; /* Local Variable */ return (x+t); /* Return */ } int main() { int b = 0; b = func1(a); c = b; return(0); }
Corresponding Assembly Code (Only relevant part shown):
a: .long 8 <-- Static variable a func1: pushl %ebp <-- Step 3, Push EBP movl %esp, %ebp <-- Step 4, Copy ESP -> EBP subl $4, %esp <-- Step 5, Create space on stack for t movl $8, -4(%ebp) <-- Initialize "t" to 8 movl -4(%ebp), %eax <-- Step 6, Copy "t" to EAX addl 8(%ebp), %eax <-- Step 6, Add "c" to EAX leave <-- Step 7 and 8: 7: Restore ESP (EBP -> ESP) 8: Restore EBP (Pop STACK -> EBP) ret <-- Step 9, (Pop STACK -> Program Counter) main: ......... pushl a <-- Step 1, push parameters call func1 <-- Step 2, call func1 addl $16, %esp movl %eax, -4(%ebp) movl -4(%ebp), %eax movl %eax, c .........
During execution of func1, STACK looks like:
______________ <---- Bottom of the stack (fixed) | | | main | |_______________| func1 frame --> | a |<---- Parameters pushed starts here |_______________| | RET Addr |<---- Return address pushed by "call" |_______________| Frame Pointer-->| EBP |<---- FP for main pushed by func1 for func1 |_______________| Copy ESP to EBP | t |<---- Local variable "t" |_______________| | V
From the figure, you can make out that to refer to parameters we have to add 8 offset to EBP ie. 8(%ebp). And to refer to local variable, we'll say -4(%ebp).
**Remember, ESP points to the last item pushed, so 0(%ebp) will give us FP of main.
Author: Manu Garg
https://www.manugarg.com
manugarg at gmail dot com
Online URL of article - https://manugarg.googlepages.com/stack.txt
Other articles of mine published on aplawrence.com:
https://aplawrence.com/Unixart/backup_rsync.html
https://aplawrence.com/Unixart/socket-programming.html
Got something to add? Send me email.
More Articles by Manu Garg © 2011-06-26 Manu Garg
There's no obfuscated Perl contest because it's pointless. (Jeff Polk )
Sat Jun 25 09:47:31 2011: 9589 venkat
Missing information
how parameters that are pushed on to the stack are removed ?
Sat Jun 25 09:58:42 2011: 9590 TonyLawrence
When you return from a function, the stack is popped.
Sun Jun 26 00:55:50 2011: 9591 BigDumbDinosaur
Something that may not have been clear in this article is that, as a rule, the stack acrobatics performed as part of executing a C language function and returning from it are not something the average programmer needs to be concerned about. The whole purpose of using C instead of assembly language is to avoid having to micromanage the system and worry about where each byte is going. However, it's useful to know something about how this stuff works, if for no other reason that to try to figure out why a function call returns garbage or generates a core dump, or the machine itself goes wacko on a function call (rare with modern operating systems, but possible on an embedded design running a real time operating system).
Something else to note: the register references in this article are specific to the x86 architecture. For example, if you're compiling on an HP PA-RISC box don't bother looking for the ESP register -- it ain't there (look at R30).
------------------------
Printer Friendly Version
Procedure Call Sequence in C Copyright © June 2006 Manu Garg
Have you tried Searching this site?
This is a Unix/Linux resource website. It contains technical articles about Unix, Linux and general computing related subjects, opinion, news, help files, how-to's, tutorials and more.
Contact us
Printer Friendly Version