ChatGPT解决这个技术问题 Extra ChatGPT

为什么不鼓励静态链接 glibc?

大多数在线资料表明您可以静态链接 glibc,但不鼓励这样做;例如centos package repo

glibc-static 包包含用于 -static 链接的 C 库静态库。你不需要这些,除非你静态链接,这是非常不鼓励的。

这些消息来源很少(或从不)说明为什么这是一个坏主意。

具有讽刺意味的是,CentOS 的软件包 repo 已经过时,有时会迫使开发人员静态链接。

S
Sergei Tachenov

其他答案中给出的原因是正确的,但它们不是最重要的原因。

glibc 不应静态链接的最重要原因是它在内部广泛使用 dlopen 来加载 NSS (Name Service Switch) 模块和 iconv 转换。模块本身引用 C 库函数。如果主程序与 C 库动态链接,那没问题。但如果主程序与 C 库静态链接,dlopen 必须加载 C 库的第二个副本以满足模块的加载要求。

这意味着您的“静态链接”程序仍然需要在文件系统上存在 libc.so.6 的副本,加上 NSS 或 iconv 或任何模块本身,以及模块可能需要的其他动态库,例如 ld-linux.so.2 , libresolv.so.2 等。这不是人们在静态链接程序时通常想要的。

这也意味着静态链接的程序在其地址空间中有两个 C 库副本,它们可能会争夺要使用谁的 stdout 缓冲区,谁可以使用非零参数调用 sbrk,诸如此类. glibc 内部有一堆防御性逻辑试图使这项工作发挥作用,但从来没有保证能够工作。

您可能认为您的程序不需要担心这一点,因为它从不调用 getaddrinfoiconv,但语言环境支持在内部使用 iconv,这意味着任何 stdio.h 函数 可能会触发对 dlopen 的调用,而您无法控制它,用户的环境变量设置可以。

例如,如果您的程序确实调用了 iconv,那么情况会变得更糟,尤其是当“静态链接”可执行文件构建在一个发行版上,然后复制到另一个发行版时。 iconv 模块有时位于不同发行版上的不同位置,因此在 Red Hat 发行版上构建的可执行文件可能无法在 Debian 上正常运行,这与人们静态想要的完全相反链接的可执行文件。


请注意,是否需要第二个 glibc 副本是设计决定。如果静态 glibc 库在 NSS 和 iconv 中静态链接,则没有必要。当然,缺点是您只能使用静态链接的那些 NSS 模块和 iconv 转换,但这从静态链接的定义中非常明显。
@MSalters 最近在 glibc 开发列表上有一些关于这样做的讨论。这个设计决策是在 1990 年代做出的,有一个强有力的论点是,我们不再需要在终端输出的字符编码中具有如此多的灵活性,尤其是在人们想要的那种程序中静态链接。 NSS 灵活性仍然很重要,但有其他方法可以处理它(例如nscd)。
这不是完全正确的;我发现使用 stdio.h 的静态链接 C 程序在 /lib 中没有库的情况下工作。我必须使用的程序是 lilo。
@zwol:如果无法加载库,它只会切换到 LANG=C 。此行为对于早期启动是必需的。
@Joshua 然后听起来它会加载库和另一个 glibc 副本,如果可以的话。
C
Community

program/glibc 接口由 POSIX、C 和 C++ 标准等标准化和记录。例如,fopen() 函数的行为符合 C 标准,pthread_mutex_lock() 符合 POSIX。

glibc/kernel 接口没有标准化。 fopen() 在底层使用 open() 吗?还是使用openat()?或者是其他东西?明年用什么?你不知道。

如果 glibc/kernel 接口发生更改,则使用任何更改但静态链接 glibc 的程序将不再工作。

15 多年前,出于这个原因,Solaris 删除了 libc 的所有静态版本。

Static Linking - where did it go?

使用 Solaris 10,您无法再构建静态可执行文件。并不是 ld(1) 不允许静态链接或使用存档,只是不再提供 libc.a,即 libc.so.1 的存档版本。这个库提供了用户空间和内核之间的接口,没有这个库就很难创建任何形式的应用程序。一段时间以来,我们一直在警告用户不要进行静态链接,而针对 libc.a 的链接尤其成问题。每个 solaris 版本或更新(甚至一些补丁)都会导致某些针对 libc.a 构建的应用程序失败。问题是 libc 应该将应用程序与用户/内核边界隔离开来,这个边界可能会随着版本的不同而发生变化。如果应用程序是针对 libc.a 构建的,那么它所引用的任何内核接口都会从存档中提取并成为应用程序的一部分。因此,此应用程序只能在与所使用的内核接口同步的内核上运行。如果这些接口发生变化,应用程序将处于不稳定的状态。 ...

编辑:

似乎严重高估了 Linux 内核接口的稳定性。有关详细信息,请参阅 Linux kernel API changes/additions。总结一下:

https://i.stack.imgur.com/PRjoG.jpg


yarchive.net/comp/linux/gcc_vs_kernel_stability.html我们非常关心用户空间界面。我们竭尽全力维护设计糟糕或无意的界面。破坏用户程序是不可接受的。
@MaximEgorushkin 现实是不同的。 The Linux ABI isn't very stable, to the point it's been mocked relatively recently:“在您的硬件上运行 Linux 发行版有两种基本方法,这已经不是什么秘密了。您要么使用具有相当过时内核版本的稳定发行版,它可能不支持您的硬件,要么运行最新的稳定版版本,但您会失去稳定性,并且容易出现回归。”
您引用的引用是关于内核驱动程序 API,而不是用户空间 API。
我从未声称 Linux API 是标准化的。只是它(相对)稳定。并且大多数 Linux POSIX 函数实现都非常兼容。
“glibc 接口是标准化的。通过 POSIX。通过 C 标准。等等。”编程接口是,但二进制接口不是!