APLawrence.com -  Resources for Unix and Linux Systems, Bloggers and the self-employed

Procedure Call Sequence in C


© June 2006 Manu Garg
by Manu Garg

When you call a function in C, you need to ensure mainly 5 things:

  1. Store the returning address somewhere, so that we can continue executing the current function after completion of called function.
  2. Saving other information about current function. (Later, we'll see that it boils down to saving current frame pointer (EBP) somewhere.)
  3. Providing the callee with the parameters.
  4. Providing called function some space to store its automatic variables.
  5. Providing some mechanism to get return value from the called function. It's implemented using EAX register. Called function stores return calue in EAX.

STACK

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.

FUNCTION EXECUTION:

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.

EXECUTION IN STEPS:

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".

ILLUSTRATION

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.





(OLDER)    <- More Stuff -> (NEWER)    (NEWEST)   

Printer Friendly Version

->
-> Procedure Call Sequence in C

3 comments


Inexpensive and informative Apple related e-books:

iOS 10: A Take Control Crash Course

Take Control of Pages

Take Control of Numbers

Take Control of Automating Your Mac

Take Control of iCloud




More Articles by © Manu Garg






Sat Jun 25 09:47:31 2011: 9589   venkat

gravatar


Missing information
how parameters that are pushed on to the stack are removed ?



Sat Jun 25 09:58:42 2011: 9590   TonyLawrence

gravatar


When you return from a function, the stack is popped.



Sun Jun 26 00:55:50 2011: 9591   BigDumbDinosaur

gravatar


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

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





There's no obfuscated Perl contest because it's pointless. (Jeff Polk )




Linux posts

Troubleshooting posts


This post tagged:

Programming



Unix/Linux Consultants

Skills Tests

Unix/Linux Book Reviews

My Unix/Linux Troubleshooting Book

This site runs on Linode