C ++中dlsym()和dlopen()的替代品

问题描述 投票:17回答:5

我有一个应用程序,其中一部分使用共享库。这些库在编译时链接。 在运行时,加载程序期望共享对象位于LD_LIBRARY_PATH中,如果没有找到整个应用程序崩溃并出现错误“无法加载共享库”。请注意,不能保证客户端将拥有该库,在这种情况下我想要留下合适的错误消息的应用程序也应该独立的部分正常工作。

为此,我使用dlsym()dlopen()在共享库中使用API​​。这个问题是如果我在API中有很多功能,我必须使用dlsym()和ptrs单独访问它们,在我的情况下会导致内存损坏和代码崩溃。

这有什么替代方案吗?

c++ shared-libraries dynamic-loading dlsym
5个回答
31
投票

你的问题的常见解决方案是声明一个函数指针表,做一个dlsym()来找到它,然后通过指向该表的指针调用所有其他函数。示例(未经测试):

// libfoo.h
struct APIs {
   void  (*api1)(void);
   void *(*api2)(int);
   long  (*api3)(int, void *);
};

// libfoo.cc
void fn1(void) { ... }
void *fn2(int) { ... }
long fn3(int, void *) { ... }

APIs api_table = { fn1, fn2, fn3 };


// client.cc
#include "libfoo.h"
...
  void *foo_handle = dlopen("libfoo.so", RTLD_LAZY);
  if (!foo_handle) {
     return false;            // library not present
  }
  APIs *table = dlsym(foo_handle, "api_table");
  table->api1();              // calls fn1
  void *p = table->api2(42);  // calls fn2
  long x = table->api3(1, p); // calls fn3

附:使用dlsym和指针单独访问API函数本身并不会导致内存损坏和崩溃。很可能你只是有bug。

编辑: 您可以使用与第三方库完全相同的技术。创建一个libdrmaa_wrapper.so并将api_table放入其中。将包装器直接链接到libdrmaa.so

在主要的可执行文件中,dlopen("libdrmaa_wrapper.so", RTLD_NOW)。如果(并且仅当)dlopen在运行时出现并且提供您在libdrmaa.so中使用的所有API函数,则此api_table将成功。如果成功,单个dlsym调用将允许您访问整个API。


2
投票

您可以使用另一个首先检查所有必需库的应用程序来包装您的应用程序,如果缺少某些内容,则会很好地解决错误,但如果一切正常,则执行真正的应用程序。


1
投票

使用以下类型的代码

Class DynLib
{
    /* All your functions */
    void fun1() {};
    void fun2() {};
    .
    .
    .
}

DynLib* getDynLibPointer()
{
    DynLib* x = new Dynlib;
    return x;
}

使用dlopen()在运行时加载此库。并使用dlsym()并调用返回DynLib对象的getDynLibPointer()。从这个对象你可以访问所有的功能jst作为obj.fun1() .....

这是前面提出的C ++样式的struct方法。


0
投票

您可能正在寻找Linux上某种形式的延迟库加载。它不是开箱即用的,但你可以通过创建一个小的静态存根库来轻松模仿它,它会在第一次调用它的任何函数时尝试dlopen所需的库(发出诊断消息并在dlopen失败时终止)然后转发所有来电。

这样的存根库可以手工编写,由项目/库特定的脚本生成或由通用工具Implib.so生成:

$ implib-gen.py libxyz.so
$ gcc myapp.c libxyz.tramp.S libxyz.init.c ...

-1
投票

您的问题是未解决的符号的解决方案很早就完成了 - 在Linux上我相信数据符号在进程启动时被解析,并且函数符号是懒惰地完成的。因此,根据您未解决的符号以及您正在进行的静态初始化,您可能无法获得代码。

我的建议是让一个包装器应用程序捕获返回代码/错误字符串“无法加载共享库”,然后将其转换为更有意义的东西。如果这是通用的,则每次添加新的共享库时都不需要更新它。

或者,您可以让您的包装器脚本运行ldd然后解析输出,ldd将报告您的特定应用程序找不到的所有库。

© www.soinside.com 2019 - 2024. All rights reserved.