我正在尝试在Unix中使用addr2line命令,但每次它都提供与??:0相同的输出。我以addr2line -e a.out 0x4005BDC
命令。我使用valgrind
工具运行此a.out可执行文件时获取此地址以查找内存泄漏。我还用-g
选项编译了源代码。
答案 0 :(得分:28)
您也可以使用gdb而不是addr2line来检查内存地址。在gdb中加载可执行文件并打印存储在该地址的符号名称。 16 Examining the Symbol Table
(gdb) info symbol 0x4005BDC
答案 1 :(得分:20)
请检查:
-g
编译,addr2line
仅支持函数具有调试信息,使用-g
编译.text
部分中的偏移量。在.text
部分中,表示地址应指向二进制文件中的指令以下是来自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
??:?
我们可以从上面的输出中得出一些结论:
-g
标志生成的代码段(这意味着该段具有调试信息)才能成功生成文件名和亚麻码信息。 -g
标志编译的函数体的偏移量都将成功输出文件名和亚麻布。偏移量0x40054b
是ret
函数的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
在我的情况下, 0x400534
是main
的地址。 0x400408
也是a.out
中的有效函数地址,但它是由GCC生成/导入的一段代码,没有调试信息。 (在这种情况下,__libc_csu_init
。您可以使用readelf -a your_exe
查看可执行文件的布局。)
addr2line
失败的其他时间是你包含一个没有调试信息的库。
答案 4 :(得分:4)
尝试添加-f
选项以显示函数名称:
addr2line -f -e a.out 0x4005BDC