为什么不建议静态链接glibc?

pze*_*sko 36 c c++ linker glibc static-linking

在线上的大多数资源都指出您可以静态链接glibc,但不建议这样做。例如centos软件包repo

The glibc-static package contains the C library static libraries
for -static linking.  You don't need these, unless you link statically,
which is highly discouraged.
Run Code Online (Sandbox Code Playgroud)

这些消息来源很少(或从来没有)说过为什么这不是一个好主意。

zwo*_*wol 26

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

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

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

这也意味着静态链接程序在其地址空间中有C库的两个副本,并且它们可能会争用stdout要使用谁的缓冲区,谁可以使用sbrk非零参数进行调用,等等。glibc中有很多防御性逻辑可以尝试使此工作正常进行,但从未保证它能正常工作。

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

  • 注意,需要第二个glibc副本是一个设计决定。如果静态glibc库已在NSS和iconv中静态链接,则没有必要。不利的一面是,您只能使用静态链接的NSS模块和iconv转换,但是从静态链接的定义中可以很明显地看出来。 (4认同)
  • @MSalters最近在glibc开发列表上进行了一些讨论。这项设计决策是在1990年代做出的,有一个强有力的论点是,我们不再需要终端输出的字符编码具有如此大的灵活性,尤其是在人们希望静态链接的程序类型中。NSS的灵活性仍然很重要,但是还有其他处理方式(例如`nscd`)。 (3认同)
  • @zwol:如果无法加载库,它只会切换到 LANG=C 。此行为对于早期启动是必要的。 (2认同)
  • @Joshua 那么听起来它将加载库以及 glibc 的另一个副本(如果可以的话)。 (2认同)

And*_*nle 5

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

glibc/内核接口没有标准化。是否在引擎盖下fopen()使用open()?或者它使用openat()?或者是其他东西?明年用什么?你不知道。

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

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

静态链接 - 它去了哪里?

使用 Solaris 10,您无法再构建静态可执行文件。不是 ld(1) 不允许静态链接或使用存档,只是不再提供 libc.a,即 libc.so.1 的存档版本。这个库提供了用户空间和内核之间的接口,没有这个库就很难创建任何形式的应用程序。

一段时间以来,我们一直在警告用户不要进行静态链接,而针对 libc.a 的链接尤其成问题。每个solaris 版本或更新(甚至一些补丁)都会导致一些针对libc.a 构建的应用程序失败。问题是 libc 应该将应用程序与用户/内核边界隔离,该边界可以在不同版本之间发生变化。

如果应用程序是针对 libc.a 构建的,那么它引用的任何内核接口都将从存档中提取并成为应用程序的一部分。因此,此应用程序只能在与所使用的内核接口同步的内核上运行。如果这些接口发生变化,应用程序就会处于不稳定的状态。

...

编辑:

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

在此处输入图片说明

  • 您引用的引用是关于内核驱动程序 API,而不是用户空间 API。 (9认同)
  • https://yarchive.net/comp/linux/gcc_vs_kernel_stability.html:_我们非常关心用户空间接口。即使是设计糟糕或无意的界面,我们也会竭尽全力进行维护。破坏用户程序是不可接受的。_ (5认同)
  • “glibc 接口是标准化的。通过 POSIX。通过 C 标准。等等。” *编程接口*是,但*二进制接口*不是! (4认同)
  • 我从未声称 Linux API 是标准化的。只是它(相对)稳定。大多数 Linux POSIX 函数实现都非常兼容。 (3认同)
  • 出于这个原因,Linux 致力于保持其接口向后兼容。 (3认同)
  • @AndrewHenle 实际上,这是支持静态链接的论点。如果 glibc 的二进制接口发生了变化,但您的程序没有更改为使用新的二进制接口怎么办?(答案:glibc 不会因此而改变其二进制接口) (3认同)
  • @MaximEgorushkin 现实是不同的。[Linux ABI 不是很稳定,最近才被嘲笑](http://lkml.iu.edu/hypermail/linux/kernel/1604.0/00998.html):“这不是什么秘密是在硬件上运行 Linux 发行版的两种基本方法。要么使用稳定的发行版,该发行版的内核版本相当过时,可能不支持您的硬件;要么运行最新的稳定版本,但会失去稳定性并且容易出现回归问题”。 (2认同)
  • 再次阅读我的前两条评论并与 Linus Torvalds 争论。 (2认同)
  • @AndrewHenle:整个混乱正是**确切**为什么它是这样一个问题,即使是在 25 年后。`glibc` 试图在太多情况下做得太多。微软在这方面就干净得多。Windows本身有Universal CRT,Visual Studio有它的MSVCRT。 (2认同)
  • @AndrewHenle 标准在这里:https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/ - 不幸的是它是用 C 而不是英语编写的。 (2认同)