当我调用库函数时,我试图找出调用哪个确切的函数(从哪个包含文件)。
所以,我有这个程序,
#include <stdio.h>
#include <math.h>
int twice(int input) {
int output;
output = input * 2;
return output;
}
int main(int argc, char **argv) {
printf("Hello World and %f\n", sin(1));
printf("Output: %d\n", twice(3));
printf("Here is the end of the program... %d\n", 3);
}
我想看看哪个printf被调用。 (我知道在这种情况下,它是stdio.h中的那个,但这只是一个例子。)
我编译文件:
g++ -g test.cpp
然后转储汇编代码
objdump --source a.out > test.objdump
在test.objdump中,我看到像
这样的行int main(int argc, char **argv) {
400528: 55 push %rbp
400529: 48 89 e5 mov %rsp,%rbp
40052c: 48 83 ec 10 sub $0x10,%rsp
400530: 89 7d fc mov %edi,-0x4(%rbp)
400533: 48 89 75 f0 mov %rsi,-0x10(%rbp)
printf("Hello World and %f\n", sin(1));
400537: f2 0f 10 05 91 01 00 movsd 0x191(%rip),%xmm0 #4006d0<__dso_handle+0x50>
40053e: 00
40053f: bf 88 06 40 00 mov $0x400688,%edi
400544: b8 01 00 00 00 mov $0x1,%eax
400549: e8 c2 fe ff ff callq 400410 <printf@plt>
printf("Output: %d\n", twice(3));
40054e: bf 03 00 00 00 mov $0x3,%edi
400553: e8 bc ff ff ff callq 400514 <_Z5twicei>
400558: 89 c6 mov %eax,%esi
40055a: bf 9c 06 40 00 mov $0x40069c,%edi
40055f: b8 00 00 00 00 mov $0x0,%eax
400564: e8 a7 fe ff ff callq 400410 <printf@plt>
printf("Here is the end of the program... %d\n", 3);
400569: be 03 00 00 00 mov $0x3,%esi
40056e: bf a8 06 40 00 mov $0x4006a8,%edi
400573: b8 00 00 00 00 mov $0x0,%eax
400578: e8 93 fe ff ff callq 400410 <printf@plt>
40057d: b8 00 00 00 00 mov $0x0,%eax
}
似乎printf函数的地址在callq行中给出:400410。但是当我这样做时
addr2line -e a.out 0x400410
(我尝试过没有0x的400410),我得到
??:0
它没有给我printf函数的位置。有人可以指出我的过程中的错误。是否有一些选项g ++我没有通过?
真的很感激帮助。谢谢!
答案 0 :(得分:3)
您正在将代码编译为目标代码,然后从目标代码中转储汇编代码。此时,链接器尚未将代码链接在一起,因此printf
仅是过程链接表中的符号。只有当程序被链接时才会出现该定义。
编辑:第二眼看看你的编译器选项,我看到可执行文件正在链接在一起。如果您确实有内置的调试符号,则可能需要指定要与addr2line
一起使用的部分。
至于使用printf
,我相信addr2line
只能找到可执行文件本地函数的定义,而不能找到外部库定义的函数。从test.cpp
的角度来看,printf
存在于其自身之外,并且不能在其下定义一条线,因为只有被链接的库记录。如果该库(本例中为glibc)未编译为调试版本,则无法知道它所定义的文件和行号。
答案 1 :(得分:2)
如果你只是想找到printf()
的声明(在C或C ++中,你的问题并不清楚你使用的是什么),你可以这样做:
g++ -E test.cpp | less
-E
表示:
-E仅预处理;不编译,汇编或链接
然后为printf
搜索前进 / 以查找声明(它必须在使用之前声明,因此必须是第一次命中)。然后向后搜索? ^#
,找到包含该文件的文件。
不需要编译,链接或对象转储!
我还建议您查看ack
和ctags
,这两种方法都可以高效地查找大型源代码树中的符号。
进一步评论后......
通过编译和链接代码来查找定义可能并不总是可行的。您已经使用printf()
看到了这一点,您没有构建它,也可能不存在于您的计算机上。还要考虑用其他语言编写的定义,或者在运行时解析的定义。
答案 2 :(得分:1)
addr2line只能找到包含在给定可执行文件中的定义,因此它不会提及有关printf
给出答案的任何内容:
rafal:~/test$ addr2line -f 0x400448
??
??:0
rafal:~/test$ addr2line -f 0x400554
_Z5twicei
/home/rafal/test/c.cpp:4
要查找有关共享库中函数的信息,可以使用gdb
gdb a.out
(gdb) info symbol 0x400448
printf@plt in section .plt of /home/rafal/test/a.out
(gdb) info symbol 0x400554
twice(int) in section .text of /home/rafal/test/a.out
答案 3 :(得分:0)
试试这个:
g++ -g -static test.cpp
这应该可以消除所有困惑。
没有针对共享库的-static
gcc链接。这意味着像printf这样的函数根本不在你的程序中,而是在运行时从/ lib加载。这样的空间效率更高,但可能稍慢(尽管整个系统运行速度更快)。您可以使用GDB检查正在运行的程序的内容,但要注意共享代码意味着存在很多令人困惑的gubbins,如GOT和PLT等等。
编译为静态二进制文件会拉入所有系统函数并将它们放入程序中。请注意,这不适用于所有可能的库(如果它们不提供静态替代)。