加载时重定位和虚拟内存支持是两个不同的概念。如今,几乎所有 CPU 和操作系统都支持虚拟内存。关于虚拟内存,唯一真正重要的一点是:忘记物理地址。现在这是硬件和操作系统的责任,除非您正在编写分页系统,否则您可以忘记物理地址。程序使用的所有地址都是虚拟地址。这是一个巨大的优势,并且极大地简化了编程模型。在 32 位系统上,这仅仅意味着每个进程都有自己的 4 GiB 内存空间,范围0x00000000
从0xffffffff
.
An.exe
代表一个过程。链接器.exe
从.obj
文件中生成。虽然两者都是二进制文件,但.obj
文件不可执行,因为它们不包含所有变量和函数的地址。链接器的工作是提供这些地址,它通过将这些.obj
文件端到端放置然后计算所有符号(函数和变量)的确切地址来确定。因此,.exe
所创建的函数和变量的每个地址都“硬编码”到其中。但是在创建之前仍然需要一个关键信息.exe
。链接器必须了解将在内存.exe
中加载的位置。是在地址0x00000000
还是在0xffff0000
, 或者别的地方?例如,在 Windows 中,所有.exe
s 总是在绝对起始地址0x00400000
. 这称为基地址。当链接器生成符号(函数和变量)的最终地址时,它会从该地址开始计算这些地址。
现在,.exe
s 很少需要在任何其他地址加载。但对于.dll
s 来说,情况并非如此。.ddl
s 与 s 相同.exe
(两者都被格式化为可移植可执行 (PE) 文件格式,它描述了内存布局,例如,文本到哪里、数据到哪里以及如何找到哪一个)。.dll
s 也有一个首选地址。这仅仅意味着链接器在计算.dll
. 如果.dll
在这个地址加载,那么我们就都设置好了。
但是,如果由于已经在该地址加载了其他一些空间而.dll
无法在该地址加载(比如说它是0x10000000
) ,那么加载器将在内存中找到一些其他空间并加载那里。但是,现在函数和符号的全局地址不正确。因此,加载程序必须进行重定位(也称为“修复”),其中它调整所有全局符号和函数的地址以反映它们的实际地址。.dll
.dll
.dll
为了进行这种调整,加载程序需要能够在.dll
. PE 文件有一个.reloc
部分包含所有此类符号的内部偏移量。
当然,还有其他细节,例如,关于在编译器生成代码时如何使用间接调用,以便调用是间接调用,而不是直接调用,并且可以通过.exe
.
最后,要点是:当代码未在预期的位置(在 4 GiB 地址空间内)加载时,您需要重定位(某种)来调整调用和跳转中的地址以及变量访问指令加载。当操作系统加载 a.exe
时,它必须在这个 4 GiB 地址空间中选择一个合适的位置,它将代码和数据块从这里复制到.exe
磁盘上。