将库与不兼容的依赖关联起来

Ale*_*ato 8 c++ linker symbols shared-libraries libpng

我正在开发一个需要两个第三方库(libfoo.solibbar.so)的C++项目.我的操作系统是Linux.

libfoo.so动态链接到libpng14.so.14(1.4.8)(编辑1)

libbar.so似乎静态链接到libpng libpng 1.2.8 的unknwon版本(编辑1)

我说"似乎是"因为:

  • ldd libbar.so png没有显示任何内容
  • nm -D libbar.so | grep png_read_png 说"004f41b0 T png_read_png"
  • less libbar.so | grep png_read_png 说"4577:004f41b0 738 FUNC GLOBAL DEFAULT 10 png_read_png"

当我启动我的程序时,它会中止:

terminate called after throwing an instance of 'char const*'
Run Code Online (Sandbox Code Playgroud)

这是gdb回溯:

#0  0xb7ffd424 in __kernel_vsyscall ()
#1  0xb5e776a1 in raise () from /lib/libc.so.6
#2  0xb5e78de2 in abort () from /lib/libc.so.6
#3  0xb60a997f in __gnu_cxx::__verbose_terminate_handler() () from /usr/lib/gcc/i686-pc-linux-gnu/4.4.5/libstdc++.so.6
#4  0xb60a78a5 in ?? () from /usr/lib/gcc/i686-pc-linux-gnu/4.4.5/libstdc++.so.6
#5  0xb60a78e2 in std::terminate() () from /usr/lib/gcc/i686-pc-linux-gnu/4.4.5/libstdc++.so.6
#6  0xb60a7a21 in __cxa_throw () from /usr/lib/gcc/i686-pc-linux-gnu/4.4.5/libstdc++.so.6
#7  0xb5abf76d in ?? () from /usr/lib/libfreeimage.so.3
#8  0xb6fb9346 in png_error () from lib/libfsdk.so
#9  0xb6fa2a59 in png_create_read_struct_2 () from lib/libfsdk.so
#10 0xb6fa2b7a in png_create_read_struct () from lib/libfsdk.so
#11 0xb5abfa44 in ?? () from /usr/lib/libfoo.so
#12 0xb5aa766b in FreeImage_LoadFromHandle () from /usr/lib/libfreeimage.so.3
#13 0xb5aa59f6 in FreeImage_LoadFromMemory () from /usr/lib/libfreeimage.so.3
#14 0xb68a94a5 in Foo::Image::load (this=0xb4eff560, input=...)
Run Code Online (Sandbox Code Playgroud)

如您所见,在属于libfoo.so的Foo :: Image :: load中抛出异常

禁用我的代码使用libbar.so并删除它的链接,Foo :: Image :: load不会抛出任何异常并且工作正常.

所以我想这可能是由于符号表中的一些歧义.我该如何解决?

编辑1

png_access_version_number()

  • 在libbar.so链接后,png_access_version_number()返回10208:版本1.2.8
  • 如果没有链接libbar.so,则png_access_version_number()返回10408:版本1.4.8

Emp*_*ian 5

由于您无法重建任何一个库,并且由于符号冲突而不允许库驻留在同一个“动态链接器命名空间”中,因此您唯一的选择是隔离它们。

您可以通过使用dlopen("lib*.so", RTLD_LOCAL)(对于一个或两个库)来实现这一点,而不是直接链接到它们。

如果您只需要来自 eg 的几个符号,这可能是可行的libfoo.so——您可以简单地使用dlsym而不是直接调用函数。

如果您对两个库都有“太多”依赖项,那么您的其他解决方案可能是构建一个“内插器”库。假设您想插入libbar.so,并且您需要bar1(), bar2(), ...bar1000()从中插入。

编写(或使用简单的 Perl 脚本生成)一个如下所示的源文件:

static void *handle;
void *bar1()
{
   static void* (*pfn)(void *arg1, void *arg2, void *arg3, ..., argN);
   if (pfn == NULL) {
      if (handle == NULL)
        handle = dlopen("libbar.so", RTLD_LOCAL|RTLD_LAZY);
      pfn = dlsym(handle, "bar1");
   }
   return (*pfn)(arg1, arg2, ..., argN);
}
... repeat for all other libbar functions you depend on
Run Code Online (Sandbox Code Playgroud)

现在编译这个源libbar_interposer.so并将其链接到它,并将您的应用程序链接到它(C++由于名称修改,这将不起作用,仅适用于普通的C)。瞧,应用程序没有源代码更改,并且您仍然隔离了libbar.so它的符号,因此应用程序的其余部分将不可见,特别是不会与libpng.