Intro

For those of you not familiar with the C toolchain, it goes something like this:

Source -> preprocessed source -> assembly -> object file -> binary

The second step (preprocessed -> assembly) is the hardest to undo. It removes labels, debug symbols, and turns a high-level control flow into assembly instructions. However, since you need a binary to run a program, if you can undo that step, you can see what’s going on.

GDB Basics

GDB is a debugger. It steps through a compiled program, line by line, and shows you the changes after every step.

Let’s take a simple hello-world program and step through it.

#include <stdio.h>

int main(void) {
  puts("Hello, world!");
  return 0;
}

Passing -g to gcc means preserve debug symbols, so we know where we are in the original source code. Passing -q to gdb means don’t print 10 lines of copyright info. Note that we have to add a breakpoint in the main function, or it will run without ever stopping.

$ gcc -g hello_world.c -o hello
$ gdb -q hello
Reading symbols from hello...done.
(gdb) list
1       #include <stdio.h>
2
3       int main(void) {
4         puts("Hello, world!");
5         return 0;
6       }
7
(gdb) break main
Breakpoint 1 at 0x4004db: file hello_world.c, line 4.
(gdb) run
Starting program: /home/joshua/hello
Breakpoint 1, main () at tmp.c:4
4         puts("Hello, world!");
(gdb) next
Hello, world!
5         return 0;
(gdb)
6       }
(gdb) continue
Continuing.
[Inferior 1 (process 28690) exited normally]

You can also show variables as you step through. (Note that return 0 is optional in the main function)

#include <stdio.h>

int main(void) {
  char *var = "Hello, world!";
  var = "changed my mind";
  puts(var);
}
$ gcc -g var.c -o var
$ gdb -q var
Reading symbols from var...done.
(gdb) break main
Breakpoint 1 at 0x4004df: file var.c, line 4.
(gdb) run
Starting program: /home/joshua/var
Breakpoint 1, main () at var.c:4
4         char *var = "Hello, world!";
(gdb) print var
$1 = 0x0
(gdb) n
5         var = "changed my mind";
(gdb) print var
$2 = 0x4005a0 "Hello, world!"
(gdb) n
6         puts(var);
(gdb) print var
$3 = 0x4005ae "changed my mind"
(gdb) n
changed my mind
7       }
(gdb) c
Continuing.
[Inferior 1 (process 29542) exited normally]

This post is continued in Buffer Overflows and Stacks and Assembly, Oh My.