1 windows dll x86 winapi virtual-address-space
假设两个进程正在使用 Kernel32.dll,Windows 是否将 DLL 映射到两个进程中的同一虚拟地址空间?如果不是,分页机制最终如何使用实际上为两个进程加载 DLL 的相同物理地址?我尝试在 Windows 内部手册中查找此信息,但没有找到任何内容
TL;DR:不,它可能被加载到另一个进程的其他地方。
Ntdll 和 Kernel32 很特殊,并且总是在同一地址加载,因此最好关注其他内容,例如 Shell32。
DLL 具有所谓的首选基地址,该地址存储在 PE 标头 ( ImageBase) 中。加载程序将首先尝试加载该地址处的 dll。如果该地址范围是空闲的,则加载将成功,不需要额外的工作。
如果地址不空闲,则加载器必须将其加载到其他地方。在不同的地址加载通常需要重定位信息,如果在链接()期间删除了重定位信息/FIXED,则加载将失败!如果其他地方有空间来加载 dll,加载器将使用重定位信息来用新的基地址修补 dll 中的给定位置。由于 dll 是以写时复制的方式加载的,因此与在首选地址加载相比,这会导致额外的内存使用,因为需要修补程序的每个内存页现在都是进程中的私有副本。这意味着您的问题的答案是否定的,如果该进程已经加载了其他内容,则 dll 可能不会加载到不同进程中的同一地址。
到目前为止我只讨论了装载机。加载程序在 Ntdll 中作为普通用户模式代码实现,不涉及映射到内存的文件实际工作方式。内存映射文件(NT 内部称为“段”)是操作系统内核和 CPU 硬件之间的协作。这本身就是一个完整的主题,但需要了解的重要一点是,物理内存和页面/交换文件机制与用户模式进程访问其虚拟内存页面的方式完全无关。内核可以将物理内存页映射到进程虚拟内存中的零个、一个或多个位置,并且当进程访问虚拟页时,CPU 将自动进行转换。
最后一点,ASLR 确实使事情变得有点复杂,但“偏移”仅在重新启动时发生变化,并且不应该对当前实现中的这个特定问题产生影响。理论上,Windows 将来可能会改变这一点,并始终在不同进程中的不同地址加载内容,但由于写时复制的缺点,这种情况不太可能发生。