三、加载CLR
主要分为托管EXE加载CLR和托管DLL加载CLR。
在Windows XP和Windows .NET服务器家族出现之前:
EXE文件加载CLR的过程:
托管EXE文件被调用时,Windows先加载该文件,发现其.idata部分记录了要把MSCorEE.dll(微软组件对象运行时执行引擎)加载到进程的地址空间(编译器在生成托管EXE文件时,x86 stub函数JMP _CorExeMain被嵌入到了PE文件的.text部分,而这个函数是从MSCorEE.dll中导入的,所以.idata部分会记录MSCorEE.dll).加载了MSCorEE.dll后,加载器获得_CorExeMain函数的地址,同时修正托管EXE中的stub函数的JMP指令。进程的主线程开始执行修正后的x86 stub函数,该函数会跳转到_CorExeMain函数上,_CorExeMain函数开始初始化CLR,并查看可执行程序集的CLR表头以确定要执行的托管入口点方法。随后IL代码被翻译成本地CPU指令。CLR跳转到编译后的本地CPU指令上,至此,托管应用程序开始真正运行。
托管DLL加载CLR的过程类似,分为非托管代码加载托管DLL和托管代码加载托管DLL。
非托管代码加载托管DLL:
1、Windows加载托管DLL,并自动加载MSCorEE.dll,获取MSCorEE.dll中_CorDllMain函数的地址,并修正托管DLL中x86 stub函数的JMP指令。
2、调用LoadLibrary加载托管DLL的线程将跳到x86 stub函数上来,该函数会跳转到MSCorEE.dll中的_CorDllMain函数上。_CorDllMain函数开始初始化CLR,然后立即返回,应用程序也返回到正常状态继续执行。
注:托管DLL中方法的IL代码只会在非托管代码明确调用时开始以即时编译的方法执行。
托管代码加载托管DLL:
调用代码会检查托管DLL中的元数据,然后以即时编译的方式执行其内方法的IL代码。
Windows XP和Windows .NET服务器家族上,托管程序被调用时:
1、操作系统的加载器会查看PE表头的第14条目录项来检测该文件中是否包含托管代码。
2、如果目录存在且不为0,加载器将忽略该文件的导入部分.idata,而自动将
MSCorEE.dll加载到进程地址空间内
3、加载完成后,操作系统的加载线程将直接跳转到MSCorEE.dll中相应的函数上。