Article :: Getting Started with GDB
Introduction
This comprehensive guide covers everything you need to start debugging C programs with GDB, from compiling with debug symbols to inspecting memory and controlling program execution.
Overview
GDB (GNU Debugger) is the standard debugger for programs compiled with GCC. Unlike tools like strace that show system calls, GDB lets you step through your source code line by line, inspect variables, set conditional breakpoints, and even modify program behavior during execution.
Whether you’re tracking down a segfault, understanding how code executes, or reverse-engineering a binary, GDB is an indispensable tool for any C/C++ developer or security researcher.
Prerequisites
Before starting, you should:
- Have GCC installed and understand basic compilation
- Know C programming fundamentals (variables, functions, pointers)
- Be comfortable with the Linux command line
- Optionally read our GCC Compiling Cheat Sheet for compilation basics
Compiling with Debug Symbols
To debug with GDB effectively, you need debug symbols in your binary. These symbols map machine code back to your source code (functions, variables, line numbers).
| |
The -ggdb flag produces GDB-specific debug information with more details than the standard -g flag.
Always compile separate debug and release builds:
- Debug build:
gcc -ggdb -O0(no optimization, full symbols) - Release build:
gcc -O2(optimized, strip symbols before distribution)
Optimizations can make debugging confusing as the compiler reorders and eliminates code.
Managing Debug Symbols
Stripping Symbols from Binaries
For production deployments, remove debug symbols to reduce binary size:
Using strip (removes all symbols):
| |
Using objcopy (creates separate debug file):
| |
This approach keeps symbols in a separate file, allowing you to debug production issues without shipping large binaries.
Adding Debug Symbols Back
Option 1: Link debug file to binary
| |
GDB will automatically find and load debug_file when debugging binary_file.
Option 2: Load symbol file manually in GDB
| |
Analyzing Symbols with nm
The nm command lists symbols in object files and binaries, useful for understanding what functions and variables exist in a program.
Symbol case conventions:
- Lowercase = local symbols (file-scope)
- Uppercase = external symbols (global scope)
Common Symbol Types
| Symbol | Meaning |
|---|---|
| T | In the Text Section (code) |
| D | In the Initialized Data Section |
| B | In the Uninitialized Data Section (BSS) |
| U | Undefined (external reference) |
| A | Absolute symbol |
| N | Debugging Symbol |
man nm to see all symbol types. These are the most common you’ll encounter.Useful nm Commands
Search for a specific symbol:
| |
Display symbols sorted by address:
| |
Show only external (global) symbols:
| |
Show all symbols including debugger-only symbols:
| |
List only symbols in TEXT section (executable code):
| |
Decompiling with objdump
The objdump command disassembles binaries, showing the actual assembly code:
| |
This shows 20 lines of assembly starting from the main function using Intel syntax.
-M intel: Use Intel syntax (more readable than AT&T default)-D: Disassemble all sections (not just code)-d: Disassemble only code sections-S: Interleave source code with assembly (requires debug symbols)
GDB Command Reference
Starting GDB
| |
Switching Syntax (AT&T vs Intel)
By default, GDB uses AT&T assembly syntax. Intel syntax is often more readable:
| |
Add this to ~/.gdbinit to make it permanent.
Disassembling Code
| |
Disassemble a range:
| |
Two arguments (separated by comma) specify a range: start,end or start,+length.
Running the Program
| |
Listing Source Code
Requires source file available in the same directory:
| |
GDB needs the source file at the same path where it was compiled. If you compiled in /home/user/project but run GDB from /tmp, use:
| |
Inspecting Program State
Register information:
| |
Function listing:
| |
Source files:
| |
Variable information:
| |
Symbol table dump:
| |
Working with Breakpoints
Set breakpoints:
| |
List breakpoints:
| |
Enable/disable breakpoints:
| |
Delete breakpoints:
| |
Break only when a condition is true:
| |
This is incredibly powerful for debugging loops or specific error conditions.
Modifying Memory and Registers
GDB lets you modify program state during execution:
| |
Opcodes can be patched too:
| |
objcopy or hex editors.Defining Macros
Create custom commands that run automatically:
| |
This runs after every step, showing registers, stack, and next instructions. Save to ~/.gdbinit to use in all sessions.
Working with Data
Print register values in different formats:
| |
Example output:
(gdb) print /d $eax
$17 = 13
(gdb) print /t $eax
$18 = 1101
(gdb) print /x $eax
$19 = 0xd
Examine memory (x/nyz):
Format: x/[count][format][size] address
- count (n): Number of items to display
- format (y):
c(char),d(decimal),x(hex),s(string) - size (z):
b(byte),h(halfword/2 bytes),w(word/4 bytes),g(giant/8 bytes)
Examples:
| |
Convenience Variables
Create temporary variables for debugging:
| |
These variables exist only in the GDB session.
Calling Functions
You can call functions in the debugged program:
| |
malloc() to allocate memory, modify it, then see how your program handles it.Key Takeaways
- Compile with
-ggdbto include debug symbols for source-level debugging - Use separate debug files for production binaries to save space
- nm and objdump help analyze binaries before debugging
- GDB lets you control execution with breakpoints, stepping, and conditional breaks
- Inspect and modify memory, registers, and variables during runtime
- Conditional breakpoints are powerful for debugging specific scenarios
- GEF plugin dramatically improves the GDB experience with visual context
Practice Exercises
- Write a simple C program with a bug, compile with
-ggdb, and use GDB to find the issue - Set a breakpoint in
main(), run the program, and step through line by line usingnextandstep - Use
x/20xw $espto examine the stack and understand how local variables are stored - Create a conditional breakpoint that only triggers when a loop counter reaches 100
- Use
callto invoke a function with different arguments without recompiling - Practice disassembling functions with
disassembleand correlating assembly with source code
Further Reading
- GCC Compiling Cheat Sheet - Learn compilation options and debug symbol management
- Installing GEF - Supercharge your GDB with a modern interface
- Debugging with strace - System call tracing for when source isn’t available
- Linux Syscalls in Assembly - Understanding low-level program execution
- GDB Manual - Official comprehensive documentation