Linux静态链接已死?


69

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

2.22。即使是静态链接程序也需要一些共享库,这对我来说是不可接受的。我能做什么?

{AJ} NSS(有关详细信息,请键入`info libc“名称服务开关”“)在没有共享库的情况下无法正常工作。NSS只需更改一个配置文件(/etc/nsswitch.conf)即可使用不同的服务(例如NIS,文件,db,hesiod),而无需重新链接任何程序。唯一的缺点是现在静态库需要访问共享库。这由GNU C库透明地处理。

一种解决方案是使用--enable-static-nss配置glibc。在这种情况下,您可以创建仅使用服务dns和文件的静态二进制文件(为此更改/etc/nsswitch.conf)。您需要明确链接所有这些服务。例如:

这种方法的问题在于,必须将使用NSS例程的每个静态程序链接到所有这些库。
{UD}实际上,无法再说使用此选项编译的libc正在使用NSS。不再有开关。因此,强烈 建议不要使用--enable-static-nss,因为这会使系统上程序的行为不一致。

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

  • 行为与动态构建完全相同(行为不一致的static-nss是邪恶的!);
  • 适用于合理的glibc环境和Linux版本;

12
没有其他替代C库适合您的目的吗?(饮食/ uclibc /等等)?
蒂姆·波斯特

1
他们使用NSS吗?由于我怀疑这些库是否考虑了NSS,因此行为很可能也会不一致。
Shcheklein

您是否还使用了最终以nss结尾的任何功能(例如,gethostname / getpwname / getgroups / etc)?

1
当然))这是一个客户端/服务器应用程序。
Shcheklein

1
这是否仍然正确,还是自2010年以来发生了变化?
Atifm

Answers:


31

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

我不知道在哪里可以找到历史参考资料,但是是的,静态链接在GNU系统上已失效。(我相信它在从libc4 / libc5过渡到libc6 / glibc 2.x的过程中死了。)

鉴于以下原因,该功能被认为无用:

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

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

尝试挖掘10-15年前的LKML和glibc邮件列表档案。我很确定很早以前就已经看到了与LKML相关的内容。


44
不幸的是,他们没有提到另一面:静态链接二进制启动所需的时间比动态链接二进制启动所需的时间少90%,脏页开销也要低得多。如今,的eglibc分支glibc使静态链接至少再次中途可行,但是如果您想真正使用静态链接而又没有像nss这样的巨大二进制文件和错误/问题,则可能需要使用其他libc实现。
R .. GitHub停止帮助ICE,2010年

1
@R:您写道:“ ...但是,如果您想真正使用静态链接而又没有像nss这样的巨大二进制文件和错误/问题,则可能需要使用其他libc实现。” 有谁知道提供对nss的静态访问的libc实现?我想在NIS环境中静态访问getgrgid_r()。从理论上讲,可以使用对基础NIS例程的静态访问来实现grgetgid_r和其他类似的例程。有人真的这样做过吗?

4
我认为我的答案已经过时:我已经在Ubuntu 14.04上静态链接了中等大小的应用程序,没有任何问题。但是,静态链接错误的原因保持不变。除非,当然,除非一个人严重需要上面R ..列出的奖金,否则它们的弊端就超过了。
Dummy00001

1
@yugr:我有一个测试程序,该程序可以测量从执行前execvemain执行后第一行之间的时间。对于除了libc之外没有共享库的琐碎程序,静态链接的自我执行时间将减少大约50%,并且该时间在数十或数百微秒的范围内,具体取决于机器速度。加入几个共享库,您可以轻松地将其减少90%。您正在将O(n)时间与O(1)时间进行比较,因此无论每库成本多么小,您都可以任意接近100%,但实际上只需花费很少的时间。
R .. GitHub停止帮助ICE,

1
@yugr:即使只是mmap开销也足以使其在许多库中变得有意义。对于小型库而言,mmap时间将仍然占主导地位,但对于大型(尤其是C ++)库,重定位时间(包括需要修补的每个数据/ GOT页中的按需分页)是主要因素。
R .. GitHub停止帮助ICE,

46

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

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


25

静态链接又重新兴起!

  • 许多(大多数?)Go编程语言可执行文件都是静态链接的。
    • 增加的可移植性和向后兼容性是它们流行的原因之一。
  • 其他编程语言也做出了类似的努力,以使真正的链接真正变得容易,例如:
    • Haskell(我正在努力
    • Zig(请参阅此处了解详细信息)
  • 可配置的Linux发行版/软件包集(例如NixOS / nixpkgs)使静态链接其软件包的很大一部分成为可能(例如,其pkgsStatic软件包集可以提供各种静态链接的可执行文件)。
  • 静态链接可以在链接时更好地消除未使用的代码,从而使可执行文件更小。
  • 像musl这样的libcs使静态链接变得容易和正确。
  • 一些大型软件行业的领导人对此表示赞同。例如,谷歌正在编写针对静态链接的新libc“支持静态非PIE和静态PIE链接”“我们目前不打算在动态加载和链接支持方面进行投资”)。

自提出原始问题以来已有将近10年的时间,这是一个好消息。我个人看到了这种优势,因为我在一个受监管的行业工作,在那里我拥有的嵌入式目标在libc更新方面不太灵活。但是,我始终可以随时为我的(完全)静态链接的程序提供新的二进制文件,以将其链接到现场的设备。
苏宾·塞巴斯蒂安

公平地提及静态链接的缺点(漏洞修复速度慢,软件包更新updates肿,链接时间增加)将是很公平的。
yugr

@yugr增加链接时间可能是正确的。我认为其他人并非普遍如此。静态与动态对漏洞修复的速度没有影响;这仅取决于谁在提供更新。如果发行版提供了它们,则它们同样快,而如果第三方提供了它们,则它们可能比您选择的发行版更快或更慢。更新的大小取决于您要比较的内容。像Ubuntu这样的普通发行版可能必须通过共享对象传输较少的字节,但是通常,例如Docker映像必须批量升级,并且必须大于静态exe。
nh2

“我不认为其他人普遍适用”-可能但您的答案可能适用相同的原则)我是从普通Linux桌面用户的立场(他们使用一些流行的发行版,也许只有第三方提供的应用程序)开发人员)。
yugr

“静态与动态对漏洞修复的速度没有影响……如果第三方提供漏洞,它们的速度可能会更快或更慢”-我认为第三方提供商主要关注软件功能和/或在性能上,安全更像是二等公民。在任何情况下,第三方提供商都必须在跟踪安全问题上重复发行发行版的工作(这意味着更少的资源将花费在其产品上)。
yugr

24

在Linux世界中,静态链接似乎并没有获得多少支持。这是我的看法。

看不到静态链接的吸引力的人们通常在内核和较低级别的操作系统领域工作。许多* nix库开发人员花费了一生的时间来解决不可避免的问题,这些问题试图将数百个不断变化的库链接在一起,这是他们每天要做的任务。如果您想知道他们习惯执行的后空翻,可以看看自动工具。

但是,不应期望其他所有人都将大部分时间都花在此上。静态链接将使您很长的路要走,以免受到库流失的影响。开发人员可以根据软件的日程安排升级其软件的依赖项,而不必在新的库版本出现时立即执行。这对于具有复杂用户界面的面向用户的应用程序很重要,这些应用程序需要控制它们不可避免地依赖的许多低级库的流量。这就是为什么我将始终喜欢静态链接的原因。如果您可以静态链接交叉编译的可移植C和C ++代码,那么您将牡蛎变成了世界,因为您可以更快地将复杂的软件交付给世界范围内不断增长的设备。

从其他角度来看,还有很多不同意见,而且开源软件允许所有这些都很好。


应当公平地提及静态链接的缺点:对库更新的免疫力(以及由此引起的错误/漏洞修复),更长的链接时间和更大的可执行文件大小。
yugr

当然,您仍然可以在需要时(例如,当您即将发布版本时)进行库更新,因此值得添加最新的安全补丁程序-这样可以减少浪费的时间。尝试忍者加快链接时间。在一种非常罕见的情况下,二进制大小很重要-也许在很小的嵌入式空间中,但是在这种情况下,您可以剥离操作系统的共享库。同样,用例各不相同。
moodboom

1
“您仍在需要时仍在进行库更新”-通常,真正需要更新的人是您应用程序的用户,他们现在不能仅仅依靠发行版维护者提供的自动安全更新,而必须跟踪使用了哪个库版本(以静态方式链接的二进制文件)(或依靠您及时将所述二进制文件与固定库重新链接,从而使Distro安全团队的工作重复)。
yugr

“尝试ninja加快链接时间”-我不确定Ninja如何加快我的链接器。
yugr

1
docker容器的爆炸式增长显示出极大的兴趣,那就是以磁盘大小为代价来一次又一次地捕获整个静态环境。但是,正如我所说,这取决于您可能正在处理的微型可执行文件,您必须将其加载到busybox环境中。在这一点上,我们正在“在互联网上争论”。我承认有些时候您提到的观点是有效的。
moodboom

13

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


4
这意味着它不是完​​全静态的构建。实际上,在大多数情况下,将需要安装相同版本的glibc才能正常工作。为什么我需要这样的静态构建?
Shcheklein

3
@Dead:静态生成的可执行文件没有动态链接器,因此无法加载共享库。我可以找到的最佳参考:en.wikipedia.org/wiki/Static_build。正如我在下面写的,Linux并非故意支持它。
Dummy00001 2010年

1
到了游戏后期,但是当在高度严格的系统上工作时,例如一个系统上的c ++编译器仅支持C ++ 98,并且移植了写入C ++ 11的代码,...在支持该功能的RHEL 6系统上进行编译C ++ 0x,然后将二进制文件复制到不支持C ++ 0x的较旧RHEL 5系统中。如果我可以静态链接整个应用程序,则它将在RHEL 5系统上运行。静态链接为何有用的一个示例。但是我不能-因为一个原因是,在g ++ 4.4.7上没有G ++编译器选项可静态链接到C ++库中。
Minok 2015年

11

加上其他答案:

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

从静态描述:

静态linux基于为每个任务和静态链接的每个工具(包括一些X客户端,例如st,surf,dwm,dmenu)的最佳工具的精选集合,

它还通过尽可能避免使用glibc和其他过大的GNU库来减少二进制文件的大小(早期实验表明,静态链接的二进制文件通常小于动态链接的glibc对应文件!!!)。请注意,这与Ulrich Drepper认为的有关静态链接的说法完全相反。

由于静态链接的二进制文件起步较快的附带好处,因此发行版还以提高性能为目标。

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

您可以在有关静态链接与动态链接的问题中阅读更多有关它的内容

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.