大多数在线资料表明您可以静态链接 glibc,但不鼓励这样做;例如centos package repo:
glibc-static 包包含用于 -static 链接的 C 库静态库。你不需要这些,除非你静态链接,这是非常不鼓励的。
这些消息来源很少(或从不)说明为什么这是一个坏主意。
其他答案中给出的原因是正确的,但它们不是最重要的原因。
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 内部有一堆防御性逻辑试图使这项工作发挥作用,但从来没有保证能够工作。
您可能认为您的程序不需要担心这一点,因为它从不调用 getaddrinfo
或 iconv
,但语言环境支持在内部使用 iconv
,这意味着任何 stdio.h
函数 可能会触发对 dlopen
的调用,而您无法控制它,用户的环境变量设置可以。
例如,如果您的程序确实调用了 iconv
,那么情况会变得更糟,尤其是当“静态链接”可执行文件构建在一个发行版上,然后复制到另一个发行版时。 iconv
模块有时位于不同发行版上的不同位置,因此在 Red Hat 发行版上构建的可执行文件可能无法在 Debian 上正常运行,这与人们静态想要的完全相反链接的可执行文件。
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
nscd
)。stdio.h
的静态链接 C 程序在 /lib 中没有库的情况下工作。我必须使用的程序是 lilo。