重新绑定DLL意味着修复DLL,因此它首选的加载地址是加载器实际上能够加载DLL的加载地址。
这可以通过诸如Rebase.exe
的工具来实现,也可以通过为所有(自己的)dll指定默认加载地址,以使它们“适合”在可执行进程中。
以这种方式pipe理DLL基地址的关键是加速应用程序负载。 (或者我明白)
现在的问题是: 值得麻烦吗?
我有Richter / Nazarre的C / C ++书,他们强烈build议[a]确保加载地址全部匹配,以便Loader不必重新加载加载的DLL。
然而,他们没有争论,如果这加快了应用程序加载时间的任何重要的数额。
而且,对于ASLR来说,这似乎有点可疑,因为无论如何,加载地址都是随机的。
有没有关于这个利弊的事实?
[a]:在我的WvC ++ / 5th ed中,在第568ff页的标题为Rebasing Modules and Binding Modules的章节中。 在第20章, DLL高级技术 。
修补可重定位地址并不是什么大事,它以内存速度运行,微秒。 更大的问题是,包含此代码的页面现在需要由分页文件而不是DLL文件进行备份。 换句话说,当包含代码的页面未被映射时,它们需要被写入分页文件而不是被丢弃。
这个成本并不容易衡量,特别是在有大量RAM的现代机器上。 只有当机器开始承受很多进程争夺内存的负担时才算数。 和分页文件的碎片。
但显然,重新分配是一个非常便宜的优化。 在“Debug + Windows +模块”窗口中可以很容易地看到,重新绑定的DLL上有一个明亮的图标。 地址列给你一个很好的提示什么基地址是一个不错的选择。 在它们之间留出足够的空间,这样随着程序的增长,你不需要经常调整它。
我想自己提供一个答案,虽然Hans Passant和其他人的回答已经很好地描述了这种权衡。
最近在我们的应用程序中调用DLL基础地址之后,我会在这里给出我的结论:
我认为,除非你能证明,否则提供一个非默认的基地址的DLL是徒劳无益的。 这包括重新绑定我的DLL。
对于我控制的DLL ,给定普通的应用程序,每个DLL只会一次加载到内存中,所以分页文件的负载应该是最小的。 (但请参阅关于终端服务器环境的另一个回答Michal Burr的评论。)
如果DLL提供了一个固定的基地址(没有重新绑定),它实际上会增加地址空间碎片,因为这些地址迟早不会匹配。 在我们的应用程序中,我们已经给所有的DLL一个固定的基地址(由于其他遗留原因,而不是因为地址空间碎片),而不使用rebase.exe,这显着增加了我们的地址空间碎片,因为你真的无法手动。
重新激活(通过rebase.exe) 并不便宜 。 这是构建过程中的另一个步骤,必须进行维护和检查,因此必须具有一定的优势。
一个大的应用程序将总是有一些DLL加载在基地址不匹配,因为一些钩子DLL(AV),因为你不rebase第三方DLL(或至少我不会)。
如果您正在使用RAM磁盘作为分页文件 ,那么如果加载的DLL被分页出去,您可能会更好一些:-)
所以总结一下,我认为除了像系统DLL这样的特殊情况之外,重新组装是不值得的 。
我想添加一个旧的新事物上发现的历史片断: Windows 95如何重新绑定DLL? –
当一个DLL需要重新组装时,Windows 95只会记录这个DLL的新的基址,但是不会做太多的事情。 真正的工作发生在DLL的页面最终被换入时。原始页面已经从磁盘上交换,然后修复被应用到原始页面,从而重新定位它。 然后将固定页面映射到进程的地址空间,程序被允许继续。
看看这个过程是怎么完成的( 阅读整个过程),我个人认为,“重新定义是邪恶”的一部分可以追溯到过去的Win9x和低内存条件。
看,现在有一个关于旧新事物的非历史的片断 :
现在是多么重要,以确保我所有的DLL有不冲突的基地址?
在当天,你被要求做的事情之一是重新绑定你的DLL,以便它们都具有不重叠的地址范围,从而避免了运行时重定位的代价。 这现在还很重要吗?
…
在存在ASLR的情况下,重新绑定DLL不会产生任何影响,因为ASLR 会忽略您的基址 ,并将DLL重定位到其伪随机选择的位置。
…
结论:为了以防万一,不要为了重建而伤害,但要明白回报是非常罕见的 。 使用
/DYNAMICBASE
启用(使用/HIGHENTROPYVA
进行更好的测量)来构建DLL,并让ASLR执行确保不发生基址冲突的工作。 这将涵盖几乎所有的真实情况。 如果碰巧遇到ASLR不可用的情况,那么你的程序仍然可以工作。 由于搬迁的惩罚,它可能会稍微慢一些。ASLR实际上在避免冲突方面比手动重新组合更好,因为ASLR可以将整个系统视为一个整体,而手动重新组装需要您了解加载到您的流程中的所有DLL,并协调多个供应商的基本地址通常是不可能的。
然而,他们没有争论,如果这加快了应用程序加载时间的任何重要的数额。
加载时间的变化是最小的,因为v表是新的地址更新。 但是,如果内存不足 – 足够的东西被装入/卸出页面文件,则系统必须将dll保存在页面文件中(因为地址已更改)。 如果这些dll已经重新发布,并且重新发布的dll不会与任何其他dll发生冲突,那么系统就不会将它们交换到页面文件(并返回),而只是覆盖内存并在硬盘上重新加载dll驾驶。
这个好处只有在系统将内存分页到主内存中时才有意义。 上一次我努力保持应用程序的数据库和基地址是在VB6的时候,当我们的办公室和数据中心的电脑幸运地有256MB的RAM。
而且,对于ASLR来说,这似乎有点可疑,因为无论如何,加载地址都是随机的。
目前ASLR只影响动态重定位标志设置的dll和可执行文件。 这包括Vista / Win7系统dll和可执行文件,以及任何开发者制作的项目,开发者在构建期间有意设置这个标志 。
如果你要设置动态重定位标志,那么不要打扰重新绑定DLL。 如果你所有的客户端都有4GB的内存,那就不用麻烦了。 如果你的老板是个吝啬鬼,也许吧。
您必须考虑用户DLL(尚未加载到其他进程中)必须从HDD中读取。 通常情况下,内存映射是用于这个(它使用延迟加载),所以如果他们必须重新定位,他们将不得不实际从HDD读取,才能开始进程。
对于其他进程加载的那些,使用了写入时复制机制。 所以,再次搬迁他们将意味着额外的行动。
什么是ASLR,它的目的是为了安全目的,而不是性能。
是的,你应该这样做。 ASLR仅影响“系统”DLL,因此您正在撰写的DLL不应受ASLR的影响。 此外,ASLR不会完全“随机化”这些系统二进制文件的位置,它只是在虚拟地图中的基本位置上洗牌。