4:对于每个需要重定位的模块,它会解析模块的重定位段,并修改模块在磁盘文件中的代码。
5:将每个模块新的首选基地址写入各个模块磁盘文件中。
所以推荐你在构建完所有项目后运行Rebase.exe。
由于Microsoft在发布windows之前,已经使用Rebase.exe对所有操作系统提供的模块进行了重定位,所以即使将操作系统的所有模块都映射到进程地址空间也不会发生交叉的现象。我们不再需要对随操作系统一起发布的模块进行基址重定位。
使用Rebase.exe或是手工修改各模块的首选基地址,以免某些模块不被加载到首选基地址上。这当然可以提高系统性能。但是我们还可以更显著的提高性能。
这种技术就是模块绑定。
让我们来回顾一下程序加载模块的过程:
加载程序首先为进程创建虚拟地址空间,接着把可执行文件映射进来。之后打开可执行文件的导入段,将该程序需要的DLL进行定位并把它们也载入进来。随着加载程序将DLL模块加载到进程地址空间,它会同时检查每个DLL的导入段。如果一个DLL有导入段,那么加载程序会继续将所需的额外的DLL模块映射到进程地址空间。
当所有DLL都已被载入进程地址空间,它开始修复所有对导入符号的引用。这时它会再次查看每个模块的导入段,对导入段中每个符号,它会检查对应DLL的导出段,然后取出该符号的RVA并给他加上DLL模块被载入到进程地址空间的虚拟地址。计算出符号在进程的虚拟地址(VA),写入到可执行模块的导入段中。这样当一个符号被引用的时候,程序会查看可执行模块的导入段并取得导入符号虚拟地址(VA)。
这就是模块加载的大概过程。可以看到,每次在程序加载时,都要将导入段的符号地址从其他DLL中获得,然后写入到导入段的相应位置(IAT)。这十分耗费时间。
所谓的模块绑定就是说在运行之前,所有导入符号在进程地址空间的地址已经获得,不需要加载时在计算出来这节省初始化时间,另外将导入符号的虚拟地址写入exe模块的导入段,也会由于写时复制机制将要修改的页面以系统页交换文件为后备存储器。这会遇到与基址重定位相似的问题。所以模块绑定对提高系统性能的提高是显著的。
Visual Studio提供了一个名为Bind.exe的工具,如果在执行它的时候传给它一个映像文件名,它会对其执行模块绑定操作。
具体过程为:
1:它会打开模块的导入段。
2:对导入段列出的每个DLL,它会检查该DLL文件的文件头,来确定该DLL的首选基地址。
3:它会在DLL的导出段查看每个符号。
4:取得符号的RAV,并将其与模块的首选基地址相加。得到导入符号的虚拟地址(VA)。
5:在映像文件的导入段中添加额外信息。这些信息包括映像文件被绑定的各DLL的名称,以及各模块的时间戳。
在整个过程中Bind.exe做了两个重要假设:
1:进程初始化时所需的DLL都被映射到了它们的首选基地址。
2:绑定完成之后,DLL导出段所列出的符号的位置没有发生改变。这可以通过检查每个DLL的时间戳来保证。
如果上述假设有一个不成立。加载程序必就向绑定之前一样,手动修正可执行文件导入段。如果都成立加载程序就可以不用做这些工作了。
如有纰漏请不吝赐教。谢谢。
相关链接: