深入了解CLR的加载过程
我们知道,.net编译器在生成托管代码时会将一些重要信息写入PE文件的header和.text section(后边我会介绍这些写入程序集的重要信息是什么),本文介绍当我们双击一个托管代码写的exe程序时发生的事情。
以下说明所使用的工具是VS2005+sos.dll,示例程序代码如下:
usingSystem.Collections.Generic;
usingSystem.Text;
namespacehello
{
classProgram
{
staticvoidMain(string[] args)
{
Int32 a=1;
Int32 b=2;
b=a+b;
Console.WriteLine(b);
Console.ReadKey();
}
}
}
那么CLR是如何被加载的呢?
1、当你双击一个.exe文件时,Windows操作系统提供的PE Loader会将该exe文件载入内存;
(1)、首先明确一点,PE Loader问什么能加载exe文件呢?因为exe文件就是一种PE文件,PE(Portable Execute)文件是微软Windows操作系统上的程序文件,
EXE、DLL、OCX、SYS文件以及COM组件都是PE文件。
(2)、有必要了解一下PE文件的结构:
图 1
1) Dos stub
由100个左右的字节所组成,用来输出类似“这个程序不能在DOS下运行!”这样的错误信息;
2) PE Signature
DWORD类型,PE文件签名,用来表示这是个PE文件,用ASCII码表示;
3) File Header
包含PE文件最基本信息,通过dumpbin可以看到,如图2所示从这里可以看到:CPU类型为14c,是Intel I386、I486或者I586;section的数量为2;链接器产生这个文件的日期;COFF符号表的文件偏移量,为0;COFF符号表的符号数目,为0;Optional Header的大小。
4) Optional Header
用来存储除了基本信息以外的其他重要信息,具体含义大家可以查阅PE文件格式的相关资料,我这里对一些关心的域根据图3进行一下说明:
-- entry point,指明这个PE文件的入口地址,是一个RVA(相对虚拟地址);
-- base of code,代码块起始地址的RVA,在内存中,代码块通常在PE首部之后,数据块之前;
-- base of data,数据块;
-- image base,PE文件被链接器重定位后的内存地址,可以是链接器优化,节省载入时间和空间;
-- subsystem,可执行文件的用户界面使用的子系统类型。具体值的含义为:
1不需要子系统(比如设备驱动)
2在Windows图形用户界面子系统下运行
3在Windows字符子系统下运行(控制台程序)
5在OS/2字符子系统下运行(仅对OS/2 1.x)
7在Posix字符子系统下运行
所以可以看到我们的程序是一个控制台程序。
--最后定义了一些数据目录,具体内容不再赘述。
图 3
5)section header
Section header可以有一个或多个,见图4、图5、图6。
-- name,表示这个section的名字,例如这个section的名字为.text;
-- virtual address,保存section中数据被载入内存后的RVA;
-- file pointer to raw data,从文件开头到section中数据的偏移量。
图 4
-- Section的原始数据
图 5