19

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

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

有没有其他选择?

4

5 回答 5

32

您的问题的常见解决方案是声明一个函数指针表,执行单个 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

PS 使用 dlsym 和指针单独访问您的 API 函数本身不会导致内存损坏和崩溃。很可能你只是有错误。

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

在主可执行文件中,dlopen("libdrmaa_wrapper.so", RTLD_NOW). 如果dlopen(且仅当)libdrmaa.so在运行时存在并提供您在api_table. 如果确实成功,则只需一次dlsym调用即可让您访问整个 API。

于 2009-07-01T07:11:01.200 回答
2

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

于 2009-07-01T05:17:49.727 回答
2

使用以下类型的代码

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

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

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

这当然是前面提出的一种 C++ 风格的结构方法。

于 2013-05-04T06:58:06.850 回答
1

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

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

$ implib-gen.py libxyz.so
$ gcc myapp.c libxyz.tramp.S libxyz.init.c ...
于 2018-02-16T09:55:12.477 回答
-1

您的问题是未解析符号的解析很早就完成了 - 在 Linux 上,我相信数据符号在进程启动时已解析,而函数符号是懒惰完成的。因此,取决于您未解析的符号,以及您正在进行的静态初始化类型 - 您可能没有机会进入您的代码。

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

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

于 2009-07-01T06:37:03.493 回答