如何结合backtrace()与addr2line获取调用栈的行号?
解决backtrace与addr2line匹配行号的问题
问题根源
你的问题是**地址空间随机化(ASLR)**导致的:
objdump显示的是程序文件中的链接时偏移地址(以0000开头)- 程序运行时,ASLR会随机分配加载基地址,所以
backtrace输出的是虚拟内存地址,和链接时偏移不匹配,直接传给addr2line自然查不到有效信息。
两种解决方法
方法1:临时关闭ASLR(调试用)
关闭ASLR后,程序每次运行的加载地址都会和链接时偏移一致,无需手动计算:
- 执行命令关闭ASLR:
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space - 重新运行你的程序,此时
backtrace输出的地址会和objdump显示的地址一致,直接用addr2line即可:addr2line -f -e ./main.out 0x12da - 调试完成后,记得恢复ASLR(系统默认开启,提升安全性):
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
方法2:不关闭ASLR,手动计算偏移地址
如果不想关闭ASLR,需要将运行时虚拟地址转换为链接时偏移:
- 获取程序的加载基地址:
- 先运行程序,找到它的PID(比如用
ps aux | grep main.out) - 查看进程的内存映射,找到
main.out的可执行段(权限为r-xp),起始地址就是基地址:
示例输出:cat /proc/[你的PID]/maps | grep main.out
这里的556c59bec000-556c59bed000 r-xp 00000000 08:01 123456 ./main.out0x556c59bec000就是加载基地址。
- 先运行程序,找到它的PID(比如用
- 计算偏移地址:用
backtrace输出的虚拟地址减去基地址0x556c59bed2da - 0x556c59bec000 = 0x12da - 将偏移地址传给
addr2line:
此时就能正确输出函数名和对应行号了。addr2line -f -e ./main.out 0x12da
可选:代码层面自动处理偏移
如果想让程序自动输出可直接用的偏移地址,可以在print_stack_trace中添加逻辑,读取自身的加载基地址,然后将array中的地址减去基地址后输出。示例(需要链接-ldl):
#include <dlfcn.h> void print_stack_trace() { void *array[10]; size_t size; char **strings; size_t i; // 获取加载基地址 Dl_info info; dladdr((void*)print_stack_trace, &info); uintptr_t base_addr = (uintptr_t)info.dli_fbase; size = backtrace(array, 20); strings = backtrace_symbols(array, size); if (strings == NULL) { perror("backtrace_symbols"); exit(EXIT_FAILURE); } // 输出时同时显示偏移地址 for (i = 0; i < size; i++) { uintptr_t addr = (uintptr_t)array[i]; uintptr_t offset = addr - base_addr; printf("%s | 偏移地址: 0x%lx\n", strings[i], offset); } free(strings); }
编译时添加-ldl:
g++ -rdynamic -g3 -ldl main.cpp -o main.out
运行后直接用输出的偏移地址传给addr2line即可。
内容的提问来源于stack exchange,提问作者Tokubara
相关产品推荐
相关产品推荐

