我只是想验证一下:二进制所需的glibc版本是否完全由使用哪个版本的g++编译决定?
根本没有。运行时所需的 GLIBC 版本由几个因素决定:
- 链接时使用的 GLIBC 版本。
- GLIBC 的特性实际使用(这又取决于编译器版本,可能还有编译标志)。
- 所使用的任何功能的 ABI 是否发生变化。
在这种情况下,二进制文件是用 g++ 11 构建的
您可以在带有 GLIBC-2.15 的系统和带有 GLIBC-2.31 的系统上安装 g++-11。对于非平凡的源,生成的二进制文件在运行时可能对 GLIBC 有不同的要求。
请参阅 this answer 以了解符号版本控制如何发挥作用。
TL;DR:如果您想构建可移植到不同 Linux 发行版的可执行文件或共享库,请选择您愿意支持的最旧版本的 GLIBC,并在系统上构建使用那个版本(你不必为此拥有一台物理机器——一个虚拟机或一个 docker 容器工作查找)。
由于向后兼容性保证,您的二进制文件可以在具有相同或更新版本 GLIBC 的所有系统上运行。
更新:
假设您在 GLIBC-NN 上构建 libfoo.a,最终用户在 GLIBC-MM 上链接 a.out,其中 MM < NN。
这是一种非常危险的做法:程序可能会默默地损坏其数据,并且很难找到这种损坏的根本原因。
现在假设最终用户在 GLIBC-MM 上链接 a.out NN <= MM(这是根据 cmets 的实际情况),然后在具有 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 它可能会写入超过结尾。
注意:上面的程序已经违反了“应用程序不得修改返回值指向的结构”的要求,因此无论如何都有未定义的行为;这只是 GLIBC 不匹配导致的错误的一个示例可能。