在C程序中调用哪个函数?

时间:2012-01-08 00:34:35

标签: debugging gcc

当我调用库函数时,我试图找出调用哪个确切的函数(从哪个包含文件)。

所以,我有这个程序,

#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 ++我没有通过?

真的很感激帮助。谢谢!

4 个答案:

答案 0 :(得分:3)

您正在将代码编译为目标代码,然后从目标代码中转储汇编代码。此时,链接器尚未将代码链接在一起,因此printf仅是过程链接表中的符号。只有当程序被链接时才会出现该定义。

编辑:第二眼看看你的编译器选项,我看到可执行文件正在链接在一起。如果您确实有内置的调试符号,则可能需要指定要与addr2line一起使用的部分。

至于使用printf,我相信addr2line只能找到可执行文件本地函数的定义,而不能找到外部库定义的函数。从test.cpp的角度来看,printf存在于其自身之外,并且不能在其下定义一条线,因为只有被链接的库记录。如果该库(本例中为glibc)未编译为调试版本,则无法知道它所定义的文件和行号。

答案 1 :(得分:2)

如果你只是想找到printf()声明(在C或C ++中,你的问题并不清楚你使用的是什么),你可以这样做:

g++ -E test.cpp | less

-E表示:

  

-E仅预处理;不编译,汇编或链接

然后为printf搜索前进 / 以查找声明(它必须在使用之前声明,因此必须是第一次命中)。然后向后搜索 ^#,找到包含该文件的文件。

不需要编译,链接或对象转储!

我还建议您查看ackctags,这两种方法都可以高效地查找大型源代码树中的符号。


进一步评论后......

通过编译和链接代码来查找定义可能并不总是可行的。您已经使用printf()看到了这一点,您没有构建它,也可能不存在于您的计算机上。还要考虑用其他语言编写的定义,或者在运行时解析的定义。

ackctags可能是您的最佳选择。

祝你好运!

答案 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等等。

编译为静态二进制文件会拉入所有系统函数并将它们放入程序中。请注意,这不适用于所有可能的库(如果它们不提供静态替代)。