GCC调试指南:从

GCC调试指南:从-g1到-g3的符号信息管理及strip应用

2025-12-04

GCC 提供了多种选项来帮助开发者调试程序。最核心的选项是 −g,用于在编译时将调试信息嵌入到可执行文件中,让 GDB(GNU Debugger)等调试器能够读取源代码、变量、函数等信息。

问题描述 (Trouble)

在编译时忘记使用 −g 选项,导致生成的可执行文件(例如 a.out)不包含任何调试符号。当尝试用 GDB 调试时,无法查看变量值、设置断点或进行源码级别的调试。

GDB 报错示例

No debugging symbols found in a.out

替代方案 (Alternative Solution)

始终记得在开发和调试阶段使用 −g 选项。此外,可以使用 −g 的不同级别来控制包含的调试信息的详细程度,平衡文件大小和调试能力。

选项描述−g默认级别,通常等同于 −g2。−g1最少量的调试信息,仅包含行号和最基本的变量/函数信息。−g2推荐。包含标准调试信息,包括局部变量、函数参数、完整的类型信息等。−g3包含 −g2 的所有信息,以及宏定义。这会生成最大的调试文件。代码示例 (Sample Code Example)

假设您的 C 源代码文件是 test.c

#include

int factorial(int n) {

if (n <= 1) {

return 1;

}

return n * factorial(n - 1);

}

int main() {

int number = 5;

int result = factorial(number);

printf("The factorial of %d is %d\n", number, result);

return 0;

}

正确的编译命令

# 使用 -g2 (推荐) 编译,生成带调试信息的可执行文件 'myprog'

gcc -g2 test.c -o myprog

# 启动 GDB 调试

gdb ./myprog

GDB 调试示例

在 GDB 中输入 break main (设置断点) 和 run (运行) 就可以正常开始调试。

问题描述 (Trouble)

同时使用高优化级别(如 −O2 或 −O3)和 −g 进行编译。虽然这是允许的,但编译器优化(如变量消除、代码重排)可能会使得调试器(GDB)看到的执行流程和变量值与源代码中的描述不一致。这会给调试带来极大的困扰。

替代方案 (Alternative Solution)

在进行问题排查或详细调试时,请关闭或降低优化级别。通常的做法是

调试时 仅使用 −g 或 −Og (专为改善调试体验而设计的优化级别)。

发布时 使用 −O2 或 −O3 (不带 −g) 来最大化性能。

代码示例 (Sample Code Example)

# 错误/不推荐做法:高优化与调试信息混用

# 可能会导致 GDB 无法准确追踪变量 'i' 的值或代码执行的顺序

gcc -O3 -g test.c -o myprog_optimized_debug

# 推荐做法:降低优化级别以保证调试准确性

gcc -Og -g test.c -o myprog_debug

问题描述 (Trouble)

即使使用 −g 编译,生成的程序在发布时通常需要去除调试信息以减小文件大小,并防止反向工程泄露过多内部细节。如果直接发布带调试信息的程序,会造成不必要的开销。

替代方案 (Alternative Solution)

使用 strip 工具或 GCC 的 −s 选项来去除可执行文件中的调试符号和重定位信息。

编译时去除 使用 −s 选项。注意 这样就无法使用 GDB 调试该文件了。

编译后去除(更灵活) 先用 −g 编译,然后使用 strip 工具。这样可以保留带调试信息的版本(仅供内部调试),同时创建一个“干净”的发布版本。

代码示例 (Sample Code Example)

方法 A 编译时直接去除 (不推荐用于内部测试)

# 编译并立即去除所有符号表和重定位信息

gcc -O2 -s test.c -o myprog_stripped_s

方法 B 编译后使用 strip (推荐)

# 1. 使用 -g 编译,保留调试信息

gcc -O2 -g test.c -o myprog_full

# 2. (内部保留 myprog_full 调试)

# 3. 使用 strip 创建一个精简的发布版本

strip myprog_full -o myprog_release

# 或者直接 strip myprog_full (会覆盖原文件)