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

90

大多数在线资源都表明您可以静态链接glibc,但不建议这样做。例如,CentOS软件包仓库

glibc-static软件包包含用于静态链接的C库静态库。除非您静态链接,否则您不需要它们,这是极其不建议的。

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


10
有些讽刺的是,CentOS的软件包仓库过时了,有时候开发人员不得不使用静态链接。 - nada
3个回答

105

其他回答提供的原因是正确的,但并不是最重要的原因。

glibc不应该被静态链接的最重要原因是它在内部广泛使用dlopen来加载NSS(Name Service Switch)模块和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的调用,而您无法控制此操作,用户的环境变量设置会影响到它。
如果您的程序确实调用了iconv,那么事情会变得更糟,特别是当一个“静态链接”可执行文件在一个发行版上构建,然后复制到另一个发行版时。在不同的发行版上,iconv模块有时位于不同的位置,因此在Red Hat发行版上构建的可执行文件可能无法在Debian上正确运行,这与静态链接可执行文件的初衷完全相反。

15
请注意,需要第二个 glibc 副本是一项设计决策。如果静态的 glibc 库已经静态链接了 NSS 和 iconv,那就没有必要再进行复制。当然,缺点就是你只能使用那些在静态链接中已经被链接的 NSS 模块和 iconv 转换,但这从静态链接的定义中是非常明显的。 - MSalters
9
最近在glibc开发列表中讨论了这个问题。这个设计决策是在1990年代做出的,有一个很有力的论点认为我们在终端输出字符编码方面不需要那么多的灵活性了,特别是在人们想要静态链接的程序中。NSS的灵活性仍然很重要,但是有其他的处理方式(例如 nscd)。 - zwol
1
这还不太对,我发现静态链接的使用 stdio.h 的 C 程序可以在 /lib 中没有任何库的情况下工作。我需要用这种方法运行 lilo 程序。 - Joshua
2
@zwol:如果无法加载库,它只会切换到LANG=C。这种行为对于早期引导是必要的。 - Joshua
3
那么听起来它会加载库和另一个glibc的副本(如果可能的话)。 - user253751
显示剩余3条评论

10
该程序/glibc接口已经被POSIX、C和C++标准等规范化并记录。例如,fopen()函数遵循C标准,pthread_mutex_lock()遵循POSIX标准。 glibc/内核接口并未被规范化。 fopen()在底层使用open()还是openat()?或者其他的什么?明年会使用什么?你不知道。
如果glibc/内核接口发生更改,使用静态链接glibc的程序将无法再正常工作。
15年前,Solaris出于这个原因删除了所有静态版本的libc静态链接——它去哪里了? 使用Solaris 10,您无法再构建静态可执行文件。这并不是因为ld(1)不允许静态链接或使用存档文件,而是因为不再提供libc.a,即libc.so.1的存档版本。该库提供了用户空间与内核之间的接口,没有此库,创建任何形式的应用程序都相当困难。
我们一直在警告用户不要进行静态链接,而链接libc.a尤其有问题。每个Solaris发行版或更新(甚至某些补丁)都会导致一些使用libc.a构建的应用程序失败。问题在于libc应该将应用程序与用户/内核边界隔离开来,而该边界可能会因版本发布而发生变化。
如果应用程序是针对libc.a构建的,则它引用的任何内核接口都将从存档中提取并成为应用程序的一部分。因此,此应用程序只能在与使用的内核接口同步的内核上运行。如果这些接口发生更改,则应用程序就处于不稳定的状态。

似乎对Linux内核接口的稳定性进行了严重高估。请参见Linux内核API更改/添加以获取详细信息。总结如下:

enter image description here


11
我们极其关注用户空间接口,甚至到了疯狂的程度。我们采取极端措施来维护即使是设计不良或无意的接口。毁坏用户程序是不可接受的。 - Maxim Egorushkin
16
你引用的那句话是关于内核驱动API而不是用户空间API的。 - Maxim Egorushkin
3
我从未声称Linux API是标准化的。只是它(相对地)是稳定的。而且大多数Linux POSIX函数实现都是相当符合标准的。 - Maxim Egorushkin
8
glibc接口是标准化的。由POSIX标准和C标准等制定。但二进制接口却不是!“编程接口”是指标准化的,但“二进制接口”则不是。 - plugwash
3
为了这个原因,Linux致力于保持其接口向后兼容。 - user253751
显示剩余15条评论

-4
我可以想到一个允许静态链接的好理由。如果有人获得了系统访问权限,他们可以用包含恶意软件的libc.so替换原文件。当你运行应用程序时,它将执行恶意软件。如果该应用程序是使用静态glibc构建的,则该攻击向量将消失。

1
OP问为什么不鼓励静态链接,但你给出了一个应该使用它的理由。看起来不是很有建设性的答案。 - Adrian Mole
8
如果有人能够替换libc.so,那么你已经输了。这样的用户几乎肯定拥有root权限,并且可以修改系统中的任何文件或内存字节,包括修补运行的内核等。 - Jonathan Myers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接