ChatGPT解决这个技术问题 Extra ChatGPT

Linux静态链接死了?

事实上,Linux 上的 -static gcc 标志现在不起作用。让我引用 GNU libc 常见问题解答:

2.22。即使是静态链接的程序也需要一些我无法接受的共享库。我能做些什么? {AJ} NSS(有关详细信息,只需键入 `info libc "Name Service Switch"')在没有共享库的情况下将无法正常工作。 NSS 允许通过更改一个配置文件 (/etc/nsswitch.conf) 来使用不同的服务(例如 NIS、文件、db、hesiod),而无需重新链接任何程序。唯一的缺点是现在静态库需要访问共享库。这由 GNU C 库透明地处理。一种解决方案是使用 --enable-static-nss 配置 glibc。在这种情况下,您可以创建一个仅使用服务 dns 和文件的静态二进制文件(为此更改 /etc/nsswitch.conf)。您需要明确链接所有这些服务。例如: gcc -static test-netdb.c -o test-netdb \ -Wl,--start-group -lc -lnss_files -lnss_dns -lresolv -Wl,--end-group 这种方法的问题是你'必须将每个使用 NSS 例程的静态程序与所有这些库链接起来。 {UD} 事实上,不能再说使用此选项编译的 libc 正在使用 NSS。没有开关了。因此强烈建议不要使用 --enable-static-nss,因为这会使系统上程序的行为不一致。

关于这个事实,现在是否有任何合理的方法可以在 Linux 上创建功能齐全的静态构建,或者静态链接在 Linux 上完全死了?我的意思是静态构建:

行为与动态构建完全相同(行为不一致的静态nss是邪恶的!);

适用于 glibc 环境和 Linux 版本的合理变化;

没有其他替代 C 库适合您的目的吗? (饮食 / uclibc / 等)?
他们使用 NSS 吗?最有可能的是,行为也会不一致,因为我怀疑这些库是否考虑了 NSS。
您是否甚至使用最终调用 nss 的任何函数(例如 gethostname/getpwname/getgroups/等)?
当然))这是一个客户端/服务器应用程序。
这仍然是真的,还是自 2010 年以来情况发生了变化?

K
Ketil

我认为这很烦人,并且我认为将某个功能称为“无用”是傲慢的,因为它在处理某些用例时存在问题。 glibc 方法的最大问题是它对系统库(gconv 和 nss)的路径进行硬编码,因此当人们尝试在不同于构建它的 Linux 发行版上运行静态二进制文件时,它会中断。

无论如何,您可以通过将 GCONV_PATH 设置为指向适当的位置来解决 gconv 问题,这使我可以获取在 Ubuntu 上构建的二进制文件并在 Red Hat 上运行它们。


n
nh2

静态链接又回来了!

Linus Torvalds 支持静态链接,并对 Linux 发行版中的静态链接数量表示担忧(另请参阅此讨论)。

许多(大多数?)Go 编程语言可执行文件是静态链接的。增加的可移植性和向后兼容性是它们流行的原因之一。

增加的可移植性和向后兼容性是它们流行的原因之一。

其他编程语言也有类似的努力使静态链接变得非常容易,例如:Haskell(我正在努力)Zig(详见此处)

Haskell(我正在努力)

之字(详情见这里)

可配置的 Linux 发行版 / 包集,如 NixOS / nixpkgs 使得静态链接大部分包成为可能(例如,它的 pkgsStatic 包集可以提供各种静态链接的可执行文件)。

静态链接可以在链接时更好地消除未使用的代码,从而使可执行文件更小。

像 musl 这样的 libcs 使静态链接变得简单而正确。

一些大型软件行业的领导者同意这一点。例如,Google 正在编写针对静态链接的新 libc(“支持静态非 PIE 和静态 PIE 链接”、“我们目前不打算投资 [in] 动态加载和链接支持”)。


在提出最初的问题近 10 年后,这是一个好消息。我个人看到了优势,因为我在一个受监管的行业工作,我拥有的嵌入式目标对于 libc 更新不太灵活。但是我总是可以随时为我的(完全)静态链接的程序提供一个新的二进制文件到现场的设备。
公平地提及静态链接的缺点(漏洞修复速度较慢、软件包更新臃肿、链接时间增加)是公平的。
@yugr 链接时间增加可能是真的。我不认为其他人是普遍正确的。静态 vs 动态对漏洞修复速度没有影响;这仅取决于谁在提供更新。如果发行版提供它们,它们同样快,如果第三方提供它们,它们可能比您选择的发行版更快或更慢。更新的大小取决于您要比较的内容。像 Ubuntu 这样的普通发行版可能必须通过共享对象传输更少的字节,但通常例如 Docker 映像必须批量升级并且比静态 exe 更大。
“我不认为其他人普遍正确” - 可能但同样的原则可能适用于您的答案)我从普通 Linux 桌面用户的角度来看(他们使用一些流行的发行版,也许第三方的应用程序很少开发商)。
“静态与动态对漏洞修复的速度没有影响......如果第三方提供它们,它们可能更快或更慢” - 我认为第三方提供商主要专注于软件功能和/或性能、安全性更多的是二等公民。而且在任何情况下,第三方提供商都必须重复发行版在跟踪安全问题方面的工作(这意味着在他的产品上花费的资源更少)。
D
Dummy00001

关于这个事实,现在是否有任何合理的方法可以在 Linux 上创建功能齐全的静态构建,或者静态链接在 Linux 上完全死了?

我不知道在哪里可以找到历史参考,但是是的,static linking 在 GNU 系统上已死。 (我相信它在从 libc4/libc5 到 libc6/glibc 2.x 的过渡期间死了。)

鉴于以下情况,该功能被认为是无用的:

安全漏洞。静态链接的应用程序甚至不支持升级 libc。如果应用程序链接到包含 lib 漏洞的系统上,那么它将在静态链接的可执行文件中永久存在。

代码膨胀。如果许多静态链接的应用程序在同一个系统上运行,标准库就不会被重用,因为每个应用程序都包含在它自己的所有内容的副本中。 (尝试 du -sh /usr/lib 以了解问题的严重程度。)

尝试挖掘 10-15 年前的 LKML 和 glibc 邮件列表档案。我很确定很久以前我在 LKML 上看到过一些相关的东西。


不幸的是,他们没有提到另一面:静态链接的二进制文件的启动时间比动态链接的二进制文件少 90%,脏页开销也低得多。这些天来,glibceglibc 分支使静态链接至少再次成为可能,但如果您想实际使用静态链接而没有巨大的二进制文件和像 nss 这样的错误/问题,您可能需要使用不同的 libc执行。
@R:你写道,“......但如果你想实际使用静态链接而不需要巨大的二进制文件和像 nss 这样的错误/问题,你可能需要使用不同的 libc 实现”。有谁知道提供对 nss 的静态访问的 libc 实现?我想在 NIS 环境中静态访问 getgrgid_r() 。理论上,有人可以使用对底层 NIS 例程的静态访问来实现 grgetgid_r 和其他类似例程。有没有人真的这样做过?
我认为我的答案已经过时了:我在 Ubuntu 14.04 上静态链接了一个中等大小的应用程序,没有任何问题。但是静态链接不好的原因保持不变。当然,除非一个人非常需要上面 R.. 列出的奖金,而且它们超过了不利因素。
@yugr:我有一个测试程序,可以测量从 execve 之前到自执行之后 main 的第一行所经过的时间。对于除了 libc 之外没有共享库的普通程序,静态链接自执行所需的时间减少了大约 50%,并且时间在几十或几百微秒的范围内,具体取决于机器速度。加入一些共享库,您可以轻松地将其减少 90%。您将 O(n) 时间与 O(1) 时间进行比较,因此无论每个库的成本有多小,您都可以任意接近 100%,但实际上只需要很少的时间。
@yugr:即使只是 mmap 开销也足以让它在许多库中变得重要。对于小型库mmap,时间将占主导地位,但对于大型(尤其是 C++)库,重定位时间,包括需要修补的每个数据/GOT 页面中的需求分页,是主要因素。
S
Smi

静态链接在 Linux 世界中似乎并不受欢迎。这是我的看法。

没有看到静态链接吸引力的人通常在内核和较低级别的操作系统领域工作。许多 *nix 库开发人员一生都在处理不可避免的问题,试图将一百个不断变化的库链接在一起,这是他们每天都在做的任务。如果您想了解他们擅长的后空翻,请查看自动工具。

但不应期望其他所有人将大部分时间都花在这上面。静态链接将使您远离库流失的缓冲。开发人员可以根据软件的时间表升级她的软件的依赖项,而不是在新的库版本出现时被迫这样做。这对于具有复杂用户界面的面向用户的应用程序非常重要,这些应用程序需要控制它们不可避免地依赖的许多低级库的流量。这就是为什么我将永远是静态链接的粉丝。如果您可以静态链接交叉编译的可移植 C 和 C++ 代码,那么您几乎可以将世界变成您的牡蛎,因为您可以更快地将复杂的软件交付给世界上各种不断增长的设备。

从其他角度来看,那里有很多不同意的地方,开源软件允许所有这些都很好。


公平地提及静态链接的缺点:不受库更新(以及因此的错误/漏洞修复)、更长的链接时间和更大的可执行文件大小。
相反,您仍然会在需要时进行库更新,例如,当您即将发布并且值得合并最新的安全补丁时 - 这会减少浪费的时间。尝试忍者加快链接时间。这是一种非常罕见的情况,二进制大小很重要 - 也许在微小的嵌入式空间中,但在这种情况下您可以剥离操作系统的共享库。同样,用例各不相同,ymmv,所有这些。
“您仍然在需要时进行库更新” - 通常真正需要更新的人是您的应用程序的用户,他们现在不能简单地依赖发行版维护者提供的自动安全更新,而是必须跟踪使用的任何库版本在您的静态链接二进制文件中(或依赖您及时将所述二进制文件与固定库重新链接,复制发行版安全团队的工作)。
“尝试忍者加快链接时间” - 我不确定忍者如何加快我的链接器。
Docker 容器的爆炸式增长显示出对以牺牲磁盘大小为代价一次又一次地捕获整个静态环境的极大兴趣。但同样,正如我所说,这取决于您可能正在处理一个必须加载到busybox环境或诸如此类的小型可执行文件。在这一点上,我们正在“在互联网上争论”。我承认有时你提到的观点是有效的。
D
Dean Harding

仅仅因为您必须动态链接到 NSS 服务并不意味着您不能静态链接到任何其他库。常见问题解答的所有内容是,即使是“静态”链接的程序也有一些动态链接的库。这并不是说静态链接“不可能”或“不起作用”。


这意味着它不是完全静态的构建。事实上,在大多数情况下,它需要安装相同版本的 glibc 才能正常工作。为什么我需要这样的静态构建?
@Dead:静态构建的可执行文件没有动态链接器,因此无法加载共享库。我能找到的最佳参考:en.wikipedia.org/wiki/Static_build。正如我在下面所写的,Linux 并非有意支持它。
游戏晚了,但是当在高度严格的系统上工作时,比如说一个系统上的 c++ 编译器只支持 C++98,而用 C++11 编写的代码被移植......在支持的 RHEL 6 系统上编译它C++0x,然后将二进制文件复制到不支持 C++0x 的旧 RHEL 5 系统中。如果我可以静态链接整个应用程序,那么它将在 RHEL 5 系统上运行。为什么静态链接有用的一个例子。但我不能——因为一方面,g++ 4.4.7 上没有 G++ 编译器选项可以静态链接到 C++ 库中。
C
Community

添加其他答案:

由于其他答案中所述的原因,不建议将其用于大多数 Linux 发行版,但实际上有一些发行版专门用于运行静态链接的二进制文件:

斯塔里

睡眠

淀粉Linux

彩虹桥

从斯塔利描述:

static linux 是基于为每个任务和每个静态链接的工具(包括一些 X 客户端,例如 st、surf、dwm、dmenu)手工选择的最佳工具集合,它还通过避免 glibc 和其他臃肿的 GNU 库(早期实验表明,静态链接的二进制文件通常比动态链接的 glibc 对应的文件小!!!)。请注意,这与 Ulrich Drepper 对静态链接的估计完全相反。由于静态链接的二进制文件启动速度更快的附带好处,该分发还以性能提升为目标。

静态链接也有助于减少依赖。

您可以在 this question about static vs dynamic linking 中阅读有关它的更多信息。