如何知道程序调用了库中的哪些函数?

8
假设我有一个二进制库(*.so)和使用该库的二进制程序(即我没有这两者的源代码)。
如何在运行时找出调用了库中哪些函数。我想知道它们的名称,但不需要实时信息。
这两个二进制文件都不包含调试符号。

如果你正在进行静态链接(即程序启动后不会动态加载库中的符号),那么只需将程序链接起来,但不包括库文件。错误消息将指向在库中使用的任何内容。如果你的程序在运行时动态加载任何符号,则更加困难(如果从库中加载失败,代码可以做任何喜欢的事情,包括查找备用库)- 你需要检查从库中加载的代码源或找到其他方法来监视其行为。 - Peter
我一直希望有像strace这样的东西,可以显示实际发出的系统调用。但我想这需要内核支持。至少概述的静态分析将给我一个提示,哪些库函数没有被调用。这是我的实际用例。 - highsciguy
strace 能帮到你吗? - alk
它会显示函数名称,当被调用时。试一下吧。 - alk
我快速编译了来自https://gist.github.com/tailriver/30bf0c943325330b7b6a的示例,以及一个没有dlopen版本的示例,并发现在我的x86_64平台上,如果没有任何参数,strace不会打印库函数名称。也许你有一些命令行开关在脑海中? - highsciguy
显示剩余4条评论
2个回答

4

Objdump命令从二进制文件中转储外部符号引用。一般使用-T选项运行它,以便转储一个二进制文件的外部符号引用。

例如,在/bin/ls上运行objdump -T

/bin/ls:     file format elf64-x86-64

DYNAMIC SYMBOL TABLE:
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.3   __ctype_toupper_loc
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 getenv
0000000000000000      DF *UND*  0000000000000000              cap_to_text
0000000000000000      DO *UND*  0000000000000000  GLIBC_2.2.5 __progname
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 sigprocmask
...

等等,转储包括所有外部符号引用,不仅仅是函数。手册页面解释了第二列中代码的含义,指定外部符号引用的类型。这里,看起来我们对DF感兴趣,表示动态函数调用。在这里,我们可以看到一些熟悉的C库函数,如getenv()sigprocmask(),被"/bin/ls"引用。

无法识别的库调用可能是头文件中的内部宏使用库的内部实现细节导致的。这可能就是“__ctype_toupper_loc”的全部含义。

当使用此选项与C++代码一起使用时,您还需要指定-C选项以取消编译C++符号。

当然,特定二进制文件携带某个库函数的外部引用并不能保证二进制文件在运行时实际调用该函数。


0

暂时忽略使用dlopen/dlsysm的可能性,通常你只需要使用类似nm a.out(或者你可执行文件的名称)的命令。

对于C++,你可能想要添加--demangle来解开C++名称,使其更易读。你也可以使用-u来获取仅未定义的外部符号(这将删除一些你可能不关心的符号)。

如果程序正在使用dlopen/dlsym,那么了解它链接的内容的唯一方法就是在运行时监视它。它可能(例如)打开一个文本文件,读取一些字符串,然后使用这些字符串作为库和函数的名称进行链接。更改该文本文件可能会完全改变它链接的函数,因此你无法根据可执行文件本身的内容确定这一点。


有没有一种方法可以提取dlopen / dlsym调用以推断它的使用方式?如果只使用固定字符串参数来使用dlsym,则我至少有机会了解发生了什么。 - highsciguy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接