如何在Linux中使用addr2line命令?

时间:2011-10-04 13:28:08

标签: c linux valgrind addr2line

我正在尝试在Unix中使用addr2line命令,但每次它都提供与??:0相同的输出。我以addr2line -e a.out 0x4005BDC命令。我使用valgrind工具运行此a.out可执行文件时获取此地址以查找内存泄漏。我还用-g选项编译了源代码。

5 个答案:

答案 0 :(得分:28)

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

(gdb) info symbol 0x4005BDC 

答案 1 :(得分:20)

请检查:

  • 二进制文件中的所有函数是否都使用-g编译,addr2line仅支持函数具有调试信息,使用-g编译
  • 您的偏移量是否为有效偏移量。这意味着您的偏移量不应该是虚拟内存地址,而应该只是.text部分中的偏移量。在.text部分中,表示地址应指向二进制文件中的指令

addr2line用法

以下是来自man addr2line的消息。

  

addr2line - 将地址转换为文件名和行号。

addresses应该是可执行文件中的地址或可重定位目标文件的一部分中的偏移量。

输出类似于FILENAME:LINENO,源文件名和文件中的行号

实施例

helloworld为例。

#include <stdio.h>
int main()
{
    printf("hello\n");
    return 0;
}

使用gcc -g hello.c对其进行编译后,我们首先可以使用objdump来了解生成的a.out文件中的偏移信息。

以下是转储拆卸组件的一部分:

Disassembly of section .text:

0000000000400440 <_start>:
  400440:       31 ed                   xor    %ebp,%ebp
  400442:       49 89 d1                mov    %rdx,%r9
  400445:       5e                      pop    %rsi
  400446:       48 89 e2                mov    %rsp,%rdx
  400449:       48 83 e4 f0             and    $0xfffffffffffffff0,%rsp
  40044d:       50                      push   %rax
  40044e:       54                      push   %rsp
  40044f:       49 c7 c0 c0 05 40 00    mov    $0x4005c0,%r8
  400456:       48 c7 c1 50 05 40 00    mov    $0x400550,%rcx
  40045d:       48 c7 c7 36 05 40 00    mov    $0x400536,%rdi
  400464:       e8 b7 ff ff ff          callq  400420 <__libc_start_main@plt>
  400469:       f4                      hlt
  40046a:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)

  ...

   0000000000400536 <main>:

#include <stdio.h>
int main()
{
  400536:       55                      push   %rbp
  400537:       48 89 e5                mov    %rsp,%rbp
    printf("hello\n");
  40053a:       bf d4 05 40 00          mov    $0x4005d4,%edi
  40053f:       e8 cc fe ff ff          callq  400410 <puts@plt>
    return 0;
  400544:       b8 00 00 00 00          mov    $0x0,%eax
}
  400549:       5d                      pop    %rbp
  40054a:       c3                      retq
  40054b:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

代码的最左列是二进制文件中的偏移量。 __start函数来自标准C库,并且在没有调试信息的情况下进行预编译。 main函数来自我们的helloworld代码,它具有调试信息,因为我们使用-g编译文件。

以下是addr2line的输出:

$ addr2line -e a.out 0x400442 #offset in the `__start` function
??:?
$ addr2line -e a.out 0x400536 #offset in the `main` function
hello.c:21
$ addr2line -e a.out 0x40054b -f #The last instruction of the `main` function
main
??:?

我们可以从上面的输出中得出一些结论:

  1. 只有使用-g标志生成的代码段(这意味着该段具有调试信息)才能成功生成文件名和亚麻码信息。
  2. 并非所有使用-g标志编译的函数体的偏移量都将成功输出文件名和亚麻布。偏移量0x40054bret函数的main指令之后的最后一条指令,但我们无法获取信息。

答案 2 :(得分:16)

您需要为addr2line指定偏移,而不是虚拟地址(VA)。假设您关闭了地址空间随机化,您可以使用完整的VA,但在大多数现代操作系统中,地址空间随机化以用于新进程。

给定valgrind的VA 0x4005BDC,在内存中找到进程或库的基址。通过在程序运行时检查/proc/<PID>/maps文件来执行此操作。感兴趣的行是您的流程的text段,可通过权限r-xp以及您的程序或库的名称进行识别。

假设基础VA为0x0x4005000。然后你会发现valgrind提供的VA和基础VA之间的区别:0xbdc。然后,将其提供给add2line:

addr2line -e a.out -j .text 0xbdc

看看是否能获得你的行号。

答案 3 :(得分:10)

这正是你如何使用它的。您的地址可能与源代码中的某些内容不对应。

例如:

$ 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
在我的情况下,

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

addr2line失败的其他时间是你包含一个没有调试信息的库。

答案 4 :(得分:4)

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

addr2line -f -e a.out 0x4005BDC