Libunwind获取的PC值无法配合addr2line使用的问题咨询
解决libunwind回溯addr2line无法解析PC的问题
我之前也碰到过这个坑!问题出在libunwind输出的function_name+0xoffset里的偏移值,和addr2line需要的地址格式不匹配——前者是相对于函数起始地址的偏移,而addr2line要的是程序内存中的绝对虚拟地址,或者是可执行文件的相对地址(如果是PIE程序的话还要额外处理)。
下面给你一步步拆解解决方案:
1. 先让libunwind输出绝对PC地址
修改你的代码,用unw_get_ip()拿到当前指令的绝对地址,这才是addr2line能识别的关键信息。示例代码如下:
#include <libunwind.h> #include <stdio.h> void print_backtrace() { unw_cursor_t cursor; unw_context_t context; unw_getcontext(&context); unw_init_local(&cursor, &context); printf("Backtrace (absolute PC):\n"); while (unw_step(&cursor) > 0) { unw_word_t ip; unw_get_reg(&cursor, UNW_REG_IP, &ip); char func_name[256]; unw_word_t func_offset; int ret = unw_get_proc_name(&cursor, func_name, sizeof(func_name), &func_offset); if (ret == 0) { printf("0x%lx (%s+0x%lx)\n", ip, func_name, func_offset); } else { printf("0x%lx (unknown function)\n", ip); } } } // 测试用函数 void nested_func() { print_backtrace(); } void middle_func() { nested_func(); } int main() { middle_func(); return 0; }
编译时记得加调试符号和链接libunwind:
g++ -g -o test test.cpp -lunwind
2. 处理PIE位置无关可执行文件的情况
现在大多数系统默认编译PIE程序,程序加载地址是随机的,直接用绝对PC传给addr2line会无效。这时候需要:
- 先获取程序的加载基址:运行程序后,查看
/proc/<你的进程PID>/maps,找到主程序的第一行(比如55f87a6a2000-55f87a6a4000 r-xp ... /home/you/test),前面的0x55f87a6a2000就是加载基址。 - 用绝对PC减去加载基址,得到可执行文件内的相对地址,再传给addr2line。
比如你运行程序得到的绝对PC是0x55f87a6a369d,减去基址0x55f87a6a2000得到0x169d,然后执行:
addr2line -e test 0x169d
就能看到对应的文件名和行号了。
嫌手动计算麻烦的话,也可以用addr2line的--adjust-vma参数直接指定加载基址:
addr2line -e test --adjust-vma=0x55f87a6a2000 0x55f87a6a369d
3. 避坑提醒
- 编译时必须加
-g:没有调试符号的话,addr2line只能输出??:0,啥有用信息都没有。 - 注意PC的指令偏移:部分架构(比如x86-64)中,
unw_get_ip()返回的是下一条要执行的指令地址,这时候需要给PC减1,才能让addr2line对应到当前代码行。 - 动态链接库的函数:如果回溯里有.so中的函数,要给
addr2line的-e参数指定对应的动态库文件,而不是主程序。
内容的提问来源于stack exchange,提问作者Raees Rajwani
相关产品推荐
相关产品推荐

