《Windows核心编程系列》谈谈基址重定位和模块绑定

上一篇 / 下一篇  2012-09-07 13:51:55 / 个人分类:杂谈

b{DP;?b0  每个DLL和可执行文件都有一个首选基地址。它表示该模块被映射到进程地址空间时最佳的内存地址。在构建可执行文件时,默认情况下链接器会将它 的首选基地址设为0x400000。对于DLL来说,链接器会将它的首选基地址设为0x10000000,然后将该地址以及代码、数据的相关地址都写入它 们的PE文件中。当它们被加载时,加载程序读取首选基地址的值,并试图把它们加载到相应位置。51Testing软件测试网9k sw7OSU4Td`0q

krqG S[&lWvM0  对于可执行文件和DLL中的代码,它们运行的时候所引用的的数据的地址,在链接的时候就已经确定。并且这些都是当exe文件或是DLL被加载到它们的首选基地址处时才是有效的。

\J \3ROn'c0

fy5W)K;T0  对于汇编代码:MOV [0x00414540] ,551Testing软件测试网J-n4A8II

51Testing软件测试网?:Y Q+FW;r0q

   它是将5赋值给0x00414540处的内存地址。此地址已经固定。它说明当EXE或是DLL被加载到0x400000处时,0x00414540才是 正确的。然而并不是所有模块都会被载入到首选基地址处。一旦模块没有被加载到首选基地址处,在该模块中对于地址的引用都是错误的,这时候基址重定位就是必 须的。

5HYUj']I'pH(I0

d-Hdj4U^d0  所谓重定位就是当某模块未被载入到首选基地址时,加载器会计算模块实际载入的地址跟首选基地址的差值,将这个差值加到机器指令所引用的原来的地址,得到的就是模块中各指令所引用的数据在本进程地址空间的正确地址,随后加载器会修正模块中对每个内存地址的引用。51Testing软件测试网zTR8[ NU2Zi

gMY%ssszn0  为了便于系统有能力对各数据的地址进行修正,windows提供了重定位段。它包含很多基址重定位信息。这个段是有很多项组成。每一项表示一个要重定位的地址,它包含一个字节偏移量列。该偏移量表示一条机器指令所使用的内存地址。这便于系统在确认该模块需要重定位时对需要重定位的数据进行定位。

E1r(\9a F0

:D y9t2Vc8F2f;Fx;L%e0  当链接器在构建模块时,它会将重定位段嵌入到生成的文件中。

J"@gk;Ix'h}-R8R051Testing软件测试网[SP NM{lQNm

  对exe文件来说,由于每个文件总是使用独立的地址空间,所以exe文件总是可以被加载到首选基地址上。对于DLL来说,很多DLL都是使用寄主进程的地址空间,不能保证各DLL的首选基地址各不相同。

:|lOa]X ]0

|D+N{ H/v'[0  所以DLL一定要有重定位段,除非在链接时使用/FIXED 开关。此时链接器会去掉重定位段。如果此后模块未被载入首选基地址,由于无法重定位,模块不会被载入,导致程序无法运行。

.yl&LgiT051Testing软件测试网$fE!l J&^"M RE4S K

  如果加载程序将模块加载到它的首选基地址,那么系统就不会访问模块的重定位段。否则系统会打开模块的重定位段并遍历其中的条目,对每个条目,加载程序先找到包含机器指令的那个存储页面,算出差值,将差值加到机器指令当前正在使用的内存地址上。

zS"VrT5b5E2Mw?051Testing软件测试网%^3_0?Eaz#Q

   对于上例:假如它是在一DLL中。如果该DLL实际被加载到0x20000000处,由于首选基地址是0x10000000,两者差为 0x10000000。加载器修正之后,该指令所引用的实际地址变成了0x10414540。这样对各个数据的引用都会引用到正确的地址 。51Testing软件测试网?8E4^`:rt@8L/cR]

51Testing软件测试网%K"MY8@.me1c

  要知道基址重定位需要很大的系统开销:

^1KvLzx:]051Testing软件测试网 O,pc ^AM

  1:加载程序需要遍历模块重定位段并修改模块中大量的代码。这增加了程序初始化时间。

7oGS$^I$g@ ^051Testing软件测试网;EEL;vH)V5R6T2@

  2:当加载程序写入模块的代码页面时,由于它们具有写时复制属性,写时复制机制会导致系统从页交换文件中分配空间来容纳这些修改后的页面。51Testing软件测试网)~b#rP4T X(n(E_

_;W,J9GI:jDZ0   明白了这些我们应该清楚,为每个映射到进程的DLL设置不同的首选基地址是必须的。在链接时,选中ProjectProperties\Linker \Advancved,然后在BaseAddress中设置。这听起来很容易,但是如果程序需要载入大量的模块,我们还需要这样一个一个设置吗?话又说回 来了,有些模块并不是我们创建的,我们应该怎么办呢?

