深入了解CLR的加载过程

我们知道,.net编译器在生成托管代码时会将一些重要信息写入PE文件的header.text section(后边我会介绍这些写入程序集的重要信息是什么),本文介绍当我们双击一个托管代码写的exe程序时发生的事情。

以下说明所使用的工具是VS2005+sos.dll,示例程序代码如下:

usingSystem;
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操作系统上的程序文件
EXEDLLOCXSYS文件以及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 I386I486或者I586section的数量为2;链接器产生这个文件的日期;COFF符号表的文件偏移量,为0COFF符号表的符号数目,为0Optional Header的大小。

图2

4) Optional Header

用来存储除了基本信息以外的其他重要信息,具体含义大家可以查阅PE文件格式的相关资料,我这里对一些关心的域根据图3进行一下说明:

-- entry point,指明这个PE文件的入口地址,是一个RVA(相对虚拟地址)

-- base of code,代码块起始地址的RVA,在内存中,代码块通常在PE首部之后,数据块之前;

-- base of data,数据块;

-- image basePE文件被链接器重定位后的内存地址,可以是链接器优化,节省载入时间和空间;

-- subsystem,可执行文件的用户界面使用的子系统类型。具体值的含义为:

1不需要子系统(比如设备驱动)

2Windows图形用户界面子系统下运行

3Windows字符子系统下运行(控制台程序)

5OS/2字符子系统下运行(仅对OS/2 1.x

7Posix字符子系统下运行

所以可以看到我们的程序是一个控制台程序。

--最后定义了一些数据目录,具体内容不再赘述。

图 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