Winapi 不在偏移量0处映射Win32可移植可执行映像的可能原因是什么?

Winapi 不在偏移量0处映射Win32可移植可执行映像的可能原因是什么?,winapi,mapping,executable,portability,offset,Winapi,Mapping,Executable,Portability,Offset,我最近一直在研究Window的PE格式,我注意到在大多数示例中, 人们倾向于将可选标题中的ImageBase偏移值设置为不合理的高值,如0x400000 什么会使映射偏移量为0x0的图像变得不利于呢?如果将其映射到地址0,则意味着代码预期从地址0开始运行 对于操作系统,地址0为空,这是一个无效的地址。 (不是“基本上”,但对于现代操作系统来说,却是如此。) 此外,出于多种原因,通常您不希望内存的较低16 MiB中有任何内容(甚至是虚拟的) 但还有什么选择呢?它必须映射到某个地方,所以他们选择了0

我最近一直在研究Window的PE格式,我注意到在大多数示例中, 人们倾向于将
可选标题中的
ImageBase
偏移值设置为不合理的高值,如
0x400000


什么会使映射偏移量为0x0的图像变得不利于呢?

如果将其映射到地址0,则意味着代码预期从地址0开始运行

对于操作系统,地址0为空,这是一个无效的地址。
(不是“基本上”,但对于现代操作系统来说,却是如此。)

此外,出于多种原因,通常您不希望内存的较低16 MiB中有任何内容(甚至是虚拟的)


但还有什么选择呢?它必须映射到某个地方,所以他们选择了
0x400000
。。。可能没有什么特别的理由要那个特别的地址。这可能很方便。

Microsoft选择该地址作为链接器指定的默认起始地址,PE文件将在该地址进行内存映射。链接器采用此地址,并可以使用该地址优化可执行文件。当文件在该地址进行内存映射时,代码可以运行,而无需修改任何内部偏移量

如果由于某种原因无法将文件加载到该位置(已在该位置加载另一个exe/dll),则需要在可执行文件运行之前进行重新定位,这将增加加载时间

较低的内存地址通常被认为包含较低级别的系统例程,并且通常被单独保留。ImageBase地址的唯一实际要求是0x10000的倍数

建议阅读:


首先,这不是Windows或PE文件格式的默认设置,而是链接器的/BASE选项在链接EXE时的默认设置。DLL的默认值为0x10000000

选择/BASE:0将是错误的选择,任何程序都不能在该基址上运行。地址空间的前64 KB是保留的,永远无法映射。主要用于捕获空指针取消引用错误。并扩展到64KB以捕获程序中的指针错误,这些程序从16位开始运行,然后重新编译到32位

为什么默认设置为0x40000而不是0x10000也是一个历史事故,至少可以追溯到Windows 95。它为“16位/MS-DOS兼容性竞技场”保留了前4兆字节的地址空间。我不太记得了,Windows9x有一个与NT非常不同的16位虚拟机实现。你可以在这本古书中读到更多关于它的内容。现在,这当然不再相关了,64位操作系统将很容易在0x010000和0x400000之间分配堆内存


更改EXE的/BASE选项没有任何意义。然而,为DLL更改它有很多意义。如果它们不重叠,因此不必重新定位,它们将更加有效,不会占用分页文件中的任何空间,并且可以在进程之间共享。甚至还有一个SDK工具,您可以在构建后对其进行更改,rebase.exe

实际上,将/BASE设置为0的影响取决于图像的设置(链接器也会设置该设置)

如果映像具有/BASE:0且ASLR处于启用状态(/DYNAMICBASE:YES),则映像将启动并运行,因为加载程序将自动在“有效”地址加载它


如果您的映像具有/BASE:0且ASLR处于关闭状态(/DYNAMICBASE:NO),那么您的映像将不会启动,因为加载程序不会将其加载到所需的基于地址(如上所述,该地址是未验证/保留的)。

提示:x86上单个PTE条目映射的地址空间量是多少?@RaymondChen 2(技术上为4)GB,其中
0x400000
为1/512。这是如何回答这个问题的呢?一个PDE条目(对不起,我指的是PDE而不是PTE)映射0x400000字节(4MB)。汉斯·帕桑的回答填补了其余部分。