GDB basic tutorial

Jean-David Gadina
© 2023 Jean-David Gadina - - All Rights Reserved
This article is published under the terms of the FreeBSD Documentation License

I have to admit I always felt stupid, while building XCode projects, when the GDB window comes out, or when it display a message like: «set a breakpoint in malloc to debug».
So I decided to learn a few things about GDB. This tutorial will explain you some of the basics.

Example program & Compilation

Before using GCC, we need a sample program to work with. We'll also need to add a specific compiler flag when compiling.
Let's begin with a simple C program:

#import <stdlib.h>

void do_stuff( void );

unsigned long x;

int main( void )
    x = 10;
    return 0;

void do_stuff( void )
    char * s;
    s = ( char * )x;
    if( s != NULL )
        s[ 0 ] = 0;

Name the file 'gdb_test.c', then compile and run the code with the following command:

gcc -Wall -o gdb_test gdb_test.c && ./gdb_test

No surprise, the program will end with a segmentation fault (EXC_BAD_ACCESS - SIGSEGV).

Now compile the same file again, and add the '-g' parameter to the GCC invocation:

gcc -Wall -g -o gdb_test gdb_test.c

That will tell GCC to generate the debug symbols file. It will be called 'gdb_test.dSYM'.
Such a file contains informations about each symbol of the executable (functions, variables, line numbers, etc). Now that we have that file, we are ready to use GDB.

Using GDB

Simply type 'gdb' to enter a new GDB session. We'll the load our executable using the file command:

GNU gdb 6.3.50-20050815 (Apple version gdb-1518) (Thu Jan 27 08:34:47 UTC 2011)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "x86_64-apple-darwin".
(gdb) file gdb_test

The executable is now loaded. We can run it with the 'run' command:

(gdb) run
Starting program: /Users/macmade/Desktop/gdb_test 
Reading symbols for shared libraries +. done

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x000000000000000a
0x0000000100000f0f in do_stuff () at gdb_test.c:24
24	        s[ 0 ] = 0;

We can see GDB caught the segmentation fault, and stopped the program's execution. It even display the line where the segmentation fault occurs. Very useful!

We can also ask GDB for a backtrace, with the 'bt' command:

(gdb) bt
#0  0x0000000100000f0f in do_stuff () at gdb_test.c:24
#1  0x0000000100000eeb in main () at gdb_test.c:11


We can also set breakpoints with GDB. A breakpoint can be a function's name, a specific line number, or a condition.
When GDB encounters a breakpoint, it will stop the program execution. The execution can the be continued with the 's' (step) or 'n' (next) commands.

So lets run our program again, and let's set a breakpoint in the do_stuff() function:

(gdb) break do_stuff
Breakpoint 1 at 0x100000ef6: file gdb_test.c, line 20.
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /Users/macmade/Desktop/gdb_test 

Breakpoint 1, do_stuff () at gdb_test.c:20
20	    s = ( char * )x;

GDB will automatically stops the program's execution when we call the do_stuff() function.
Now we can inspect our program.

We can start by asking the value of our 'x' variable:

(gdb) p x

That will print the value of the 'x' variable:

$1 = 10

We can now modify that variable, so it equals '0' (NULL):

(gdb) p x=0

Now we've fixed the problem, and we can continue the program's execution, by stepping multiple times:

(gdb) s

Till GDB prints:

Program exited normally.