Sunday, November 22, 2009

C Programming: Understanding function calls

Today I had some fun playing with assembly to understand the mechanics of function calls and parameter passing on x86.

The C Program:
#include <stdio.h>
void func1(int a)
{
printf("hey there, I'm in func1! %d\n", a);
}

void func0()
{
printf("hey there, I'm in func0!");
}

int main()
{
func0(); /* Pass no arguments */
func1(1); /* Pass 1 argument */
return 0;
}

Next I compiled and disassembled this:
gcc -o func.c func
objdump -d -M intel func > func.dump

Disassembled Output (heavily commented):
;;;;;;;;;;;;; func1 disassembled ;;;;;;;;;;;;;;;
080483c4 (func1):
; Save the calling func's stack frame pointer.
80483c4: 55 push ebp

; Create the stack frame pointer for this function. This will be used later as
; a reference point from which the arguments that were just pushed to stack
; from the main function can be retrieved (see main function below)
80483c5: 89 e5 mov ebp,esp

; Allocate space on the stack for local variables.
80483c7: 83 ec 08 sub esp,0x8

; Fetch the argument passed from calling function (integer 1)
; We add 8 to ebp because we need to go back in the stack to a point where
; we can fetch the argument being passed. Its 8 because we used 4 while
; pushing ebp (above) and 4 more because the calling function pushed the
; address of the instruction to be executed after this function returns.
80483ca: 8b 45 08 mov eax,DWORD PTR [ebp+0x8]

; Store the fetched argument on the stack (to be passed to printf).
80483cd: 89 44 24 04 mov DWORD PTR [esp+0x4],eax

; Store the address of the string on the stack (to be passed to printf).
; This string is stored in the ".rodata" section of the executable file.
80483d1: c7 04 24 f0 84 04 08 mov DWORD PTR [esp],0x80484f0

; Call the function to format/print the text.
80483d8: e8 1b ff ff ff call 80482f8

; Restore the frame pointer (ebp) to its original value and return.
80483dd: c9 leave
80483de: c3 ret

; func0 is the same as func1 except we don't need to fetch the integer
; and store it on the stack.
080483df (func0):
80483df: 55 push ebp
; .. skipped ..

;;;;;;;;;;;;; main function disassembled ;;;;;;;;;;;;;;;
080483f3 (main):
80483f3: 8d 4c 24 04 lea ecx,[esp+0x4]
80483f7: 83 e4 f0 and esp,0xfffffff0
80483fa: ff 71 fc push DWORD PTR [ecx-0x4]
80483fd: 55 push ebp
80483fe: 89 e5 mov ebp,esp
8048400: 51 push ecx
; Allocate space on stack for local variables/args.
8048401: 83 ec 04 sub esp,0x4

; Call func0 (no arguments, nothing on our stack).
8048404: e8 d6 ff ff ff call 80483df

; Store the integer 1 on stack which will be retrieved by the function func1.
8048409: c7 04 24 01 00 00 00 mov DWORD PTR [esp],0x1
8048410: e8 af ff ff ff call 80483c4

; This I'm guess is the return value of the main funciton.
8048415: b8 00 00 00 00 mov eax,0x0

; Relinquish allocated stack space.
804841a: 83 c4 04 add esp,0x4

; Restore the contents of registers and the stack frame pointer (ebp).
804841d: 59 pop ecx
804841e: 5d pop ebp
804841f: 8d 61 fc lea esp,[ecx-0x4]

Learning assembly is fun and can further your understanding at a much deeper level.
I highly recommend Programming from the ground up for an introduction to programming, assembly and linux.

0 comments: