我有 2 个共享对象库,例如libA.so
和libB.so
。我有一个可执行文件,它从这两个库中调用函数,例如funcA()
来自libA
和funcB()
来自libB
。
不幸的是,libA
和都libB
具有一些使用相同符号名称的其他函数,例如libA
具有函数f1()
和f2()
并且libB
还具有函数f1()
和。请注意,libA和libB之间的和的f2()
实现完全不同,它们只是同名而已。f1()
f2()
可执行代码从来不会直接调用f1()
或f2()
,当然libA
应该只调用它的f1()
& f2()
,并且libB
应该只调用它的f1()
和f2()
。
但是,似乎出了点问题,我在运行可执行文件时遇到了段错误。
我对此有一个解决方案,即在构建libA
和时使用“version.script” libB
,这样只公开外部 API 函数(即名为“func*”的函数),即以下脚本...
{
global: func*;
local: *;
};
...并-Wl,--version-script=version.script
在与 gcc 链接时使用。
这是可行的,但它确实给我带来了麻烦,因为我必须使用第三方工具链,这使得使用此 version.script 方法变得复杂(即 hacky)。
libA
有没有更好/其他的方法?(注意,我确实有和的源代码libB
,因此可以更改符号名称,但不喜欢,因为此代码是由第三方工具自动生成的,而且我并不想进入并编辑所有名称。当然,真正的代码不仅仅是和,f1()
而是f2()
数百个类似名称的符号)。
另外,我想了解问题的根本原因?我假设libA
(或libB
)对应该调用哪个f1()
或感到“困惑” f2()
,但为什么——由于libA
和libB
都是单独编译的——我怀疑这与共享对象库有关?
以防万一...库和可执行文件的源代码是 C,并使用 gcc 进行编译和链接,这是在 Linux 上。
首先,对您的问题进行最简化的说明。为了保持最小,我将矛盾函数从 减少
f1()
到f2()
。f1()
由于您说:我将使用最简洁的例子来解释这一点。然后我将展示如何在没有版本脚本的情况下避免这种情况。
示例
源文件和头文件
libA.so
:源文件和头文件
libB.so
:程序来源:
编译所有共享库源:
程序源码如下:
链接共享库:
程序如下:
然后我们看到了你的问题:
funcA
和都funcB
调用在中定义的f1
from 。libA.so
A1.c
libA.so
通过以相反的顺序重新链接程序libA.so
:我们可以得出相反的问题:
这并不是什么问题。
解释
矛盾函数符号在和的
f1
动态符号表中都有定义:libA.so
libB.so
因此,在两个共享库中,它在运行时对于动态链接器都是可见的,作为对的引用的合格定义
f1
,并且动态链接器将默认将所有此类引用绑定到它在加载和链接构建过程中递归所需的共享库过程中找到的第一个定义。要运行
prog
,该构造从加载可执行文件 开始prog
。查看其动态部分的顶部:这是静态链接器写入其中以供动态链接器读取和执行的信息。可执行文件需要按顺序加载
libB.so
和。 是运行时链接器可以搜索以查找所需共享库的目录(除了其默认搜索目录之外)。这是我添加到程序链接的结果(因为我不会费心正确安装这些一次性库)。libA.so
RUNPATH
/home/imk/develop/so/scrap
-rpath=$(pwd)
因此在这种情况下,动态链接器找到的第一个定义加载的共享库
f1
将是libB.so
;这是来自的定义B1.c
,它会将程序中的所有引用绑定到该定义。让我们恢复到原来的联系
prog
:然后,正如您所猜测的,prog 的动态部分的顶部将显示:
libA.so
在 之前加载,libB.so
并且libA
的定义f1
将是获胜的定义,正如我们所见。修复
您的问题源于创建共享库时静态链接器的默认行为:共享库中对其定义的动态符号的引用不会预先绑定到内部定义1。有一个链接器选项将覆盖默认行为,使每个库调用其自己的定义
f1
。来自man ld
(ld
是静态链接器,由您代表调用以gcc
执行链接):因此我们可以像这样重新链接共享库:
(顺便说一下,我们用
-Wl,<ld-option>
告诉gcc
将选项ld-option
直接传递给ld
。)将程序与新库重新链接:
然后每个库调用自己的定义
f1
:您可能也可以通过使用 GCC 的动态可见性属性来解决该问题,但这需要对库中的源进行一些修改。