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)