重新定基 DLL(或提供适当的默认加载地址)值得这么麻烦吗?

发布于 2024-09-28 07:07:14 字数 791 浏览 5 评论 0原文

变基 DLL 意味着修复 DLL,使其首选加载地址是加载器实际上能够加载 DLL 的加载地址。

这可以通过 Rebase.exe 等工具来实现,也可以通过为所有(自己的)dll 指定默认加载地址,以便它们“适合”您的可执行进程来实现。

以这种方式管理 DLL 基地址的全部目的是加快应用程序加载速度。 (或者我是这么理解的。)

现在问题是:值得这么麻烦吗?

我有这本书Windows via C/C++ by Richter/Nazarre 他们强烈建议[a]确保加载地址全部匹配这样加载器就不必重新设置加载的 DLL 的基址。

然而,他们没有争论这是否会显着加快应用程序加载时间。

另外,对于 ASLR ,这是否有任何价值似乎值得怀疑全部,因为加载地址无论如何都会被随机化。

对此的优点/缺点有任何确凿的事实吗?

[a]:在我的 WvC++/第 5 版中,它位于第 568ff 页上标题为“Rebasing Modules”和“Binding Modules”的部分。第 20 章,DLL 高级技术

Rebasing a DLL means to fix up the DLL such, that it's preferred load adress is the load address that the Loader is actually able to load the DLL at.

This can either be achieved by a tool such as Rebase.exe or by specifying default load addresses for all your (own) dlls so that they "fit" in your executable process.

The whole point of managing the DLL base addresses this way is to speed up application loads. (Or so I understand.)

The question is now: Is it worth the trouble?

I have the book Windows via C/C++ by Richter/Nazarre and they strongly recommend[a] making sure that the load addresses all match up so that the Loader doesn't have to rebase the loaded DLLs.

They fail to argue however, if this speeds up application load times to any significant amount.

Also, with ASLR it seems dubious that this has any value at all, since the load addresses will be randomized anyway.

Are there any hard facts on the pro/cons of this?

[a]: In my WvC++/5th ed it is in the sections titled Rebasing Modules and Binding Modules on pages 568ff. in Chapter 20, DLL Advanced Techniques.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

暮年慕年 2024-10-05 07:07:14

修补可重定位地址并不是什么大问题,它以微秒的内存速度运行。更大的问题是,包含此代码的页面现在需要由页面文件而不是 DLL 文件来备份。换句话说,当包含代码的页面未映射时,需要将它们写入分页文件,而不是直接丢弃。

其成本并不容易衡量,尤其是在具有大量 RAM 的现代机器上。仅当机器开始承受大量进程争夺内存的负载时,它才有意义。以及分页文件的碎片。

但显然,变基是一种非常廉价的优化。在 Debug + Windows + Modules 窗口中很容易看到,rebased DLL 上有一个明亮的图标。地址列为您提供了一个很好的提示,什么基地址是一个不错的选择。在它们之间留出足够的空间,这样您就不必随着程序的增长而不断调整。

Patching the relocatable addresses isn't the big deal, that runs at memory speeds, microseconds. The bigger issue is that the pages that contains this code now need to be backed up by the paging file instead of the DLL file. In other words, when pages containing code are unmapped, they need to be written to the paging file instead of just getting discarded.

The cost of this isn't that easy to measure, especially on modern machines with lots of RAM. It only counts when the machine starts to get under load with lots of processes competing for memory. And the fragmentation of the paging file.

But clearly, rebasing is a very cheap optimization. And it is very easy to see in the Debug + Windows + Modules window, there's a bright icon on the rebased DLLs. The Address column gives you a good hint what base address would be a good choice. Leave ample space between them so you don't constantly have to tweak this as your program grows.

忆伤 2024-10-05 07:07:14

我想自己提供一个答案,尽管 Hans Passant 和其他人的回答已经很好地描述了权衡。

最近在我们的应用程序中摆弄了 DLL 基地址之后,我将在这里给出我的结论:

我认为,除非您能证明相反,否则为 DLL 提供非默认基地址是徒劳的。这包括重新调整我的 DLL 的基础。

  • 对于我控制的DLL,对于一般的应用程序,每个DLL无论如何都只会加载到内存中一次,因此分页文件上的负载应该是最小的。 (但是请参阅 Michal Burr 在有关终端服务器环境的另一个答案中的评论。)

  • 如果 DLL 提供了固定的基地址(没有变基),它实际上会增加地址空间碎片,因为迟早这些地址不会不再匹配了在我们的应用程序中,我们为所有 DLL 指定了固定的基地址(出于其他遗留原因,而不是因为地址空间碎片),而没有使用 rebase.exe,这显着增加了我们的地址空间碎片,因为您确实无法手动解决此问题。

  • 变基(通过 rebase.exe)并不便宜。这是构建过程中必须维护和检查的另一个步骤,因此它必须有一些好处。

  • 大型应用程序总是会加载一些基地址不匹配的 DLL,因为有一些钩子 DLL (AV) 并且因为您没有对第 3 方 DLL 进行变基(或者至少我不会)。

  • 如果您将 RAM 磁盘用于分页文件< /a>,如果加载的 DLL 被调出页面,您实际上可能会更好:-)

