带有 gcc 4.4.7 的 CentOS 5.8 链接到 libstdc++ 6.0.8。这怎么可能?

Posted

技术标签:

【中文标题】带有 gcc 4.4.7 的 CentOS 5.8 链接到 libstdc++ 6.0.8。这怎么可能?【英文标题】:CentOS 5.8 with gcc 4.4.7 links against libstdc++ 6.0.8. How is it possible? 【发布时间】:2013-05-28 15:57:21 【问题描述】:

根据 gcc ABI 策略,gcc 4.4.7 应该依赖于 libstdc++ 6.0.13。据我了解,编译器版本和 libstdc++ 版本是紧密相连的,不能互换,所以我很惊讶地发现了以下事实:

CentOS 5.8 不知何故设法拥有一个链接到 6.0.8 的 gcc44 软件包,显然带有默认系统(基于 gcc-4.1.2) 编译器目录(/usr/lib/gcc/x86_64-redhat-linux6E/4.4.7,我希望在其中找到 libstdc++-6.0.13)中的 libstdc++.so 不是指向共享对象的链接任何类型,但包含 INPUT ( -lstdc++_nonshared /usr/lib64/libstdc++.so.6 ) 的文本文件

这里发生了什么魔法?

具体来说:

他们如何提供链接到旧版本 libstdc++ 的 gcc 4.4.7?我认为这是不可能的。 这个 stdc++_nonshared 库是什么? 我不知道 .so 文件可以包含该文本。谁解析它(我猜是动态链接器),它的规范和后果是什么? 这个魔法能走多远?我可以将 gcc4.7 与 libstdc++ 6.0.3 一起使用吗?兼容性的范围是什么

【问题讨论】:

编译器依赖于标准库,通过生成对特定命名函数的调用,例如在独立 gcc 环境中使用 purr 虚函数时,可能会产生对 __cxa_pure_virtual 的未定义引用,这很漂亮每当调用非覆盖的纯虚函数时调用的函数,也就是纯函数的默认函数。 编译器有很多这些,用于异常,用于 memcpy 等。这就是编译器依赖标准库的方式。每当标准库包含编译器期望的所有函数时,在链接时就不会有未定义的引用,它会“工作”。因此很有可能,但不建议混合编译器和库版本。 至于 stdc++_noshared,我的猜测是,这是库的静态链接版本,这意味着所需的一切都将在编译时包含,而不是在运行时动态链接,通过动态链接器。 正如您所料,它将对函数的引用映射到它们的位置,在可能新加载的动态库中。可能是因为共享库可能已经在内存中,并且被其他应用程序使用(共享库的想法是避免重复重复相同的库代码)。对于标准的 c++ 库,情况大多如此。 动态链接的规范和结果取决于所使用的对象格式,在 Unix 上,这通常是 ELF,由 ELF 规范描述; www.skyfree.org/linux/references/ELF_Format.pdf 【参考方案1】:

这里发生了什么魔法?

这主要是 ELF 符号版本控制的魔力。我在这方面看到的最佳参考是 Ulrich Drepper 的 How to Write Shared Libraries,特别是第 3 节。

他们如何提供链接到旧版本 libstdc++ 的 gcc 4.4.7?我认为这是不可能的。

libstdc++ 开发人员非常注意 ABI 兼容性。这使得查看两个不同版本的 libstdc++ 之间引入的新符号变得相当简单。

查看 libstdc++ 的ABI compatibility page,您可以看到 GCC 4.1 的 libstdc++ 具有 GLIBCXX_3.4.8,而 GCC 4.4 的库存(FSF 版本)具有 GLIBCXX_3.4.13。然后可以查看libstdc++ version scripts 以查看在这两个版本之间添加了哪些符号。

查看符号版本的更经验方法是使用 readelf 之类的工具。例如。在我的 Ubuntu 系统上readelf --dyn-syms --wide /usr/lib/x86_64-linux-gnu/libstdc++.so.6 | c++filt 显示这样的输出(只是几个选定的短线):

223: 000000000006dbe0  2121 FUNC    GLOBAL DEFAULT   12 std::ios_base::Init::Init()@@GLIBCXX_3.4
...
226: 00000000002ed920     1 OBJECT  GLOBAL DEFAULT   27 std::adopt_lock@@GLIBCXX_3.4.11

注意符号名称末尾的符号版本,在 @@ 之后。

这个 stdc++_nonshared 库是什么?

加入上述段落中的点,stdc++_nonshared 是一个静态库,其中包含库存 libstdc++.so.6.0.13 但不在 libstdc++.so.6.0.8 中的符号。

我不知道 .so 文件可以包含该文本。谁解析它(我猜是动态链接器),它的规范和后果是什么?

常规链接器解析这个,而不是运行时链接器。请参阅INPUT in a linker script 的 binutils 文档。因此,与这个特殊的 libstdc++.so.6.0.13 链接将导致链接器动态链接到 libstdc++.so.6.0.8,并静态链接到包含额外符号的库。

这样做的缺点是,以这种方式编译的应用程序将比同一个应用程序更臃肿,后者仅动态链接到包含所有必要符号的库存 libstdc++.so.6.0.13。好处是新标准库的功能可以在旧系统的标准安装中使用,而独立的软件供应商不需要发布他们自己的 libstdc++ 副本。

这个魔法能走多远?我可以将 gcc4.7 与 libstdc++ 6.0.3 一起使用吗?兼容性的范围是什么

现在您知道了魔法是什么,您可能会意识到您拥有的这个版本的 GCC 4.4 是专为 Centos 5(嗯,最初是 Red Hat 5)制作的特殊版本。因此,您可能可以创建一个 GCC 4.7,它将生成只需要 libstdc++ 6.0.3 的二进制文件,但它需要大量的工作来修补您的 GCC 树以将正确的符号放入 stdc++_nonshared.a。您至少可以查看 gcc44 RPM 的源代码,了解 Red Hat 是如何做到的。

Red Hat 似乎在他们的Red Hat Developer Toolset 中延续了这种方法。在撰写本文时,GCC 4.8.1 是最新的编译器,它可以为 EL5 构建二进制文件,仍然只需要在目标系统上安装 libstdc++.so.6.0.8。这对于希望使用 C++11 编写但需要能够在 EL5 或 EL6 上进行部署的开发人员来说非常有用。

【讨论】:

以上是关于带有 gcc 4.4.7 的 CentOS 5.8 链接到 libstdc++ 6.0.8。这怎么可能?的主要内容,如果未能解决你的问题,请参考以下文章

CentOS6.5升级GCC

CentOS6.5 gcc升级到4.8.2

推荐CentOS安装gcc-4.9.4+更新环境+更新动态库

CentOS6.8下编译安装GCC

CentOS6.5升级手动安装GCC4.8.2

转载CentOS6.5升级手动安装GCC4.8.2