Quick Tutorial to understand function call stack frame using C code and Gdb.

All the explanation below will be with respect to the C code in test.c. Sizeof(int) = 4, sizeof registers = 8 bytes and address space theoretically = 2pow64.

test.c:

#include <stdio.h>

int func(int a, int b)
{
int h = 10;
int c = h+a+b;

return c;
}

int main()
{
int t = 1;
int t1 = 2;
int d = func((int)0xdeadbeef, (int)0xdeedbeef);

return 0;
}

Compiling test.c:
gcc -ggdb -O0 -o mybin ./test.c

How stack works:
1) Stack grows from from HIGH address to LOW address.
2) If main calls func(int a, int b), then call stack looks like:

HIGH_ADDR
4bytes param1 of func (Depending on compiler, params may directly go in registers and not on stack)
4bytes param2 of func
8bytes Address of instruction in main just after func ($eip or $rip)
8bytes base pointer of func (after saving old $bp on stack, $bp = $sp)
4bytes local var1 of func
4bytes local var2 of func
….
….
LOW_ADDR

Next we will debug mybin and try to understand call stack frame.

Let’s debug the code in GDB.

>gdb mybin   

Breakpoint 1, main () at ./test.c:15
(gdb) p $rbp //$rbp register value inside main
$1 = (void *) 0x7fff2f54e590
(gdb) p $rsp //stack pointer value just before calling func
$2 = (void *) 0x7fff2f54e580
(gdb) c
Continuing.

Breakpoint 2, func (a=-559038737, b=-554844433) at ./test.c:5
(gdb) p $rsp //stack pointer value after entering func
$3 = (void *) 0x7fff2f54e570
(gdb) p 0x7fff2f54e570 – 0x7fff2f54e580 //Subtract new stack pointer (inside func) with old stack pointer (in main just before calling func)
$4 = -16 //Indicates that we only push $eip value which was address of next instruction in main after func, and we pushed old $ebp value. We did not push input args of func on stack.
(gdb) p $rbp //$rbp = $rsp, so value at address $rbp is the value of old $rbp
$5 = (void *) 0x7fff2f54e570
(gdb) x/8b $rbp //Print value stored at memory $rbp and we get the value = old $rbp. See above $1
0x7fff2f54e570: 0x90 0xe5 0x54 0x2f 0xff 0x7f 0x00 0x00
(gdb) x/16b $rbp //Print value stored in stack before the oldbp. Value = address of instruction that will execute just after func call inside main function
0x7fff2f54e570: 0x90 0xe5 0x54 0x2f 0xff 0x7f 0x00 0x00
0x7fff2f54e578: 0x6f 0x04 0x40 0x00 0x00 0x00 0x00 0x00
Breakpoint 3 at 0x400445: file ./test.c, line 8.
(gdb) c
Continuing.

Breakpoint 3, func (a=-559038737, b=-554844433) at ./test.c:8
(gdb) x/4b $rbp – 4  //value of h
0x7fff2f54e568: 0x0a 0x00 0x00 0x00
(gdb) x/4b $rbp – 8 //value of c
0x7fff2f54e56c: 0xe8 0x7d 0x9b 0xbd

(gdb) disassemble main
Dump of assembler code for function main:
0x000000000040044a <main+0>: push rbp
0x000000000040044b <main+1>: mov rbp,rsp
0x000000000040044e <main+4>: sub rsp,0x10
0x0000000000400452 <main+8>: mov DWORD PTR [rbp-0xc],0x1
0x0000000000400459 <main+15>: mov DWORD PTR [rbp-0x8],0x2
0x0000000000400460 <main+22>: mov esi,0xdeedbeef
0x0000000000400465 <main+27>: mov edi,0xdeadbeef
0x000000000040046a <main+32>: call 0x400428 <func>
0x000000000040046f <main+37>: mov DWORD PTR [rbp-0x4],eax
0x0000000000400472 <main+40>: mov eax,0x0
0x0000000000400477 <main+45>: leave
0x0000000000400478 <main+46>: ret
End of assembler dump.
(gdb) disassemble func
Dump of assembler code for function func:
0x0000000000400428 <func+0>: push rbp
0x0000000000400429 <func+1>: mov rbp,rsp
0x000000000040042c <func+4>: mov DWORD PTR [rbp-0x14],edi
0x000000000040042f <func+7>: mov DWORD PTR [rbp-0x18],esi
0x0000000000400432 <func+10>: mov DWORD PTR [rbp-0x8],0xa
0x0000000000400439 <func+17>: mov eax,DWORD PTR [rbp-0x14]
0x000000000040043c <func+20>: add eax,DWORD PTR [rbp-0x8]
0x000000000040043f <func+23>: add eax,DWORD PTR [rbp-0x18]
0x0000000000400442 <func+26>: mov DWORD PTR [rbp-0x4],eax
0x0000000000400445 <func+29>: mov eax,DWORD PTR [rbp-0x4]
0x0000000000400448 <func+32>: leave
0x0000000000400449 <func+33>: ret
End of assembler dump.
(gdb)

Let’s try to understand assuming our stack started at address 31.
//Assumption params are of 4 bytes

StartAddr Data EndAddr Print values in gdb inside function func
31 0xdeadbeef 28 x/4b  $rbp+20  //In our case, this params did not go on stack
27 0xdeedbeef 24 x/4b $rbp+16  //In our case, this did not go on stack
23 return address 16 x/8b $rbp+8
15 old rbp 8 x/8b $rbp
[At this point $sp = 8, so $bp = 8.]
7 h 4 x/4b $rbp-4
3 c 0 x/4b $rbp-8

Continue reading “Quick Tutorial to understand function call stack frame using C code and Gdb.”