链接具有不兼容依赖项的库
Posted
技术标签:
【中文标题】链接具有不兼容依赖项的库【英文标题】:Linking libraries with incompatible dependecies 【发布时间】:2012-01-22 01:09:51 【问题描述】:我正在开发一个需要两个第三方库(libfoo.so 和 libbar.so)的 C++ 项目。我的操作系统是 Linux。
libfoo.so 动态链接到 libpng14.so.14 (1.4.8) (EDIT 1)
libbar.so 似乎静态链接到 一个未知版本的 libpng libpng 1.2.8 (EDIT 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*'
这是 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=...)
如您所见,在属于 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
【问题讨论】:
您必须使用nm -D
来查看共享库的动态符号。仅nm
用于调试符号,在大多数发行版中已被剥离。
我试图了解哪个版本的 libpng 静态链接到 libbar.so
如果不重新编译任何共享库,这实际上是无法解决的,因此它们都使用相同的 libpng 库。
@nos 你确定吗?我不能这样做。我会尝试询问 libbar 开发人员是否可以给我一个带有 libpng 1.4 的版本。但是...是否可以从 libbar.so 中去除 libpng 函数?
@Alessandro Pezzato 即使这是可行的,libbar.so 中也会有代码需要 libpng 1.2 的数据结构和行为,如果 libpng 1.2 和 1.4 之间发生任何变化,那将不起作用,除非 libpng1 .4 保证二进制向后兼容。
【参考方案1】:
由于您无法重建其中任何一个库,并且由于符号冲突,不能允许这些库驻留在同一“动态链接器命名空间”中,因此您唯一的选择是隔离他们。
您可以通过使用dlopen("lib*.so", RTLD_LOCAL)
(对于一个或两个库)来实现这一点,而不是直接链接到它们。
如果您只需要几个符号,这可能是可行的,例如libfoo.so
-- 你可以简单地使用dlsym
而不是直接调用函数。
如果您对这两个库都有“太多”依赖项,那么您的其他解决方案可能是构建一个“interposer”库。假设您要插入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
现在编译并将此源链接到libbar_interposer.so
并将您的应用程序链接到它(这不适用于C++
,因为名称修改,仅适用于plain-C
)。瞧,应用程序的源代码没有更改,您仍然隔离了libbar.so
,因此它的符号对应用程序的其余部分不可见,特别是不会与libpng
中的任何符号冲突。
【讨论】:
值得注意的是,如果你这样做,你应该小心不要在 libfoo 和 libbar 之间传递 libpng 数据结构。另外,如果可能,尝试重建 libbar 以动态链接到 libpng。 谢谢!这给了我一些希望。我的解决方案是使用 libbar.so 拆分程序的一部分,并通过共享内存与主程序交换数据。但这个解决方案似乎更优雅。 我使用了您的解决方案,但有一些变化:在新库“libbar_wrapper.so”中编写一些“包装器”函数,并使用dlopen
和dlsym
加载它。我需要两个标志RTLD_LOCAL|RTLD_LAZY
。它有效!史诗般的答案!以上是关于链接具有不兼容依赖项的库的主要内容,如果未能解决你的问题,请参考以下文章
如何将依赖于 Styled Components 的 React 组件库提供给另一个也具有 Styled Comopnents 依赖项的库?