r}7tn\M&^V5W051Testing软件测试网 ty)j Ab^K

  Windows为我们提供了一个名为:Rebase.exe的工具。51Testing软件测试网:z(l]5m!F(S

51Testing软件测试网-CT-s3t5Eh

  如果在执行它时为它提供一组映像文件名,它会执行以下操作:51Testing软件测试网tD gFX

51Testing软件测试网tY9s1HDa2l

  1:它会模拟创建一个地址空间。

!P(`#z!vk(}/s051Testing软件测试网"_^e[/P

  2:它会打开这一组映像文件,并得到它们的大小和首选基地址。51Testing软件测试网C:[-|h A%F^C

H+fRW?0  3:它会在模拟的地址空间对模块重定位的过程进行模拟,以便各模块没有交叉。

U#Iz,km0e I]'y051Testing软件测试网B"` F)sFu:O

  4:对于每个需要重定位的模块,它会解析模块的重定位段,并修改模块在磁盘文件中的代码。51Testing软件测试网Ce5p(j ^R

51Testing软件测试网V}d&Z3\.r:`

  5:将每个模块新的首选基地址写入各个模块磁盘文件中。51Testing软件测试网gE*IX Lb-E

0cY+sAu+y0  所以推荐你在构建完所有项目后运行Rebase.exe。51Testing软件测试网a(j`kjJ ?!^$P

51Testing软件测试网1L-wp1cRK u-t6QZ

  由于Microsoft在发布windows之前,已经使用Rebase.exe对所有操作系统提供的模块进行了重定位,所以即使将操作系统的所有模块都映射到进程地址空间也不会发生交叉的现象。我们不再需要对随操作系统一起发布的模块进行基址重定位。

9C5D6Tk`2q051Testing软件测试网!]@ nF&W2u{p p

  使用Rebase.exe或是手工修改各模块的首选基地址,以免某些模块不被加载到首选基地址上。这当然可以提高系统性能。但是我们还可以更显著的提高性能。51Testing软件测试网&r:K \YG7u&aI5]+Y

(}Ow['@$X0  这种技术就是模块绑定。51Testing软件测试网%EooD.a`!q\

ugyq`U[cU ?p+Z0  让我们来回顾一下程序加载模块的过程:

F,XnSg2J2b051Testing软件测试网 j Y R3@/F

  加载程序首先为进程创建虚拟地址空间,接着把可执行文件映射进来。之后打开可执行文件的导入段,将该程序需要的DLL进行定位并把它们也载入进 来。随着加载程序将DLL模块加载到进程地址空间,它会同时检查每个DLL的导入段。如果一个DLL有导入段,那么加载程序会继续将所需的额外的DLL模 块映射到进程地址空间。

,_-z7f&~a"lF0

E7e{ {c[O*p:X0  当所有DLL都已被载入进程地址空间,它开始修复所有对导入符号的引用。这时它会再次查看每个模块的导入段,对导入段中每个符号,它会检查对应 DLL的导出段,然后取出该符号的RVA并给他加上DLL模块被载入到进程地址空间的虚拟地址。计算出符号在进程的虚拟地址(VA),写入到可执行模块的 导入段中。这样当一个符号被引用的时候,程序会查看可执行模块的导入段并取得导入符号虚拟地址(VA)。

!w&Z s Pvl0_051Testing软件测试网9a2Bnh)u oe

  这就是模块加载的大概过程。可以看到,每次在程序加载时,都要将导入段的符号地址从其他DLL中获得,然后写入到导入段的相应位置(IAT)。这十分耗费时间。51Testing软件测试网Xft2N m!E

51Testing软件测试网/l&O.m6xo

  所谓的模块绑定就是说在运行之前,所有导入符号在进程地址空间的地址已经获得,不需要加载时在计算出来这节省初始化时间,另外将导入符号的虚拟 地址写入exe模块的导入段,也会由于写时复制机制将要修改的页面以系统页交换文件为后备存储器。这会遇到与基址重定位相似的问题。所以模块绑定对提高系 统性能的提高是显著的。51Testing软件测试网] |mHc

?anqY%t1[0  Visual Studio提供了一个名为Bind.exe的工具,如果在执行它的时候传给它一个映像文件名,它会对其执行模块绑定操作。51Testing软件测试网Ub9e3_g.o!@:Ra

[ gq$q%k,Y ^8R0  具体过程为:51Testing软件测试网$E2e*m]:Tl'Lw

@4rh0G7y0  1:它会打开模块的导入段。51Testing软件测试网upxM/C

6g L'@mTicF3^T0U0  2:对导入段列出的每个DLL,它会检查该DLL文件的文件头,来确定该DLL的首选基地址。51Testing软件测试网r]I3Qc"N

0sI'Z,i.qzw$?0  3:它会在DLL的导出段查看每个符号。

P2e[e9r0

0B'W9Tg9Ek4MZ\0  4:取得符号的RAV,并将其与模块的首选基地址相加。得到导入符号的虚拟地址(VA)。51Testing软件测试网/^#d0yc Du

51Testing软件测试网 UZ~qZe'gs

  5:在映像文件的导入段中添加额外信息。这些信息包括映像文件被绑定的各DLL的名称,以及各模块的时间戳。51Testing软件测试网5sf*fp/HLfE

51Testing软件测试网-HC(h/TPjv0t

  在整个过程中Bind.exe做了两个重要假设:

#| fk }:y,`*?#jH? hg0

L3O/i9AL fm0  1:进程初始化时所需的DLL都被映射到了它们的首选基地址。

:Z_:G;Q[%^ N,I0

d&S5V3f cZ)B0  2:绑定完成之后,DLL导出段所列出的符号的位置没有发生改变。这可以通过检查每个DLL的时间戳来保证。

A5I2c0bvL&j051Testing软件测试网b2to%DT

  如果上述假设有一个不成立。加载程序必就向绑定之前一样,手动修正可执行文件导入段。如果都成立加载程序就可以不用做这些工作了。51Testing软件测试网_q ` aE!BC G.k

I-@6iSY x0  如有纰漏请不吝赐教。谢谢。

7P2~[0m0u7MIl0

6VV]~a6Ae_7}0相关链接:51Testing软件测试网*O1]B oA*NCmi2f#SU

#xdnw9t*}0《Windows核心编程系列》谈谈Windows钩子

{?%zF3SL051Testing软件测试网)bT#s!X+G`'F

《Windows核心编程系列》谈谈内核对象及句柄的本质51Testing软件测试网Y _Q;LX4Qna2Ud


TAG:

 

评分:0

我来说两句

Open Toolbar