






















这是一份标准化的GDB调试环境配置指南,包含了Debuginfod(第三方调试符号自动下载)和GDB历史记录持久化设置。
为使所有用户都能自动下载调试符号,建议在系统配置文件中设置。
操作步骤:
编辑 /etc/profile 或在 /etc/profile.d/ 下新建 .sh 文件(推荐):
# 编辑文件
sudo vim /etc/profile.d/debuginfod.sh
# 添加以下内容
export DEBUGINFOD_URLS="https://debuginfod.ubuntu.com"
export DEBUGINFOD_CACHE_PATH="$HOME/.debuginfod_cache"
DEBUGINFOD_URLS: 指定调试符号服务器地址(此处为Ubuntu官方)。(建议所有用户可用)DEBUGINFOD_CACHE_PATH: 设置本地缓存路径,避免重复下载。(建议用户自用)部分三方调试库有警告提示,安装时提示:e: unable to locate package libcap2-dbgsym
调试符号包(-dbgsym)托管在独立仓库(ddebs.ubuntu.com),执行下述命令前先执行:
lsb_release -cs
如果没有错误,则继续执行下面命令添加调试符号包:
echo "deb http://ddebs.ubuntu.com $(lsb_release -cs) main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ddebs.list
echo "deb http://ddebs.ubuntu.com $(lsb_release -cs)-updates main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ddebs.list
echo "deb http://ddebs.ubuntu.com $(lsb_release -cs)-proposed main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ddebs.list
否则执行获取系统代号的命令:
lsb_release -sc 2>/dev/null
上述命令如果得到:noble
则直接执行下述命令添加调试符号包:
直接手动指定 noble,避免脚本解析错误:
echo "deb http://ddebs.ubuntu.com noble main restricted universe multiverse" | sudo tee /etc/apt/sources.list.d/ddebs.list
echo "deb http://ddebs.ubuntu.com noble-updates main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ddebs.list
echo "deb http://ddebs.ubuntu.com noble-proposed main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ddebs.list
ddebs 仓库需要 GPG 密钥来验证包:
sudo apt install ubuntu-dbgsym-keyring
如果上述命令失败,请尝试: sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys C8CAB6595FDFF622
然后安装cap2调试包:
sudo apt update
sudo apt install libcap2-dbgsym
修改全局GDB配置文件,使历史记录在退出后依然保存。
操作步骤:
编辑 /etc/gdb/gdbinit:
sudo vim /etc/gdb/gdbinit
在文件中添加以下内容:
# --- GDB History Configuration ---
# 开启命令历史保存功能
set history save on
# 设置历史文件保存路径
set history filename ~/.gdb_history
# 设置保存的命令条数 (设为较大值)
set history size 10000
# 记录命令参数
set history remove-duplicates unlimited
# ---------------------------------
# 备注:根据需要,此处可选择是否开启debuginfod
# set debuginfod enabled off
执行以下命令或重新登录终端:
source /etc/profile
$pc在 GDB 中,$pc 代表 Program Counter(程序计数器)。
它是 CPU 中的一个特殊寄存器,存放的是下一条即将执行的指令的内存地址。
核心作用:
step (s) 或 next (n) 时,硬件实际上是在更新 $pc 的值。常用操作:
(gdb) print $pc
# 或者查看十六进制
(gdb) p/x $pc
(gdb) x/i $pc
$pc 让程序跳过某段代码或重复执行某行:
(gdb) set $pc = 0x400506 # 强行让程序跳转到这个地址执行
提示:
在不同的 CPU 架构上,这个寄存器的名字不同(比如在 x86-64 上叫 rip,在 32 位 x86 上叫 eip,在 ARM 上叫 R15)。但 GDB 为了方便跨平台使用,统一提供了一个通用的别名 $pc。
在 GDB 中,$sp 代表 Stack Pointer(栈指针)。
它是 CPU 中的一个核心寄存器,指向当前函数调用栈的栈顶(即当前正在使用的内存区域的边缘)。
核心作用
$sp 来为局部变量(比如 struct quantum **p)和返回地址分配空间。push 或 pop 指令时,$sp 的值会自动增加或减少。常见操作
如果你想看当前的栈顶地址或内容,可以在 GDB 中输入:
p/x $spx/4wx $sp$pc 一样,$sp 是 GDB 提供的通用别名。在 x86-64 架构上,它实际对应的是 rsp 寄存器;在 ARM 上对应的是 R13。想看看当前函数调用的完整“足迹”,可以配合使用 bt (backtrace) 命令,它会基于 $sp 和 $fp(栈帧指针)解析出整个调用链。
在 GDB 中,$fp 代表 Frame Pointer(栈帧指针)。
它是指向当前函数栈帧(Stack Frame)起始位置的寄存器。
核心作用:定位局部变量
虽然 $sp 指向栈顶(变动频繁),但 $fp 在一个函数执行期间通常是固定不变的。
struct quantum **p)通常存储在 $fp 减去一个偏移量的地方。$fp 加上一个偏移量的地方。关系对比
可以将栈看作一个文件夹:
$fp (Frame Pointer):文件夹的顶部(起始处)。它标记了当前函数空间的开始。$sp (Stack Pointer):文件夹的底部(当前位置)。随着你往文件夹里塞入更多临时数据,底部会不断伸缩。常见操作
在 GDB 中,你可以通过以下命令查看:
p/x $fpinfo locals(GDB 会自动根据 $fp 的偏移量帮你找出来)。$rbp;在 ARM 中通常是 R11。调试意义
如果你发现代码在 if (p == NULL) 处逻辑跑偏了,通过查看 $fp 附近的内存,你可以确认 p 到底被编译器分配到了哪个具体的内存地址。
注意:如果编译时开启了高等级优化(如 -O2),编译器为了节省寄存器,可能会去掉栈帧指针(Omit Frame Pointer)。在这种情况下,$fp 的值可能不可靠或不可用。
AddressSanitizer (ASan) 是一款强大的内存错误检测工具,集成在 GCC 和 Clang 编译器中。它能够实时捕获诸如堆栈越界、内存泄漏以及释放后使用 (Use-After-Free) 等常见 C 语言内存问题。
以下是一个包含“释放后使用”错误的完整示例及调试步骤:
memOverWriteT.c)这段代码申请了一块堆内存,释放后又尝试修改其中的数据,这是典型的内存安全漏洞。
#include <stdio.h>
#include <stdlib.h>
int main() {
// 1. 在堆上分配内存
int *ptr = (int *)malloc(10 * sizeof(int));
ptr[0] = 100;
printf("Value before free: %d\n", ptr[0]);
// 2. 释放内存
free(ptr);
// 3. 错误操作:访问已释放的内存 (Use-After-Free)
// ASan 将在这里捕获错误并停止程序
ptr[0] = 200;
printf("Value after free: %d\n", ptr[0]);
return 0;
}
使用编译器提供的 -fsanitize=address 标志来开启 ASan。建议同时配合以下标志以获得更清晰的报错信息:
-g: 生成调试符号,报错时显示具体的源代码行号。-fno-omit-frame-pointer: 确保函数调用栈(Stack Trace)的准确性。编译命令:
gcc -fsanitize=address -g -fno-omit-frame-pointer memOverWriteT.c -o example
直接运行生成的可执行文件,ASan 会在检测到错误时立即终止程序并打印详细报告。
运行输出(关键部分):
Value before free: 100
==12345==ERROR: AddressSanitizer: heap-use-after-free on address 0x604000000010 ...
WRITE of size 4 at 0x604000000010 thread T0
#0 0x... in main memOverWriteT.c:15 <-- 报错的具体行号 (第15行)
...
0x604000000010 is located 0 bytes inside of 40-byte region ...
freed by thread T0 here:
#0 0x... in free ...
#1 0x... in main memOverWriteT.c:11 <-- 内存被释放的地方 (第11行)
...
报告解读
heap-use-after-free(堆内存释放后访问)。memOverWriteT.c:15(第15行报错)和 memOverWriteT.c:11(第11行释放)精准定位问题。在GDB中,内存断点(通常称为观察点 Watchpoint或硬件断点)是一种非常有用的调试手段。它允许你在特定内存地址的数据被读取、写入或修改时暂停程序执行,而无需预先知道哪一行代码修改了该内存。
以下是GDB内存断点(观察点)的详细使用指南:
watch <变量名|地址> (写断点)
watch global_varrwatch <变量名|地址> (读断点)
awatch <变量名|地址> (读写断点)
(gdb) start
(gdb) watch my_variable # 监控局部或全局变量
(gdb) continue
若想监控内存地址 0x601050 处的前4个字节,使用指针强制转换:
(gdb) watch *(int *)0x601050
如果需要监控类成员或结构体成员,为了避免无效的变量出栈导致断点消失,使用 -location:
(gdb) watch -location pObject->member
# 或者使用简写
(gdb) watch -l pObject->member
info break 或 info watchpointsdelete <编号>disable <编号> / enable <编号> watch,这几乎不影响程序速度。如果硬件断点资源不足(通常仅支持 4-8 个),GDB 会尝试用软件模拟,这会极其缓慢。watch 作用于变量名时,它监控的是对应的值。作用于地址时,如 *(int*)0x...,监控的是该大小的内存单元。watch。watch 断点会自动被删除。当调试内存崩溃(例如 SIGSEGV)、变量莫名其妙被修改,或者在多线程环境中查找数据竞争时,watch 是比普通行断点(break)高效得多的工具。
其他参考文章:
2.应用程序或动态库中与加载的其他动态库的类或者函数重名问题
3.valgrind检查C/C++内存泄漏 Yosimite10.10(Mac os)安装c/c++内存检测工具valgrind
10.gdb调试4--回退
12.gdb调试线程
13.gdb带参调试
14.GDB
15.gdb线程调试指南
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。