Hello world
Take a simple hello world program (hello.c):
#include<stdio.h> main() { printf("Hello World"); }
Compile with GCC:
gcc -Wall hello.c -o hello
Open the executable with GDB:
gdb hello
List all the functions:
(gdb) info functions All defined functions: Non-debugging symbols: 0x08048294 _init 0x080482bc __gmon_start__@plt 0x080482cc __libc_start_main@plt 0x080482dc printf@plt 0x080482f0 _start 0x08048314 call_gmon_start 0x08048340 __do_global_dtors_aux 0x080483a0 frame_dummy 0x080483c4 main 0x080483f0 __libc_csu_fini 0x08048400 __libc_csu_init 0x08048469 __i686.get_pc_thunk.bx 0x08048470 __do_global_ctors_aux 0x08048498 _fini
Disassemble main:
(gdb) disas main Dump of assembler code for function main: 0x080483c4 <main+0>: lea 0x4(%esp), %ecx 0x080483c8 <main+4>: and $0xfffffff0,%esp 0x080483cb <main+7>: pushl 0xfffffffc(%ecx) 0x080483ce <main+10>: push %ebp 0x080483cf <main+11>: mov %esp,%ebp 0x080483d1 <main+13>: push %ecx 0x080483d2 <main+14>: sub $0x4,%esp 0x080483d5 <main+17>: movl $0x80484c0,(%esp) 0x080483dc <main+24>: call 0x80482dc <printf@plt> 0x080483e1 <main+29>: add $0x4,%esp 0x080483e4 <main+32>: pop %ecx 0x080483e5 <main+33>: pop %ebp 0x080483e6 <main+34>: lea 0xfffffffc(%ecx),%esp 0x080483e9 <main+37>: ret End of assembler dump.
Examine parameters passed to printf:
(gdb) x/s 0x80484c0 0x80484c0 <__dso_handle+4>: "Hello World"
Calling functions
Take a hello world program which calls a function (hello-func.c):
#include<stdio.h> int foo(int bar) { printf("bar:%d\n", bar); return bar + 3; }; int main(void) { foo(5); return 0; }
Compile with GCC:
gcc -Wall hello-func.c -o hello-func
Open the executable with GDB:
gdb hello-func
Disassemble main and foo:
Dump of assembler code for function main: 0x080483e5 <main+0>: lea 0x4(%esp),%ecx 0x080483e9 <main+4>: and $0xfffffff0,%esp 0x080483ec <main+7>: pushl 0xfffffffc(%ecx) 0x080483ef <main+10>: push %ebp 0x080483f0 <main+11>: mov %esp,%ebp 0x080483f2 <main+13>: push %ecx 0x080483f3 <main+14>: sub $0x4,%esp 0x080483f6 <main+17>: movl $0x5,(%esp) 0x080483fd <main+24>: call 0x80483c4 <foo> 0x08048402 <main+29>: mov $0x0,%eax 0x08048407 <main+34>: add $0x4,%esp 0x0804840a <main+37>: pop %ecx 0x0804840b <main+38>: pop %ebp 0x0804840c <main+39>: lea 0xfffffffc(%ecx),%esp 0x0804840f <main+42>: ret End of assembler dump. (gdb) disas foo Dump of assembler code for function foo: 0x080483c4 <foo+0>: push %ebp ; do prologue (save %ebp on the stack) 0x080483c5 <foo+1>: mov %esp,%ebp ; save current %esp to %ebp 0x080483c7 <foo+3>: sub $0x8,%esp ; reserve space for local variables ; (here you don't use local variables, ; but you pass arguments to printf on the stack 0x080483ca <foo+6>: mov 0x8(%ebp),%eax ; save parameter (bar) to %eax (I'm not sure why?) 0x080483cd <foo+9>: mov %eax,0x4(%esp) ; put %eax on the stack 0x080483d1 <foo+13>: movl $0x80484e0,(%esp) ; put format string on the stack 0x080483d8 <foo+20>: call 0x80482dc <printf@plt> ; invoke printf 0x080483dd <foo+25>: mov 0x8(%ebp),%eax ; save bar into %eax again 0x080483e0 <foo+28>: add $0x3,%eax ; increment %eax by 3 (return parameter is passed through %eax) 0x080483e3 <foo+31>: leave ; do epilogue 0x080483e4 <foo+32>: ret End of assembler dump. (gdb)
Note, that if you compile this program with -O2, things are a bit less obvious:
gcc -Wall -O2 hello-func.c -o hello-func
(gdb) disas main Dump of assembler code for function main: 0x08048400 <main+0>: lea 0x4(%esp),%ecx 0x08048404 <main+4>: and $0xfffffff0,%esp 0x08048407 <main+7>: pushl 0xfffffffc(%ecx) 0x0804840a <main+10>: push %ebp 0x0804840b <main+11>: mov %esp,%ebp 0x0804840d <main+13>: push %ecx 0x0804840e <main+14>: sub $0x4,%esp 0x08048411 <main+17>: movl $0x5,(%esp) 0x08048418 <main+24>: call 0x80483d0 <foo> 0x0804841d <main+29>: add $0x4,%esp 0x08048420 <main+32>: xor %eax,%eax 0x08048422 <main+34>: pop %ecx 0x08048423 <main+35>: pop %ebp 0x08048424 <main+36>: lea 0xfffffffc(%ecx),%esp 0x08048427 <main+39>: ret End of assembler dump. (gdb) disas foo Dump of assembler code for function foo: 0x080483d0 <foo+0>: push %ebp 0x080483d1 <foo+1>: mov %esp,%ebp 0x080483d3 <foo+3>: push %ebx ; by convention the function must save ; %ebx, %esi, %edi, %ebp, %ds, %es, %ss registers if they are used ; inside 0x080483d4 <foo+4>: sub $0x14,%esp ; reserve space for a local variables (why 0x14?) 0x080483d7 <foo+7>: mov 0x8(%ebp),%ebx ; save "bar" to %ebx 0x080483da <foo+10>: movl $0x8048500,(%esp) 0x080483e1 <foo+17>: mov %ebx,0x4(%esp) 0x080483e5 <foo+21>: call 0x80482dc <printf@plt> 0x080483ea <foo+26>: lea 0x3(%ebx),%eax ; lea -- load effective address is used for addition ; lea uses the addressing rule: ; immed32(basepointer,indexpointer,indexscale) = ; immed32 + basepointer + indexpointer * indexscale 0x080483ed <foo+29>: add $0x14,%esp ; restore %esp 0x080483f0 <foo+32>: pop %ebx ; restore %ebx 0x080483f1 <foo+33>: pop %ebp 0x080483f2 <foo+34>: ret End of assembler dump. (gdb)