我只是想验证一下:二进制文件所需的glibc版本是否完全取决于使用哪个版本的g++来编译它?
一点也不。运行时所需的 GLIBC 版本由几个因素决定:
在本例中,二进制文件是使用 g++ 11 构建的
您可以g++-11在使用 GLIBC-2.15 的系统上安装,也可以在使用 GLIBC-2.31 的系统上安装。对于重要的源,生成的二进制文件在运行时可能对 GLIBC 有不同的要求。
请参阅此答案以了解符号版本控制如何发挥作用。
TL;DR:如果您想要构建可移植到不同 Linux 发行版的可执行文件或共享库,请选择您愿意支持的最旧版本的 GLIBC,并在具有该版本的系统上构建(您不必为此有一台物理机器——一台虚拟机或一个 docker 容器(工作发现)。
由于向后兼容性保证,您的二进制文件将在具有相同或更新版本的 GLIBC 的所有系统上运行。
更新:
假设您libfoo.a在 GLIBC-NN 上构建,最终用户链接a.out在 GLIBC-MM 上,其中MM < NN.
这是一种非常危险的做法:程序可能会默默地损坏其数据,并且找到这种损坏的根本原因将非常困难。
现在假设最终用户链接a.out在 GLIBC-MM where 上NN <= MM(根据注释这是实际a.out情况),然后在系统上运行 GLIBC-JJ, where 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;
}
Run Code Online (Sandbox Code Playgroud)
现在假设在 GLIBC-NN 中field_a作为 的一部分存在struct passwd,但在 GLIBC-JJ 中则不存在。在这种情况下,在1上面的点上,程序可能会读取超过分配的缓冲区的末尾,并且在此时2它可能会写入超过该末尾。
注意:上面的程序已经违反了“应用程序不得修改返回值指向的结构”的要求,因此无论如何都有未定义的行为;这只是一个因 GLIBC 不匹配而导致错误的例子。