当联结器产生一个 EXE 档,它假设这个文件会被加载内存的某处,并且把code 和 data
的相关假设地址都写入 EXE 文件中。如果可执行档最终被加载到虚拟地址空间的另一
个地址,没有按预期来,联结器所登记的那个地址就是错误的。储存在 .reloc 的信息就
是用来帮助 PE 加载器修正加载模块的地址。如果加载器能够把模块加载到预定地址
上,.reloc 就可以弃而不用。.reloc 中的资料项被称为「基底重定位(base relocations )
资料项」,原因是它们的用途视被加载模块的基地址而定。
和NE 格式的重定位方式不同,PE 档的作法十分简单。它们并不参考到外部DLLs 或模
组中的其它sections ,而是把image (译注)中所有可能需要修改的地址串成一个串行。
译注:文件中的模块资料被加载内存后,我们称其为模块的 "image" 。我保留这个原 文,不译它。
下面是一个例子。假设有一个可执行文件,基地址是 0x400000 。在这个 image 偏移位
置 0x2134 处是一个指针,指向一个字符串。字符串始于实际地址 0x404002 处,所以指针
内容应该是 0x404002 。你可以把文件加载,但是加载器决定把它映像到实际地址
网管u家u.bitscn@com
0x600000 处。联结器假设的基地址和实际加载的起始地址之间的差额称为 delta 。此
例之 delta 为 0x20000 。整个 image 的位置提高了 0x20000 ,其中的字符串当然也是(现
在应该是 0x604002 )。所以指向字符串的指针就错误了,delta 应该加到指针值中。
为了让 Windows 加载器有能力做这样的调整,可执行档内含许多个「基底重定位资料
项」,给那些存放指针的位置(本例为 0x2134 )使用。加载器必须把 delta 加到各个位
址上。本例之中加载器应该把 0x20000 加给原来的指针值(0x404002 ),并将结果0x604002
写回原处。图8-12 显示这个过程。
图8-12 PE 文件的基底重定位动作。
「基底重定位资料项」的格式有点儿奇怪。它们被包装为一系列连续区段,长短不一。
每一个区段描述 image 中的一个 4K page 的重定位信息。它们以一个
IMAGE_BASE_RELOCATION 结构做为开始,格式如下:
网管下载dl.bitscn.com
DWORD VirtualAddress
此一字段内含这些个「基底重定位资料项」的起始 RVA 值。每一个「基底重定位资料
项」的偏移位置必须加上此值才能够构成一个真正的 RVA ,指向「基底重定位资料项」。
DWORD SizeOfBlock
结构大小,再加上所有跟随在后的「基底重定位资料项」(都是 WORDs )。为了决定
区块中的「基底重定位资料项」的个数,先把此值减去IMAGE_BASE_RELOCATION 结
构大小(8 个字节),再除以 2 (WORD 大小)。如果此字段为 44 ,就表示有 18 个
「基底重定位资料项」:
( 44 - sizeof(IMAGE_BASE_RELOCATION) ) / sizeof(WORD) = 18
WORD TypeOffset
这并不是单独一个 WORD ,事实上它是一个 WORDs 数组。数组元素个数可由上一个
式子计算获得。每一个 WORD 的最底部 12 个位代表「基底重定位资料项」偏移位
置,但必须再加上「基底重定位资料项」区块表头的 VirtualAddress 字段值。最高的4
个位是「基底重定位资料项」的型态。对于在 Intel CPUs 中执行的 PE 档,你将看到
两种型态:
中国网管联盟bitsCN.com
·0 (IMAGE_REL_BASED_ABSOLUTE ):此一「基底重定位资料项」无意义,
只是用来充数而已,使所有「基底重定位资料项」的总共大小成为 DWORD 的
倍数。
·3 (IMAGE_REL_BASED_HIGHLOW ):把delta 值加到欲计算的RVA 值去。
另外还有其它型态,在 WINNT.H 中定义。它们大部份是给 i386 以外的CPU 使用。
图8-13 描绘出一些「基底重定位资料项」,这是 PEDUMP 的输出结果。请注意图中
的RVA 值已经被 IMAGE_BASE_RELOCATION 结构中的 VirtualAddress 字段校正过
了。
Virtual Address: 00001000 Size: 0000012C
00001032 HIGHLOW
0000106D HIGHLOW
000010AF HIGHLOW
000010C5 HIGHLOW
// Rest of chunk omitted...
Virtual Address: 00002000 Size: 0000009C
000020A6 HIGHLOW
00002110 HIGHLOW
00002136 HIGHLOW
00002156 HIGHLOW
// Rest of chunk omitted...
Virtual Address: 00003000 Size: 00000114
中国网管论坛bbs.bitsCN.com 0000300A HIGHLOW
0000301E HIGHLOW
0000303B HIGHLOW
0000306A HIGHLOW
// Rest of chunk omitted...