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

时间:2009-07-01 05:05:15

标签: c++ shared-libraries dynamic-loading dlsym

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

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

有没有其他选择?

5 个答案:

答案 0 :(得分: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

P.S。使用dlsym和指针单独访问API函数本身不会导致内存损坏和崩溃。很可能你只是有虫子。

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

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

答案 1 :(得分:2)

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

答案 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方法。

答案 3 :(得分:0)

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

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

dlopen

答案 4 :(得分:-1)

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

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

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