This document demonstrates debugging segmentation faults using core files and GDB, covering commands like backtrace, up, list, and print to analyze crashes and identify off-by-one errors. Practical walkthrough of C program debugging.
This document provides a practical walkthrough of debugging segmentation faults in C programs using core files and GDB debugger, demonstrating commands like backtrace, up, list, and print to analyze crashes, examine variables, and identify common errors like off-by-one array access.
Segmentation faults represent one of the most common crash types in C and C++ programs. Understanding how to debug these crashes using core files and the GDB debugger is essential for diagnosing memory access violations and fixing the underlying code issues.
Core files (also called core dumps) capture a snapshot of a program’s memory state at the moment of a crash. They contain:
| Benefit | Description |
|---|---|
| Post-mortem analysis | Debug crashes without reproducing them |
| Complete context | Access all program state at crash time |
| Offline debugging | Analyze crashes on different machines |
| Historical record | Keep evidence of past failures |
By default, many systems don’t generate core files or limit their size. Enable unlimited core file generation:
1# Enable core file generation with unlimited size
2ulimit -c unlimited
3
4# Verify the setting
5ulimit -c
To make this setting permanent:
1# Add to ~/.bashrc or ~/.bash_profile
2echo "ulimit -c unlimited" >> ~/.bashrc
3
4# Or edit /etc/security/limits.conf (system-wide)
5# Add the following line:
6# * soft core unlimited
Core files are typically generated in:
/var/crash/ (some Linux distributions)/proc/sys/kernel/core_pattern1# Check core pattern configuration
2cat /proc/sys/kernel/core_pattern
3
4# List core files in current directory
5ls -lh core*
Launch GDB with both the executable and core file:
1# Syntax: gdb -c <core-file> <executable>
2gdb -c core example
3
4# Alternative syntax
5gdb example core
When GDB loads a core file, it displays:
| Command | Purpose | Example |
|---|---|---|
backtrace (or bt) | Show full stack trace | bt |
up | Move up one frame in stack | up |
down | Move down one frame in stack | down |
list | Show source code around current line | list |
print (or p) | Print variable value | p variable_name |
info locals | Show all local variables | info locals |
info args | Show function arguments | info args |
frame | Show current frame info | frame |
quit | Exit GDB | quit |
The backtrace command reveals the call stack at crash time:
1(gdb) backtrace
2#0 strlen () at strlen.c:45
3#1 0x00005555555551a9 in copy_parameters (argc=1, argv=0x7fffffffe0c8) at example.c:10
4#2 0x00005555555551e8 in main (argc=1, argv=0x7fffffffe0c8) at example.c:16
Reading backtrace frames:
1# Move to calling function
2(gdb) up
3#1 0x00005555555551a9 in copy_parameters (argc=1, argv=0x7fffffffe0c8) at example.c:10
410 len = strlen(argv[i]);
5
6# View source code context
7(gdb) list
85 void copy_parameters(int argc, char *argv[]) {
96 int i;
107 size_t len;
118
129 for (i = 0; i <= argc; i++) {
1310 len = strlen(argv[i]);
1411 // ... rest of code
1512 }
1613 }
1# Print loop counter
2(gdb) print i
3$1 = 1
4
5# Print array element
6(gdb) print argv[0]
7$2 = 0x7fffffffe3a0 "./example"
8
9# Print next array element
10(gdb) print argv[1]
11$3 = 0x0
| Value | Meaning |
|---|---|
0x7fffffffe3a0 | Valid memory address (hexadecimal) |
0x0 | Null pointer (invalid) |
| Actual string | Dereferenced pointer value |
Hexadecimal numbers (starting with 0x) represent memory addresses:
0x0) is never validThe example crash occurs in a for loop iterating over command-line arguments:
1for (i = 0; i <= argc; i++) {
2 len = strlen(argv[i]);
3 // Process argument
4}
GDB investigation reveals:
1(gdb) print i
2$1 = 1
3
4(gdb) print argv[0]
5$2 = 0x7fffffffe3a0 "./example"
6
7(gdb) print argv[1]
8$3 = 0x0 # NULL pointer!
The loop condition i <= argc causes one iteration too many:
argc = 1 (one argument: program name)Change the loop condition from less-than-or-equal to strictly less-than:
1// Before (buggy)
2for (i = 0; i <= argc; i++) {
3 len = strlen(argv[i]);
4}
5
6// After (fixed)
7for (i = 0; i < argc; i++) {
8 len = strlen(argv[i]);
9}
| Type | Description | Example |
|---|---|---|
| Loop boundary | Iterating one time too many/few | <= instead of < |
| Array indexing | Accessing beyond array bounds | array[size] instead of array[size-1] |
| String termination | Not accounting for null terminator | Allocating strlen(s) instead of strlen(s)+1 |
Important
Off-by-one errors are among the most common bugs in C programming. Always carefully verify loop boundaries and array indices.
GDB may display warnings about missing debugging symbols:
1Reading symbols from /lib/x86_64-linux-gnu/libc.so.6...
2(No debugging symbols found in /lib/x86_64-linux-gnu/libc.so.6)
1# Debian/Ubuntu
2sudo apt-get install libc6-dbg
3
4# Red Hat/Fedora
5sudo yum install glibc-debuginfo
6
7# Install debug symbols for specific package
8sudo apt-get install <package>-dbg
| Scenario | Need Debug Symbols |
|---|---|
| Crash in own code | Yes, compile with -g flag |
| Crash in system library | Usually not needed (trust library) |
| Deep system library analysis | Yes, install debug packages |
1# 1. Enable core dumps
2ulimit -c unlimited
3
4# 2. Reproduce the crash
5./example
6# Segmentation fault (core dumped)
7
8# 3. Verify core file created
9ls -lh core
10
11# 4. Start GDB with core file
12gdb -c core example
13
14# 5. Get backtrace
15(gdb) backtrace
16
17# 6. Navigate to relevant frame
18(gdb) up
19
20# 7. View source code
21(gdb) list
22
23# 8. Examine variables
24(gdb) print i
25(gdb) print argv[i]
26
27# 9. Analyze the problem
28# Identify root cause
29
30# 10. Exit GDB
31(gdb) quit
32
33# 11. Fix the code
34# Edit source file
35
36# 12. Recompile with debug symbols
37gcc -g -o example example.c
38
39# 13. Test the fix
40./example
Always use -g flag during development:
1# Add debug symbols
2gcc -g -o program program.c
3
4# Add debug symbols with optimization (harder to debug)
5gcc -g -O2 -o program program.c
6
7# Debug symbols without optimization (recommended for debugging)
8gcc -g -O0 -o program program.c
ulimit -cNote
Trust standard library functions like
strlen(). If they crash, the problem is almost always in the calling code (invalid arguments).
Debugging segmentation faults requires generating core files with ulimit -c unlimited, analyzing them with GDB using commands like backtrace, up, list, and print, and carefully examining variable values and memory addresses. Off-by-one errors in loops and array access are common causes of segmentation faults. Systematic analysis of the call stack and variable states leads to identifying and fixing the root cause.