有没有一种可靠的方法可以知道哪些库可以在 elf 二进制文件中 dlopen()ed?

Is there a reliable way to know what libraries could be dlopen()ed in an elf binary?

基本上,我想获取二进制文件可能加载的库列表。

我想出的不可靠的方法似乎有效(可能有误报):

comm -13 <(ldd elf_file | sed 's|\s*\([^ ]*\)\s.*||'| sort -u) <(strings -a elf_file | egrep '^(|.*/)lib[^:/]*\.so(|\.[0-9]+)$' | sort -u)

这不可靠。但它提供了有用的信息,即使二进制文件被剥离了。

是否有一种可靠的方法来获取此信息而不会出现误报?

编辑:更多上下文。

Firefox 正在从使用 gstreamer 过渡到使用 ffmpeg。 我想知道什么版本的 libavcodec.so 会起作用。 libxul.so 使用 dlopen() 用于许多可选功能。 库名称是硬编码的。所以,上面的命令有帮助 在这种情况下。

我对包管理和二进制依赖项也很感兴趣。 我知道您可以通过 readelf -d 获得直接依赖关系,依赖关系 与 ldd 的依赖关系。我想知道可选的依赖项,因此问题。

ldd 告诉您二进制文件链接到的库。这些不是程序 可以 使用 dlopen 打开的那些。

dlopen 的签名是

void *dlopen(const char *filename, int flag);

所以你可以,仍然不可靠,运行 strings 在二进制文件上,但是如果库名称不是静态字符串,这仍然会失败,但在程序执行期间从某处构建或读取 - 最后一种情况意味着您的问题的答案是 "no"... 不可靠。 (例如,库文件的名称可以从网络、Unix 套接字中读取,甚至可以即时解压缩。一切皆有可能!——尽管我自己不推荐任何这些想法……)

编辑:另外,正如 John Bollinger 提到的,可以从配置文件中读取库名称。

edit:您也可以尝试用您的系统调用替换 dlopen 系统调用(这是由 Boehm 垃圾收集器用 malloc,例如),所以它会打开库,但也会在某处记录它的名字。但是如果程序在执行过程中没有打开特定的库,你还是不会知道的。

(我关注 Linux;我想我的大部分答案都适用于每个 POSIX 系统;但在 MacOSX 上 dlopen 需要 .dylib dynamic library 个文件,而不是 .so 个共享对象)

一个程序甚至可以在某个临时文件 /tmp/foo1234.c 中发出一些 C 代码,fork 一些 [=] 将 /tmp/foo1234.c 编译到共享库 /tmp/foo1234.so 17=] 命令 - 在程序运行时生成并执行 - 可能删除 /tmp/foo1234.c 文件 - 因为不再需要它了 - 和 dlopen /tmp/foo1234.so (甚至可能在 dlopen 之后删除 /tmp/foo1234.so),所有这些都在同一个过程中。我的 GCC MELT plugin for gcc does exactly this, and so does Bigloo, and the GCCJIT 图书馆正在做一些接近的事情。

所以总的来说,你的任务是不可能的,甚至是没有意义的。

Is there a reliable way to get this information without possible false-positives?

不,没有可靠的方法来获得这样的信息而不误报(你可以证明等同于halting problem, or to some other undecidable problem). See also Rice's theorem

实际上,大多数 dlopen 发生在某些配置提供的插件上。在配置文件中可能没有这样命名(例如,一些 Foo 程序可能有一个约定,比如在一些 foo.conf 配置文件中名为 bar 的插件是由 foo-bar.so 插件).

但是,您可能会发现一些 启发式 近似值。大多数程序做一些 dlopen 有一些插件 convention 在插件中请求一些特定的 符号名称 。您可以搜索定义这些名称的共享对象。当然你会得到误报。

例如zsh shell accepts plugins called zsh modules. the example模块显示enables_, boot_features_ 等... zsh 模块中需要函数。您可以使用 nm -D 查找提供这些的 *.so 文件(因此发现插件 可能 可能 可由 zsh)

(我不相信这种方法是值得的;事实上,您通常应该知道哪些插件在您的系统上对哪些应用程序有用)

顺便说一句,你可以使用 strace(1) on the execution of some command to understand the syscalls it is doing, hence the plugins it is loading. You might also use ltrace(1), or pmap(1) (on some given process), or simply -for a process 1234- use cat /proc/1234/maps to understand its virtual address space, hence the plugins it has already loaded. See proc(5).

请注意 straceltracepmap 存在于 Linux 上,但许多 POSIX 系统具有 相似 程序。

此外,程序可以在运行时生成一些机器代码并执行它(SBCL does that at every REPL interaction!). Your program could also use some JIT techniques (e.g. with libjit, llvm, asmjit, GCCJIT or with hand-written code...) to do likewise. So plugin-like behavior can happen without dlopen (and you might mimic dlopen with mmap calls and some ELF 重定位处理)。


附录:

如果您从其打包版本(例如 Debian 上的 iceweasel 包)安装 firefox,它的包可能会处理依赖项