所以总而言之,我认为变基 不值得这么麻烦,除了像系统 DLL 这样的特殊情况。


我想添加我在 Old New Thing 上找到的历史文章: Windows 95 是如何做到的变基 DLL? --

当 DLL 需要重新建立基础时,Windows 95 只会做一个注释
DLL 的新基地址,但不会做太多其他事情。真实的
当 DLL 的页面最终被交换时,工作就发生了。
原始页面已从磁盘上交换,然后应用了修复
飞到原始页面,从而重新定位它。固定页面是
然后映射到进程的地址空间,程序被
允许继续。

看看这个过程是如何完成的(阅读全文),我个人怀疑“变基是邪恶的”立场的一部分可以追溯到 Win9x 的旧时代和低内存条件。


看,现在有一个关于旧新事物的非历史文章

现在确保我的所有 DLL 具有不冲突的基地址有多重要?

过去,你被劝告做的事情之一就是 rebase
您的 DLL 以便它们都具有不重叠的地址范围,从而
避免运行时重定位的成本。这还重要吗
现在?

...

在存在 ASLR 的情况下,对 DLL 进行变基不会产生任何影响,因为 ASLR无论如何都会忽略您的基地址并将 DLL 重新定位到其伪随机选择的位置。

...

结论:为了以防万一,rebase 并没有什么坏处,但是要理解
回报将极其罕见。构建你的 DLL
已启用 /DYNAMICBASE(并使用 /HIGHENTROPYVA 进行良好测量)
并让 ASLR 完成确保不发生基地址冲突的工作
发生。这将涵盖几乎所有现实世界的场景。
如果您碰巧遇到 ASLR 的极少数情况之一
不可用,那么你的程序仍然可以工作。它可能只是运行一个
由于搬迁惩罚,速度会慢一些。

... ASLR 实际上比手动在避免碰撞方面做得更好
rebasing,因为 ASLR 可以将系统视为一个整体,而手动
变基要求您知道加载到您的系统中的所有 DLL
过程,并协调多个供应商的基地址
一般是不可能的。

I'd like to provide one answer myself, although the answers of Hans Passant and others are describing the tradeoffs already pretty well.

After recently fiddling with DLL base addresses in our application, I will here give my conclusion:

I think that, unless you can prove otherwise, providing DLLs with a non-default Base Address is an exercise in futility. This includes rebasing my DLLs.

  • For the DLLs I control, given the average application, each DLL will be loaded into memory only once anyway, so the load on the paging file should be minimal. (But see the comment of Michal Burr in another answer about Terminal Server environment.)

  • If DLLs are provided with a fixed base address (without rebasing) it will actually increase address space fragmentation, as sooner or later these addresses won't match anymore. In our app we had given all DLLs a fixed base address (for other legacy reasons, and not because of address space fragmentation) without using rebase.exe and this significantly increased address space fragmentation for us because you really can't get this right manually.

  • Rebasing (via rebase.exe) is not cheap. It is another step in the build process that has to be maintained and checked, so it has to have some benefit.

  • A large application will always have some DLLs loaded where the base address does not match, because of some hook DLLs (AV) and because you don't rebase 3rd party DLLs (or at least I wouldn't).

  • If you're using a RAM disk for the paging file, you might actually be better of if loaded DLLs get paged out :-)

So to sum up, I think that rebasing isn't worth the trouble except for special cases like the system DLLs.


I'd like to add a historical piece that I found on Old New Thing: How did Windows 95 rebase DLLs? --

When a DLL needed to be rebased, Windows 95 would merely make a note
of the DLL's new base address, but wouldn't do much else. The real
work happened when the pages of the DLL ultimately got swapped in. The
raw page was swapped off the disk, then the fix-ups were applied on
the fly to the raw page, thereby relocating it. The fixed-up page was
then mapped into the process's address space and the program was
allowed to continue.

Looking at how this process is done (read the whole thing), I personally suspect that part of the "rebasing is evil" stance dates back to the olden days of Win9x and low memory conditions.


Look, now there's a non-historical piece on Old New Thing:

How important is it nowadays to ensure that all my DLLs have non-conflicting base addresses?

Back in the day, one of the things you were exhorted to do was rebase
your DLLs so that they all had nonoverlapping address ranges, thereby
avoiding the cost of runtime relocation. Is this still important
nowadays?

...

