在动态库中包装不同版本的静态库

问题描述:

在我的项目中,存在来自第三方的对静态库(从现在开始称为libsomething)的依赖关系。最近,libsomething已成为另一个版本。我的任务是为我的软件提供对旧版和新版的支持。 libsomething在任何给定时间仅在运行时使用一个版本,但在程序运行之间应配置哪个版本。在动态库中包装不同版本的静态库

我在WinXP上使用MSVC2005,第二个目标是准备切换到Linux和GCC。

因为libsomething的两个版本都使用相同的符号,所以将它们链接到我的可执行文件中是不可能的,因为这两个版本的符号都会在链接时发生冲突。虽然我可以创建两个可执行文件(一个与旧版本链接,另一个使用新版本),但我无法实现在最终部署环境中调用哪个可执行文件(遗留原因)的决定。

我想出了为libsomething的每个版本创建一个动态库包装的想法,并根据一些配置文件在运行时链接它们。对于MSCV来说,这意味着要走上使用LoadLibrary(),GetProcAddress()等的道路,而在Linux上我将不得不使用dlopen()dlsym()

据我所知,使用libtool(即libtldl)正在包装这个平台依赖项以使用共享库。这是一条适合的路径吗?有更好的(或者至少是不同的)方式吗? libtldl的替代方案是否以开源方式存在?

它已经有几年了,但我想提出另一种完整性的解决方案。您可以生成所有必要功能的简单存根(而不是手动dlopendlsym),并在第一次调用时(或在程序启动时)确定需要哪个库版本,加载它并解析地址。

你可以写一个脚本,专门为您量身打造的项目,或者使用Implib.so工具:

# This will generate mylib.so.init.c and mylib.so.tramp.S 
# which implement stubs. These need to be linked to your 
# executable. 
$ gen-implib.py mylib.so 

Implib.so是仅支持Linux的ATM,但应该是很容易适应的Windows。

+0

是的,这几乎是我在2010年完成的事情。所以,我想这使得它成为可接受的解决方案。 :-) – fawick 2017-12-07 19:23:47

我知道你说你不能使用两个可执行文件,因为决定执行哪个可执行文件,但是不能在可执行文件之间来回exec,具体取决于在配置中选择哪个版本?

+0

你说得对,在这种情况下,这种方法确实产生了一个解决方案。虽然我更感兴趣通过链接来完成它。但是你的想法是聪明的开箱即用的思想! – fawick 2010-05-10 18:23:47

在Linux上,您可以更容易地链接到共享库并使用符号链接来更正版本 - IMO比使用dlopen() + dlsym()更容易。

新版本和旧版本的库因此您将创建共享库:

g++ -shared -o libshared.so.1.1 -Wl,-soname=libshared.so.1 -L. -Wl,--whole-archive libstatic.a.old -Wl,-no-whole-archive

g++ -shared -o libshared.so.1.2 -Wl,-soname=libshared.so.1 -L. -Wl,--whole-archive libstatic.a.new -Wl,-no-whole-archive

创建符号链接:

ln -s libshared.so.1.1 libshared.so.1 
ln -s libshared.so.1 libshared.so 

建设您的应用程序,将其链接到旧版本的库。我想这两个版本都是二进制兼容的(ABI没有损坏),但是新版本可能会有一些新的符号。

g++ -o myapp myapp.cpp -L. -lshared

由于共享库的SONAMElibshared.so.1您的应用程序依赖于它,并会在路径搜索libshared.so.1/etc/ld.so.confLD_LIBRARY_PATH

在你运行你的应用程序,您可以设置libshared.so.1符号链接指向libshared.so.1.2libshared.so.1.1


关于这里使用的连接选项一点信息:

--whole归档
对于--whole存档选项后,在命令行中提到的每个存档,包括在每一个目标文件 链接中的存档,而不是在存档中搜索所需的目标文件。这通常用于将归档文件转换为共享库,强制每个对象都包含在生成的共享库中。该选项可能会多次使用。
从gcc使用这个选项时有两点需要注意:首先,gcc不知道这个选项,所以你必须使用-Wl,-whole-archive。 其次,不要忘记在归档列表后面使用-Wl,-no-whole-archive,因为gcc会将自己的归档列表添加到您的 链接中,并且您可能不希望此标志也影响这些归档。

-soname = name
创建ELF共享对象时,请将内部DT_SONAME字段设置为指定的名称。当可执行文件与具有DT_SONAME字段的共享对象链接时,那么当可执行文件运行时,动态链接程序将尝试加载由DT_SONAME字段指定的共享对象 ,而不是使用赋予链接程序的文件名。

+0

很好的答案,谢谢。不幸的是,如你所说,这只适用于Linux。因此,当我最终移植到Linux时,它是值得回来的,但在那天之前不适用。 – fawick 2010-05-12 13:24:13