静态链接被认为有害

2020-09-29 00:04:07

仍然有太多的人认为(甚至坚持)静态链接有好处。这种情况从来不是,将来也不会是。以下是动态链接更优越的几个原因:

修复(安全修复或仅修复错误)只能应用于一个地方:新的DSO。如果各种应用程序是静态链接的,则所有这些应用程序都必须重新链接。在发现问题时,系统管理员通常会忘记哪些应用程序是用有问题的库构建的。我认为这一点(连同下一条)就是致命的论据。

不能使用加载地址随机化等安全措施。对于静态链接的应用程序,只有堆栈和堆地址可以随机化。所有文本在所有调用中都有固定地址。对于动态链接的应用程序,内核能够加载任意地址的所有DSO,并且彼此独立。如果将应用程序构建为独立于位置的可执行文件(PIE),甚至可以在随机地址加载此代码。固定地址(甚至只有固定偏移量)是攻击者的梦想。而且,通常不可能生成具有静态链接的Pie。在IA-32上,可以在Pie中使用不使用-fpic和-fbie编译的代码(尽管会有成本),但对于其他体系结构(包括x86-64)则不是这样。

更高效地使用物理内存。对于DSO中的代码,所有进程共享相同的物理页。使用预链接,动态链接代码的启动时间与静态链接代码的启动时间一样好。

Libc中的所有功能(locale(通过iconv)、nss、idn等...)。需要动态链接以加载适当的外部代码。我们对在静态链接代码中执行此操作的支持非常有限。但是它要求在运行时可用的动态加载模块必须来自与链接到应用程序的代码相同的glibc版本。并且完全不支持以这种方式动态加载不是Glibc一部分的DSO。发布所有依赖项完全违背了静态链接People站点的优势:发布一个二进制文件就足以让它在任何地方都能工作。

相关的、琐碎的NSS模块可以直接从静态链接的应用程序中使用。如果它们需要广泛的依赖关系(比如LDAP NSS模块,而不是glibc本身的一部分),那么这很可能不会起作用。由于NSS模块的选择取决于部署代码的人(而不是开发人员),因此不可能假设不使用这些类型的模块。

没有意外违反(L)GPL。如果将静态链接的程序提供给第三方,则有必要提供重新生成程序代码的可能性。

Ltrace、LD_PRELOAD、LD_PROFILE、LD_AUDIT等工具和黑客不起作用。这些可以是有效的调试和分析,特别是对于不能信任用户进行复杂调试工作的远程调试。

当然还有更多的原因。经常使用的关于静态链接的应用程序更便携(即,可以复制到其他系统并且由于没有依赖关系而简单地使用)的论点是不正确的,因为至少出于上述原因之一,每个非平凡的程序都需要动态链接。动态链接扼杀了静态链接应用程序的可移植性。

有一个方面,人们决定曲解这些建议。我并不是说所有的东西都应该塞进它自己的DSO中。我永远不会这么说。恰恰相反。由于开发过程而属于同一对象的代码应该保存在同一对象中。原因是所需的DSO越少,一切工作就越快(加载、符号解析等)。

OpenOffice.org就是这样做得不对的一个例子。如果您查看二进制文件(swriter.bin等)的依赖关系,您将看到许多来自OO.org项目的文件。几乎所有的OO.org程序都使用了它们,没有一个是在项目之外使用的。尤其是libsoffice.so本身就吸引了大量的dso。这在所有的OO.org程序中都是这样做的!

在部署期间,没有理由这样做。由libsoffic.so引入的所有DSO,可能还有各种程序中使用的其他一些DSO,都应该是一个大型DSO的一部分。是的,此DSO会很大,但它会小于所有已加载依赖项的总和。此外,还可以进一步限制出口列表,在新的DSO中进行大量调用的速度要快得多。

有一个很好的理由在开发期间拥有所有独立的DSO。这使得只重建源树的一小部分来测试某些更改成为可能。但这就是发展。当二进制文件交付给用户时(例如,作为发行版的一部分),这种需要就消失了,理由也就消失了。