【Linux】GLIBCXX 错误的深度理解
推荐超级课程:
@TOC
如果您使用企业级或稳定的 Linux 发行版,迟早您会看到如下错误:
app: /lib64/libc.so.6: version `GLIBC_3.1.45' not found (required by ./app)
或者像这样:
app: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.20' not found (required by ./app)
应用程序(或库)本身会有所不同。libc
或 libstdc++
的路径也可能不同。但错误总是会提到未找到 GLIBCXX
或 GLIBC
版本。
您可以做些什么来修复它呢?
什么是 glibc
和 libstdc++
?
一些背景知识将帮助您更好地理解潜在的问题(并希望修复它)。
glibc
是 GNU C 库
。它是标准 C 库的实现。任何用 C 编写的程序都会使用标准库来访问文件和网络、向用户显示消息、处理进程等等。它是操作系统的基础组件。
数百个应用程序、库,甚至 Linux 系统上安装的其他非 C 编程语言都会使用 C 库。
Linux 上还有其他 C 库,例如 musl
,但 glibc
是最常见的一个。
libstdc++
与 glibc
类似,但针对 C++:它是 C++ 标准库的实现。任何用 C++ 编写的程序都会使用它来实现 C++ 库中的事物。例如线程、流、文件、输入/输出等等。
有一个 很棒的 reddit 评论
详细介绍了这两个库之间的关系,以及它们如何与系统上的其他核心工具(如 C 编译器 (gcc
) 和 binutils
)相关。
标准库的一个重要方面是其 应用程序二进制接口(或 ABI)。一个在 2010 年针对 glibc
编写和编译的 C 程序应该能够在 2020 年发布的新的 glibc
版本上继续工作。为了实现这一点,glibc
提供了一个 ABI,并承诺不会改变这个特定的 ABI。glibc
可以添加额外的内容,但不会改变或删除 ABI 的任何部分。删除 ABI 中的任何内容都会破坏以前编译的应用程序。
如果 glibc
永远不能改变其 ABI,它如何改进事物呢?
glibc
通过使用符号版本控制来保持 ABI 并提供新功能。glibc
提供的每个符号或函数都与一个版本相关联。链接器 (ld
) 链接到带有版本的函数名。如果您的 C 程序调用函数 glob64
,链接器将不仅将其链接到 glibc
中的 glob64
符号,还将链接到完全版本化的符号 glob64@GLIBC_2.27
。您可以将文本 glob64@GLIBC_2.27
视为具有版本 GLIBC_2.27
的 glob64
符号。
此功能允许旧程序使用旧但兼容的符号 glob64@GLIBC_2.2.5
,而您的新程序可以利用较新的符号 glob64@GLIBC_2.27
。
glibc
用于符号的版本通常看起来像 GLIBC_<VERSION>
。一些示例是 GLIBC_2.27
和 GLIBC_2.2.5
。
您可以在这里找到有关 glibc
中符号版本控制如何工作的更多信息。
libstdc++
具有类似的概念和实现 ABI。事实上,一些版本的 libstdc++
甚至提供 两种不同的 ABI
!
libstdc++
导出的版本通常看起来像 GLIBCXX_<VERSION>
。一些示例:GLIBCXX_3.4
和 CXXABI_1.3
。
这个错误是什么意思?
现在您对 C 和 C++ 标准库和符号版本有了一些了解,让我们回到错误:
app: /lib64/libc.so.6: version `GLIBC_3.1.45' not found (required by ./app)
当运行时链接器尝试为 app
加载标准 C 库(libc
)时,会发生此错误。运行时链接器看到 app
依赖于一个符号,但此 C 库中未找到版本 GLIBC_3.1.45
。
app: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.20' not found (required by ./app)
当运行时链接器尝试为 app
加载 C++ 库(libstdc++
)时,会发生此错误。运行时链接器看到 app
依赖于一个符号,但此 C++ 库中未找到符号的版本 GLIBCXX_3.4.20
。
换句话说,错误意味着 app
是针对较新版本的 C/C++ 库链接的。系统上可用的 C/C++ 库太旧,不提供具有这些版本的符号。
这是如何发生的?
这种类型的错误很容易遇到。
您下载了一个为较新的 Linux 发行版编译的预构建二进制文件,并尝试在较旧的 Linux 发行版上运行它。
例如,在 RHEL 7 上运行编译为在 RHEL 8 上运行的程序时,将显示错误。同样,一个旨在在较新发行版上运行的 Python 包 也会出现这种情况。
您以某种方式 安装了为较新版本的发行版设计的发行版包 。
您下载了一个在您的发行版上不受支持的应用程序。
例如,如果您尝试在 RHEL 5 上运行 .NET Core,您会遇到这个问题。
根本原因是一样的:您的操作系统与您想要运行的应用程序或库之间存在不兼容性。
您可以使用 readelf
检查应用程序或库所需的 GLIBC
或 GLIBCXX
版本:
$ readelf --dyn-syms /usr/bin/java | grep '@GLIBC'
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (3)
6: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@GLIBC_2.2.5 (3)
在这里,您可以看到这个应用程序(java
)使用了两个符号,这些符号的版本是 GLIBC_2.2.5
。
如何修复此错误?
修复此错误的正确方法是确保您使用的 Linux 发行版与导致此错误的应用程序(或库)兼容。 应用程序/库不支持您的 Linux 发行版: 如果您使用的是太旧且不受应用程序支持的应用程序,请升级到较新的版本。 例如,如果您需要在一个报告此错误的 RHEL 6 上运行应用程序,请考虑升级到 RHEL 7(或 RHEL 8)以解决这个问题。 应用程序/库支持您的 Linux 发行版: 如果应用程序/库支持您的 Linux 发行版的版本,那么您得到了错误的应用程序或库二进制文件。看看您是否能找到适合您的发行版的二进制下载。例如,.NET Core 支持 RHEL 6,但为 RHEL 6 提供了单独的二进制下载。常规 .NET 下载支持比 RHEL 6 更新的 Linux 发行版。 您也可能想要报告一个错误/问题或以其他方式将其报告给应用程序/库提供商。这将让他们知道他们需要更改下载部分和文档,以帮助他们的用户避免此问题。
一些关于如何修复此问题的糟糕建议
有些建议说您可以构建自己的 glibc
和/或 libstdc++
版本并使用它们。我建议不要这样做。一旦您构建了自己的版本,您就必须负责维护构建。这也意味着要跟上所有 libc
/libstdc++
安全修复并应用它们并重新构建 libc
/libstdc++
。这可能适合爱好项目,但不是生产环境中的适当解决方案。
如果您确实非常了解您正在做什么,并理解了所有维护、安全和兼容性的影响,那么最后一个选项是:您可以重新构建自己的 glibc
或 libstdc++
版本。这里有 如何构建自己的 glibc
版本的步骤
。还有 在这里描述的运行您刚刚构建的 glibc
的更好方法
。
但话又说回来,如果您完全理解了所有的影响,您就不会阅读这篇文章来帮助理解和修复错误了。您可能已经注意到这里的一些想法并不完全正确。
还有一些建议说,从您 Linux 发行版的另一个版本中简单地获取一个 glibc
版本是可以的。这可能有效。它也可能完全破坏您的安装。它也可能阻止通过发行版的包管理器对 glibc
/libstdc++
进行任何进一步的更新。