如何在Linux中使用addr2line命令?

Pra*_*rak 46 c linux valgrind addr2line

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

ks1*_*322 30

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

(gdb) info symbol 0x4005BDC 
Run Code Online (Sandbox Code Playgroud)

  • 您也可以直接列出代码:`(gdb)list*0x4005BDC` (8认同)
  • 另请参阅(gdb)信息行*0x4005BDC (6认同)

Kun*_*ing 21

请检查:

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

addr2line用法

以下是来自的消息man addr2line.

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

addresses应在一个可执行或可重定位目标的部分的偏移地址.

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

例.

就拿helloworld作为一个例子.

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

在编译之后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)
Run Code Online (Sandbox Code Playgroud)

代码的最左列是二进制文件中的偏移量.__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
??:?
Run Code Online (Sandbox Code Playgroud)

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

  1. 只有使用-gflag 生成的代码段(这意味着该段具有调试信息)才能成功生成文件名和亚麻码信息.
  2. 并非所有使用-gflag 编译的函数体的偏移量都将成功输出文件名和亚麻布.偏移0x40054b是在最后指令ret的指令main功能,但我们无法得到的信息.


tde*_*ton 17

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

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

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

addr2line -e a.out -j .text 0xbdc
Run Code Online (Sandbox Code Playgroud)

并查看是否能获得您的行号.

  • 等等,有什么抵消?是不是addr2line只从symtab(或dyntab)获取地址? (2认同)

Mat*_*Mat 11

这正是你如何使用它.但是,您所拥有的地址可能与源代码中的某些内容不对应.

例如:

$ 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
Run Code Online (Sandbox Code Playgroud)

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

addr2line如果您包含没有调试信息的库,则失败的其他时间.


Pen*_*eng 6

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

addr2line -f -e a.out 0x4005BDC
Run Code Online (Sandbox Code Playgroud)