如何在Linux中使用addr2line命令?

62

我正在尝试在Unix中使用addr2line命令,但每次输出都是??:0。我使用的命令是addr2line -e a.out 0x4005BDC。我在运行这个a.out可执行文件时使用了valgrind工具来查找内存泄漏时得到了这个地址。我还使用了-g选项编译源代码。

4个回答

39

你也可以使用gdb而不是addr2line来检查内存地址。在gdb中加载可执行文件并打印储存在该地址的符号的名称。16 检查符号表

(gdb) info symbol 0x4005BDC 

1
好主意。在那个地址设置断点并在gdb中运行程序,获取回溯信息可以提供关于发生了什么的良好信息。 - Mat
9
另请参阅 (gdb) info line * 0x4005BDC - user47559
1
使用“-g”标志编译和不使用该标志编译,(库)代码地址会相同还是不同? - WindChaser

30
您需要为addr2line指定一个偏移量(offset),而不是一个虚拟地址(VA)。假设您关闭了地址空间随机化,那么您可以使用完整的VA,但在大多数现代操作系统中,进程的地址空间都是随机化的。
通过valgrind给出的VA 0x4005BDC,在程序运行时检查/proc//maps文件,以找到您的进程或库在内存中的基地址。您要查看的行是进程的text段,它可以通过权限r-xp和程序或库的名称进行识别。
假设基地址是0x4005000。然后您需要计算valgrind提供的VA和基地址之间的差异:0xbdc。然后,将该值作为偏移量提供给addr2line:
addr2line -e a.out -j .text 0xbdc

再看看这是否能给你提供行号。


2
等等,什么是偏移量?addr2line不是只从symtab(或dyntab)中获取地址吗? - Alexander Malakhov

15

这就是正确使用它的方式。但是有可能你所拥有的地址并没有直接对应于你的源代码。

例如:

$ cat t.c
#include <stdio.h>
int main()
{
    printf("hello\n");
    return 0;
}
$ gcc -g t.c
$ addr2line -e a.out 0x400534
/tmp/t.c:3
$ addr2line -e a.out 0x400550
??:0

0x400534 是我这个程序中 main 函数的地址。在 a.out 中,0x400408 也是一个有效的函数地址,但它是由 GCC 生成或导入的一段代码,没有调试信息(在这种情况下,是 __libc_csu_init)。你可以使用 readelf -a your_exe 命令查看可执行文件的布局。

另外,当你包含没有调试信息的库时,addr2line 也会失败。


在我的代码中,我正在使用pthread和libconfing库。我不知道这些库是否有调试信息。 - Prak

13
尝试添加-f选项以显示函数名称:

尝试添加-f选项以显示函数名称:

addr2line -f -e a.out 0x4005BDC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接