In the presence of ASLR, rebasing your DLLs has no effect because ASLR is going to ignore your base address anyway and relocate the DLL into a location of its pseudo-random choosing.

...

Conclusion: It doesn't hurt to rebase, just in case, but understand
that the payoff will be extremely rare. Build your DLL with
/DYNAMICBASE enabled (and with /HIGHENTROPYVA for good measure)
and let ASLR do the work of ensuring that no base address collision
occurs. That will cover pretty much all of the real-world scenarios.
If you happen to fall into one of the very rare cases where ASLR is
not available, then your program will still work. It just may run a
little slower due to the relocation penalty.

... ASLR actually does a better job of avoiding collisions than manual
rebasing, since ASLR can view the system as a whole, whereas manual
rebasing requires you to know all the DLLs that are loaded into your
process, and coordinating base addresses across multiple vendors is
generally not possible.

々眼睛长脚气 2024-10-05 07:07:14

但是,他们没有争论这是否会显着加快应用程序加载时间。

加载时间的变化很小,因为虚拟表是用新地址更新的。但是,如果内存不足 - 足以将内容加载到页面文件中/从页面文件中加载出来,那么系统必须将 dll 保留在页面文件中(因为地址已更改)。如果 dll 被重新设置基数 - 并且重新设置基数的 dll 不会与任何其他 dll 发生冲突 - 那么系统不会将它们换出到页面文件(或换回),而是覆盖内存并从硬盘上的原始文件重新加载 dll驾驶。

仅当系统将内容调入或调出主内存时,这种好处才有意义。上一次我努力保存应用程序及其基址的数据库是在 VB6 时代,当时我们办公室和数据中心的计算机幸运地拥有 256MB 的 RAM。

此外,对于 ASLR 来说,这是否有任何价值似乎值得怀疑,因为加载地址无论如何都会被随机化。

目前,ASLR 仅影响设置了动态重定位标志的 dll 和可执行文件。这包括 Vista/Win7 系统 dll 和可执行文件,以及任何开发人员在构建期间故意设置该标志的项目。

如果您要设置动态重定位标志,则不必费心重新确定 dll 的基础。如果您的所有客户端都有 4GB RAM,那就不用担心。如果你的老板是个小气鬼,那么也许吧。

They fail to argue however, if this speeds up application load times to any significant amount.

The load time change is minimal, because the v-table is what gets updated with the new addresses. However, if you have low memory - enough that stuff gets loaded in/out of the page file, then the system has to keep the dll in the page file (since the addresses are changed). If the dlls were rebased - and the rebased dlls don't collide with any other dlls - then instead of swapping them out to the page file (and back), the system just overwrites the memory and reloads the dll from the original on the hard drive.

The benefit is only relevant when systems are paging stuff in and out of main memory. The last time I made efforts to keep databases of applications and their base addresses was back in VB6 days, when the computers in our offices and data centers were lucky to have even 256MB of RAM.

Also, with ASLR it seems dubious that this has any value at all, since the load addresses will be randomized anyway.

At the moment ASLR only affects dlls and executables with the dynamic-relocation flag set. This includes Vista/Win7 system dlls and executables, and any developer made items where the developer intentionally set that flag during the build.

If you are going to set the dynamic-relocation flag, then don't bother rebasing the dlls. If all your clients have 4GB of RAM, then don't bother. If your boss is a cheapskate, then maybe.

失退 2024-10-05 07:07:14

您必须考虑必须从 HDD 读取用户 DLL(尚未加载到其他进程中)。通常,内存映射用于此目的(并且它使用延迟加载),因此如果必须重新定位它们,则必须在进程启动之前从 HDD 实际读取它们。

对于那些由其他进程加载的数据,使用写时复制机制。因此,重新安置它们将意味着额外的操作。

ASLR 是怎么回事,它是为了安全目的,而不是为了性能。

You have to consider that user DLLs (that are not already loaded into another processes) has to be read from HDD. Usually the memory mapping is used for that (and it uses lazy loading), so if they have to be relocated, they'll have to be actually read from HDD before the process can start.

For those loaded by other processes the copy-on-write mechanism is used. So, again, relocating them will mean additional operations.

What's about ASLR, it's intended for security purposes, not for performance.

新一帅帅 2024-10-05 07:07:14

是的,你应该这样做。
ASLR 仅影响“系统”DLL,因此您正在编写的 DLL 不应受到 ASLR 的影响。此外,ASLR 并没有完全“随机”这些系统二进制文件的位置,它只是将它们重新排列在虚拟机映射的基本位置上。

Yes, you should do it.
ASLR only impacts "system" DLLs and therefore the ones you are writing should not be impacted by ASLR. Additionally, ASLR doesn't completely "randomize" the location of these system binaries, it simply shuffles them around in the basic spot in the vm map.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文