通过运行应用程序以程序方式确定正在使用的共享库

Programatically determine shared libraries in use by running application

本文关键字:共享 运行 应用程序 方式确 程序      更新时间:2023-10-16

是否可以(如果可以,如何)确定应用程序在运行时使用的应用程序的共享库?基本上,我可以通过编程获得ldd的输出吗?首选的C/C++解决方案不仅仅是跳转到命令行上执行ldd。

考虑以下内容:我有一个驱动程序应用程序,它从共享库libfoo调用doAction()。我编译了一次应用程序,然后将LD_LIBRARY_PATH设置到一个适当的目录中,该目录包含定义了doAction()符号的libfoo。这样,我可以在不同的libfoo中实现多个doAction(),但只能编译一个应用程序一次。

一个真实世界的例子是一位教授让一班学生实现doAction()。学生们提交了一个共享库,教授可以简单地更改LD_LIBRARY_PATH来评估每个学生,而不是针对每个学生的doAction()实现编写一个测试工具。

我获取当前使用的库的目标是在运行时对库执行md5sum,以确保我调用的是正确的库。在这个设计的例子中,所有学生都会提交他们图书馆的md5sum,教授可以将正在运行的可执行文件+共享图书馆(数据库查找、日志到文件…)匹配给学生,以防止设置LD_LIBRARY_PATH时发生意外影响另一个学生的成绩(忘记将LD_LIBRARY_PATH更改为David的目录,并使用Bill的libfoo再次运行)。

因为看起来你在使用UNIX-y,所以只需使用dlopen,而不是针对缺失的符号动态链接驱动程序应用程序。

完整序列为:

  1. 以某种方式迭代所有提交的.so库文件名(也许您有一个带有studentname.so之类的目录)
  2. 加载每个库
  3. 获取入口点函数
  4. 称之为
  5. 卸载库(我想是可选的)

像这样:

void *lib = dlopen(filename, RTLD_LOCAL);
void *libfun = dlsym(lib, "doAction");
if (libfun == NULL)
    cout << "student failed by not providing doAction() in " << filename << endl;
else {
    void (*doAction)(void) = (void (*)(void)) libfun;
    // no, I can't remember the correct syntax for casting to function pointer
    cout << "calling " << filename << ":doAction()" << endl;
    doAction();
    // is there some way to tell if it succeeded?
    cout << "unloading " << filename << endl;
    dlclose(lib);
}

注:

  • 如果接口在每种情况下都是相同的(即void (*)()),则可以通过目录名和符号名进行配置,并且它适用于多个测试
  • 事实上,如果接口不是您所期望的,那么函数指针转换将做一些可怕的事情,因此要小心
  • 最后,如果学生使用C++,他们的函数名称符号将被破坏。告诉他们将入口点声明为extern "C" void doAction()以避免这种情况
  • RTLD_LOCAL标志应该阻止一个学生库中的任何内容干扰另一个(如果你不卸载),但添加其他标志可能是明智的
    • 具体来说,如果学生库有一个无法解析的外部引用,RTLD_NOW将导致dlopen失败(所以你可以通过失败来优雅地处理它):否则,当你调用doAction时,你的程序可能会崩溃

尽管我认为上面的比您直接寻求帮助的解决方案更好,但在仔细检查文档时,我也找到了dl_iterate_phdr的参考。如果您具体使用Linux,并且dl_phdr_info.dlpi_name实际上是文件名。。。你也许可以这样做。

不过我还是觉得它更丑。

如果您使用的是Linux,您可以使用dl_iterate_phdr函数:

dl_iterate_phdr()函数允许应用程序在运行时查询它加载了哪些共享对象。

http://linux.die.net/man/3/dl_iterate_phdr

在运行时,它不是一个应用程序,而是一个进程。

如果进程有pid 1234,则可以通过读取/proc/1234/maps(或更详细的/proc/1234/smaps)来获取其内存映射。该映射列出了特定的mmap-ed文件(尤其是共享库)。从应用程序内部读取/proc/self/maps

尝试

   grep so /proc/self/maps

明白我的意思。

顺便说一句,如果你有一个地址,dladdr函数会提供关于最近的符号和共享对象的信息。。。

附录

正如Rob Mayoff所回答的,dl_iterate_phdr可能是Linux 上最好的解决方案

如果这是Linux(我怀疑是否有通用的POSIX方法可以做到这一点,但我可能错了),您可能会对/proc/(pid)/maps的内容感兴趣。这为您的进程提供了映射的内存范围,您可以搜索md5sum()函数的地址所在的范围。

如果您在linux/unix中,您可以使用类似strace -o strace.log -f students_binary的strace。Strace跟踪所有系统调用,包括打开库的调用。然后,您可以解析任何文件的所有打开的strace.log,并对所有打开的文件执行md5sum