为什么不鼓励静态链接 glibc?
Why is statically linking glibc discouraged?
大多数在线资源声明您可以静态 link glibc,但不鼓励这样做;例如centos package 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.
这些消息来源很少(或从不)说明为什么这是个坏主意。
程序/glibc
接口由 POSIX、C 和 C++ 标准以及其他标准标准化和记录。例如,fopen()
函数的行为符合 C 标准,pthread_mutex_lock()
符合 POSIX.
glibc
/内核接口未标准化。 fopen()
是否在幕后使用 open()
?还是使用openat()
?或者是其他东西?明年用什么?你不知道。
如果 glibc
/kernel 接口发生变化,使用任何变化但静态链接 glibc
的程序将不再工作。
15 多年前,出于这个原因,Solaris 删除了 libc
的所有静态版本。
Static Linking - where did it go?
With Solaris 10 you can no longer build a static executable. It's not that ld(1) doesn't allow static linking, or using archives, it's just that libc.a, the archive version of libc.so.1, is no longer provided. This library provides the interfaces between user land and the kernel, and without this library it is rather hard to create any form of application.
We've been warning users against static linking for some time now, and linking against libc.a has been especially problematic. Every solaris release, or update (even some patches) has resulted in some application that was built against libc.a, failing. The problem is that libc is supposed to isolate an application from the user/kernel boundary, a boundary which can undergo changes from release to release.
If an application is built against libc.a, then any kernel interface it references is extracted from the archive and becomes a part of the application. Thus, this application can only run on a kernel that is in-sync with the kernel interfaces used. Should these interfaces change, the application is treading on shaky ground.
...
编辑:
似乎严重高估了Linux内核接口的稳定性。有关详细信息,请参阅 Linux kernel API changes/additions。总结一下:
其他答案给出的原因都对,但不是最重要的原因。
glibc 不应静态 linked 的最重要原因是它在内部广泛使用 dlopen
, to load NSS (Name Service Switch) modules and iconv
转换。模块本身引用 C 库函数。如果主程序是用 C 库动态 linked 的,那没问题。但是如果主程序静态地 link 编辑了 C 库,dlopen
必须加载 C 库的 第二个副本 以满足模块的加载要求。
这意味着您的“静态 linked”程序仍然需要 libc.so.6
的副本才能出现在文件系统上,加上 NSS 或 iconv
或任何模块本身,加上模块可能需要的其他动态库,如 ld-linux.so.2
、libresolv.so.2
等。这不是人们在静态 link 程序时通常想要的。
这也意味着静态 linked 程序在其地址 space 中有两个 C 库的副本,它们可能会争夺使用谁的 stdout
缓冲区,谁可以用非零参数调用 sbrk
之类的东西。 glibc 中有一堆防御逻辑试图使这项工作正常进行,但从未保证 正常工作。
您可能认为您的程序不需要担心这个,因为它从不调用 getaddrinfo
或 iconv
,但区域设置支持在内部使用 iconv
,这意味着任何 stdio.h
函数 都可能触发对 dlopen
的调用,您无法控制它,用户的环境变量设置可以。
并且如果您的程序 确实 调用 iconv
,那么事情会变得更糟,尤其是当“静态 linked”可执行文件是建立在一个发行版上,然后复制到另一个发行版。 iconv
模块有时位于不同发行版的不同位置,因此在 Red Hat 发行版上构建的可执行文件可能无法 运行 在 Debian 发行版上正确构建,这恰恰相反人们对静态 linked 可执行文件的需求。
大多数在线资源声明您可以静态 link glibc,但不鼓励这样做;例如centos package 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.
这些消息来源很少(或从不)说明为什么这是个坏主意。
程序/glibc
接口由 POSIX、C 和 C++ 标准以及其他标准标准化和记录。例如,fopen()
函数的行为符合 C 标准,pthread_mutex_lock()
符合 POSIX.
glibc
/内核接口未标准化。 fopen()
是否在幕后使用 open()
?还是使用openat()
?或者是其他东西?明年用什么?你不知道。
如果 glibc
/kernel 接口发生变化,使用任何变化但静态链接 glibc
的程序将不再工作。
15 多年前,出于这个原因,Solaris 删除了 libc
的所有静态版本。
Static Linking - where did it go?
With Solaris 10 you can no longer build a static executable. It's not that ld(1) doesn't allow static linking, or using archives, it's just that libc.a, the archive version of libc.so.1, is no longer provided. This library provides the interfaces between user land and the kernel, and without this library it is rather hard to create any form of application.
We've been warning users against static linking for some time now, and linking against libc.a has been especially problematic. Every solaris release, or update (even some patches) has resulted in some application that was built against libc.a, failing. The problem is that libc is supposed to isolate an application from the user/kernel boundary, a boundary which can undergo changes from release to release.
If an application is built against libc.a, then any kernel interface it references is extracted from the archive and becomes a part of the application. Thus, this application can only run on a kernel that is in-sync with the kernel interfaces used. Should these interfaces change, the application is treading on shaky ground.
...
编辑:
似乎严重高估了Linux内核接口的稳定性。有关详细信息,请参阅 Linux kernel API changes/additions。总结一下:
其他答案给出的原因都对,但不是最重要的原因。
glibc 不应静态 linked 的最重要原因是它在内部广泛使用 dlopen
, to load NSS (Name Service Switch) modules and iconv
转换。模块本身引用 C 库函数。如果主程序是用 C 库动态 linked 的,那没问题。但是如果主程序静态地 link 编辑了 C 库,dlopen
必须加载 C 库的 第二个副本 以满足模块的加载要求。
这意味着您的“静态 linked”程序仍然需要 libc.so.6
的副本才能出现在文件系统上,加上 NSS 或 iconv
或任何模块本身,加上模块可能需要的其他动态库,如 ld-linux.so.2
、libresolv.so.2
等。这不是人们在静态 link 程序时通常想要的。
这也意味着静态 linked 程序在其地址 space 中有两个 C 库的副本,它们可能会争夺使用谁的 stdout
缓冲区,谁可以用非零参数调用 sbrk
之类的东西。 glibc 中有一堆防御逻辑试图使这项工作正常进行,但从未保证 正常工作。
您可能认为您的程序不需要担心这个,因为它从不调用 getaddrinfo
或 iconv
,但区域设置支持在内部使用 iconv
,这意味着任何 stdio.h
函数 都可能触发对 dlopen
的调用,您无法控制它,用户的环境变量设置可以。
并且如果您的程序 确实 调用 iconv
,那么事情会变得更糟,尤其是当“静态 linked”可执行文件是建立在一个发行版上,然后复制到另一个发行版。 iconv
模块有时位于不同发行版的不同位置,因此在 Red Hat 发行版上构建的可执行文件可能无法 运行 在 Debian 发行版上正确构建,这恰恰相反人们对静态 linked 可执行文件的需求。