GCC / Linux: 如何将静态库添加到 .so 文件中?

6
我有一个程序,通过动态加载来实现插件系统,从一些plugin_name.so中加载函数(通常如此)。
但是我也有一个静态的“helper”库(称为helper.a),它的函数被主程序和插件中的主函数使用。它们不必以任何方式互操作,只是用于文本处理等辅助功能。
这个程序一旦启动,就不能重新加载或重新启动,这就是为什么我期望从插件而不是主程序中获得新的“helper”功能。
那么我的问题是,是否可以强制.so中的“插件函数代码”使用(静态链接到?)与主程序不同(可能更新)版本的“helper”?
这该怎么做?也许通过静态链接或将helper.a添加到plugin_name.so中?

我想补充一下,我不希望或期望从主程序中使用新的辅助库。我只想将每个新插件与更新/更好的辅助库链接起来。 - royconejo
你不能使用动态链接来调用辅助函数,有什么原因吗? - Chris Huang-Leaver
一旦启动,主程序不能停止执行,甚至无法重新加载更高版本的 helper.so。程序仅需要基本的辅助功能,因为它只是插件的占位符。所以如果我有一个新的/改进的或扩展的 helper 库,我更喜欢重新编译整个插件。我认为分发单个 .so(插件)比两个 .so(插件和插件使用的最新的 helper)更实用。 - royconejo
那么你需要能够更新插件或插件使用的库代码,而无需重新启动主进程? - Chris Huang-Leaver
我对 PIC 不太了解,无法在答案中写出相关内容。但是我知道 .so 文件需要使用 -fPIC 编译,混合编译非 PIC 和 PIC 代码是否会引起问题呢? - rubenvb
显示剩余2条评论
3个回答

6

Nick Meyer的回答在Windows和AIX上是正确的,但默认情况下,在大多数其他UNIX平台上可能不正确。

在大多数UNIX平台上,运行时加载器为所有符号维护一个单一的名称空间。因此,如果您在a.out中定义了foo_helper,并且还在plugin.so中定义了它,然后从任何一个中调用foo_helper,则默认情况下由运行时加载器可见的第一个定义(通常来自a.out)将用于两个调用。

此外,事情变得复杂的原因是,除非您使用-rdynamic标志或某些其他共享库引用它,否则可能不会从a.out中导出foo_helper(因此对运行时加载器不可见)。换句话说,事情可能会像Nick描述的那样“看起来”正常,然后您向a.out链接行添加一个共享库,它们就不再按照那种方式工作。

在ELF平台(如Linux)上,您可以很好地控制符号可见性和绑定。请参阅GCC手册中-fvisibility=hidden和-rdynamic的描述,以及链接器手册中的-Bsymbolic。

大多数其他UNIX平台也有一些控制符号绑定的方法,但这必须是特定于平台的。


1
如果您的主程序和动态库都静态链接到helper.a,则不需要担心混合使用helper.a的版本(只要您不执行诸如在.exe和.so边界之间传递在helper.a中分配的指针之类的操作)。
当您链接到helper.a时,所需的代码将插入实际二进制文件中。因此,当您从.exe调用helper.a时,您将执行来自可执行映像的代码段的代码,并且当您从.so调用helper.a时,您将执行来自加载.so的地址空间部分的代码。即使您在helper.a内部调用相同的函数,根据调用是从.exe还是.so进行的,您将调用两个不同的“实例”该函数。

我明白了。但是我该怎么在编译时将libhelper.a中的代码添加到plugin.so中呢?我应该像这样指定一些内容"gcc -lhelper.a -static"来编译plugin.so吗? - royconejo
如果你的库名为libhelper.a,在编译plugin.so时,需要在gcc命令中传递-Ldir -lhelper参数,其中dir是libhelper.a所在的路径。不需要指定-static参数,因为这个参数只用于创建静态库。 - Nick Meyer
这个答案只在特定条件下才正确,但这些条件并没有被说明。 - Employed Russian

0

我认为这个问题和你的问题是一样的。如何强制从静态库中包含符号以构建共享库?

--whole-archive 链接器选项应该可以做到这一点。您可以像这样使用它:

gcc -o libmyshared.so foo.o -lanothersharedlib -Wl,--whole-archive -lmystaticlib

对我来说,它是有效的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接