哪些因素决定了用 g++ 构建的二进制文件需要哪个版本的 glibc?
What factors determine which version of glibc is required for binary built with g++?
我管理一个综合体C++ project
我收到实地报告说在一个系统上构建的二进制文件不会 运行 在另一个系统上构建
系统因为 glibc 版本不匹配。
我只是想验证一下:二进制文件需要的glibc版本是否完全由编译时使用的g++版本决定?
在这种情况下,二进制文件是使用 g++ 11 构建的,运行 在另一个未安装 g++ 11 的系统上构建。
I just want to verify: is the version of glibc required by the binary completely determined by which version of g++ is used to compile it?
根本。运行时所需的 GLIBC 版本由几个因素决定:
- link 时使用的 GLIBC 版本。
- GLIBC 的特性实际使用(这又取决于编译器版本,可能还有编译标志)。
- 所使用的任何功能的 ABI 是否发生了变化。
In this case, the binary is built with g++ 11
您可以在 GLIBC-2.15 和 GLIBC-2.31 系统上安装 g++-11
。对于非平凡的源代码,生成的二进制文件在运行时可能对 GLIBC 有不同的要求。
请参阅 了解符号版本控制的作用。
TL;DR:如果你想构建一个可移植到不同 Linux 发行版的可执行文件或共享库,select 最旧的 版本您愿意支持的 GLIBC,并在具有该版本的系统上构建(您不必为此拥有一台物理机器——VM 或 docker 容器工作查找)。
由于向后兼容性保证,您的二进制文件将适用于具有相同或更新版本 GLIBC 的所有系统。
更新:
假设您在 GLIBC-NN 上构建 libfoo.a
,最终用户 links a.out
在 GLIBC-MM 上构建,其中 MM < NN
.
这是一种非常危险的做法:程序可能会悄无声息地破坏其数据,找到这种破坏的根本原因将非常困难。
现在假设最终用户 links a.out
在 GLIBC-MM 上 NN <= MM
(根据评论,这是 实际 的情况),然后在具有 GLIBC-JJ 的系统上运行 a.out
,其中 JJ < NN
.
在那种情况下,我将“无声”损坏的可能性降到最低,但不会完全消除。
考虑以下假设示例(foo()
是您提供的libfoo.a
的一部分):
struct passwd *foo()
{
struct passwd *pw = getpwuid(42);
if (pw->field_a == 123) // 1
pw->field_a = 1234; // 2
return pw;
}
现在假设在 GLIBC-NN 中 field_a
作为 struct passwd
的一部分存在,但在 GLIBC-JJ 中它不存在。在这种情况下,程序可能会在 1
点上方读取已分配缓冲区的末尾,而在 2
点,它可能会 write 超过末尾。
注意:上面的程序已经违反了“应用程序不得修改 return 值指向的结构”的要求,因此无论如何都有未定义的行为;这只是 GLIBC 可能 不匹配导致错误的一个例子。
我管理一个综合体C++ project
我收到实地报告说在一个系统上构建的二进制文件不会 运行 在另一个系统上构建 系统因为 glibc 版本不匹配。
我只是想验证一下:二进制文件需要的glibc版本是否完全由编译时使用的g++版本决定?
在这种情况下,二进制文件是使用 g++ 11 构建的,运行 在另一个未安装 g++ 11 的系统上构建。
I just want to verify: is the version of glibc required by the binary completely determined by which version of g++ is used to compile it?
根本。运行时所需的 GLIBC 版本由几个因素决定:
- link 时使用的 GLIBC 版本。
- GLIBC 的特性实际使用(这又取决于编译器版本,可能还有编译标志)。
- 所使用的任何功能的 ABI 是否发生了变化。
In this case, the binary is built with g++ 11
您可以在 GLIBC-2.15 和 GLIBC-2.31 系统上安装 g++-11
。对于非平凡的源代码,生成的二进制文件在运行时可能对 GLIBC 有不同的要求。
请参阅
TL;DR:如果你想构建一个可移植到不同 Linux 发行版的可执行文件或共享库,select 最旧的 版本您愿意支持的 GLIBC,并在具有该版本的系统上构建(您不必为此拥有一台物理机器——VM 或 docker 容器工作查找)。
由于向后兼容性保证,您的二进制文件将适用于具有相同或更新版本 GLIBC 的所有系统。
更新:
假设您在 GLIBC-NN 上构建 libfoo.a
,最终用户 links a.out
在 GLIBC-MM 上构建,其中 MM < NN
.
这是一种非常危险的做法:程序可能会悄无声息地破坏其数据,找到这种破坏的根本原因将非常困难。
现在假设最终用户 links a.out
在 GLIBC-MM 上 NN <= MM
(根据评论,这是 实际 的情况),然后在具有 GLIBC-JJ 的系统上运行 a.out
,其中 JJ < NN
.
在那种情况下,我将“无声”损坏的可能性降到最低,但不会完全消除。
考虑以下假设示例(foo()
是您提供的libfoo.a
的一部分):
struct passwd *foo()
{
struct passwd *pw = getpwuid(42);
if (pw->field_a == 123) // 1
pw->field_a = 1234; // 2
return pw;
}
现在假设在 GLIBC-NN 中 field_a
作为 struct passwd
的一部分存在,但在 GLIBC-JJ 中它不存在。在这种情况下,程序可能会在 1
点上方读取已分配缓冲区的末尾,而在 2
点,它可能会 write 超过末尾。
注意:上面的程序已经违反了“应用程序不得修改 return 值指向的结构”的要求,因此无论如何都有未定义的行为;这只是 GLIBC 可能 不匹配导致错误的一个例子。