在Linux上,什么决定了共享库的二进制兼容性?

8
我正在Linux上构建一个共享库,它作为某些软件(具体而言,扩展Mathematica)的“插件”使用。
我发现如果我在Ubuntu 16.04上构建,生成的库在RHEL 7.6上不起作用。但是,如果我在RHEL 7.6上构建,该库在RHEL和Ubuntu上都可以工作。
所谓“不起作用”,是指Mathematica拒绝加载它,但它只给出了一个通用且无用的“加载失败”的错误消息。
我已经排除了许多可能会破坏兼容性的因素,但我找不到更多的原因。这个问题是关于还有哪些因素可能影响兼容性,除了我下面列出的以外。
该库是用C和C++混合编写的,但导出了一个C接口。它使用“-static-libstdc++”和“-static-libgcc”进行构建。如果我在“.so”文件上使用“ldd”,它列出的唯一依赖关系是:
linux-vdso.so.1 =>  (0x00007ffc757b9000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa286e62000)
/lib64/ld-linux-x86-64.so.2 (0x00007fa287854000)

可能存在不兼容的一个潜在来源是 glibc 版本。我使用 nm -gC 查看库中的符号,当我在Ubuntu上构建时,我看到的最高GLIBC版本引用是2.14。RHEL 7.6具有glibc 2.17,即更新版本而不是2.14。因此,我认为不兼容性不是由glibc引起的。

还有什么其他原因会导致在Ubuntu 16.04上编译的共享对象无法在RHEL 7.6上加载?


更新:我成功地让Mathematica给出了更详细的错误信息(这是一个没有很好文档化的功能),因此我有了明确的错误消息。同样可以通过 @Ctx 的建议设置LD_DEBUG = all 来看到错误。

错误如下:

IGraphM.so: undefined symbol: _ZTVNSt7__cxx1115basic_stringbufIcSt11char_traitsIcESaIcEEE

(IGraphM.so 是我的库。)

如果我没有搞错,这个函数似乎是 libstdc++ 的一部分。如果我已经指定了 -static-libstdc++ 并验证了 ldd 没有列出 libstdc++,为什么会出现这个错误?


更新2:

根据 SergeyA 和 这个 QA 的建议,我在定义 _GLIBCXX_USE_CXX11_ABI=0 后进行了编译。这确实解决了不兼容性问题。

但我仍然不理解为什么。错误消息抱怨缺少一个符号。这个符号通常从哪里加载?我以为如果我使用 -static-libstdc++,那么它应该包含在我的库中。但这似乎是错误的。

虽然我似乎已经针对这个特定情况找到了实用的解决方案,但我希望能得到一些解释,以便将来可以自己解决类似的问题。


glibc 应该没问题。我假设两个系统是相同的平台(比如 x86-64)。也许 Mathematica 是一个 32 位应用程序,但你提供了一个 64 位共享对象?你尝试过创建一个没有代码的空“虚拟”插件,并仅使用 C,看看是否有效吗?结果可能会排除一些可能性。 - TypeIA
你可以尝试在启动Mathematica的控制台中输入export LD_DEBUG=all。也许它会产生一些调试输出,有助于解决问题。 - Ctx
@Ctx 谢谢!这是一个好的提示,正是我在寻找的那种东西。 - Szabolcs
1
你缺失的符号可能是 vtable for std::__cxx11::basic_stringbuf<char, std::char_traits<char>, std::allocator<char> >,这个链接 https://dev59.com/E1wX5IYBdhLWcg3wyB_T 可能会有一些线索。 - SergeyA
也许这是因为我的库在内部使用了异常?(外部,它呈现的是 C 接口,而不是 C++ 接口。) - Szabolcs
显示剩余15条评论
1个回答

4
我无法解释为什么你的.so库不能将所有使用的符号静态链接(而是将它们保留为未定义),但我可以就如何解决手头问题提供实用建议。
你可以停止将libstdc++静态链接到你的插件中,而是使用主机系统中可用的版本。这对你不起作用,因为在构建和目标平台之间存在ABI不兼容性。您可以通过指定宏_GLIBCXX_USE_CXX11_ABI=0来降级您的插件所使用的ABI版本。

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