自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

深入了解.NET編譯器中CLR加載過(guò)程

開(kāi)發(fā) 后端
.NET編譯器在生成托管代碼時(shí)會(huì)將一些重要信息寫(xiě)入PE文件的header和.text section(后邊我會(huì)介紹這些寫(xiě)入程序集的重要信息是什么),本文介紹CLR加載過(guò)程中,當(dāng)我們雙擊一個(gè)托管代碼寫(xiě)的exe程序時(shí)發(fā)生的事情。

以下說(shuō)明CLR加載過(guò)程所使用的工具是VS2005+sos.dll,示例程序代碼如下:    using System;

  1. using System.Collections.Generic;  
  2. using System.Text;  
  3.  
  4. namespace hello  
  5. {  
  6.     class Program  
  7.     {  
  8.         static void Main(string[] args)  
  9.         {  
  10.             Int32 a = 1;  
  11.             Int32 b = 2;  
  12.             b = a + b;  
  13.             Console.WriteLine(b);  
  14.  
  15.             Console.ReadKey();  
  16.         }  
  17.     }  

那么CLR加載過(guò)程是怎樣的呢?

1、當(dāng)你雙擊一個(gè).exe文件時(shí),Windows操作系統(tǒng)提供的PE Loader會(huì)將該exe文件載入內(nèi)存;

(1)、首先明確一點(diǎn),PE Loader問(wèn)什么能加載exe文件呢?因?yàn)?/SPAN>exe文件就是一種PE文件,PE(Portable Execute)文件是微軟Windows操作系統(tǒng)上的程序文件,EXE、DLL、OCX、SYS、COM都是PE文件。

(2)、有必要了解一下PE文件的結(jié)構(gòu):

Dos stub

圖 1

1) Dos stub

由100個(gè)左右的字節(jié)所組成,用來(lái)輸出類似“這個(gè)程序不能在DOS下運(yùn)行!”這樣的錯(cuò)誤信息;

2) PE Signature

DWORD類型,PE文件簽名,用來(lái)表示這是個(gè)PE文件,用ASCII碼表示;

3) File Header

包含PE文件最基本信息,通過(guò)dumpbin可以看到,如圖2所示 從這里可以看到:CPU類型為14c,是Intel I386、I486或者I586;section的數(shù)量為2;鏈接器產(chǎn)生這個(gè)文件的日期;COFF符號(hào)表的文件偏移量,為0;COFF符號(hào)表的符號(hào)數(shù)目,為0;Optional Header的大小。

CLR加載過(guò)程命令提示
圖2

4) Optional Header

用來(lái)存儲(chǔ)除了基本信息以外的其他重要信息,具體含義大家可以查閱PE文件格式的相關(guān)資料,我這里對(duì)一些關(guān)心的域根據(jù)圖3進(jìn)行一下說(shuō)明:

-- entry point,指明這個(gè)PE文件的入口地址,是一個(gè)RVA(相對(duì)虛擬地址); -- base of code,代碼塊起始地址的RVA,在內(nèi)存中,代碼塊通常在PE首部之后,數(shù)據(jù)塊之前;

 -- base of data,數(shù)據(jù)塊;

 -- image base,PE文件被鏈接器重定位后的內(nèi)存地址,可以是鏈接器優(yōu)化,節(jié)省載入時(shí)間和空間;

 -- subsystem,可執(zhí)行文件的用戶界面使用的子系統(tǒng)類型。具體值的含義為:

1 不需要子系統(tǒng)(比如設(shè)備驅(qū)動(dòng))  

2 在Windows圖形用戶界面子系統(tǒng)下運(yùn)行

3 在Windows字符子系統(tǒng)下運(yùn)行(控制臺(tái)程序)

5 在OS/2字符子系統(tǒng)下運(yùn)行(僅對(duì)OS/2 1.x)

7 在 Posix 字符子系統(tǒng)下運(yùn)行

所以可以看到我們的程序是一個(gè)控制臺(tái)程序。

       -- 最后定義了一些數(shù)據(jù)目錄,具體內(nèi)容不再贅述。

控制臺(tái)程序

                                                                     圖 3

5)  section header

Section header可以有一個(gè)或多個(gè),見(jiàn)圖4、圖5、圖6。

-- name,表示這個(gè)section的名字,例如這個(gè)section的名字為.text;

-- virtual address,保存section中數(shù)據(jù)被載入內(nèi)存后的RVA;

-- file pointer to raw data,從文件開(kāi)頭到section中數(shù)據(jù)的偏移量。

數(shù)據(jù)的偏移量
                                                                     圖 4
                                                                        
             -- Section 的原始數(shù)據(jù)
原始數(shù)據(jù)
                                                                     圖 5
CLR加載過(guò)程

圖 6
 -- CLR頭,從圖7可以找到隨托管代碼IL同時(shí)生成的元數(shù)據(jù)表的RVA。

CLR頭

圖 7

2、PE loader通過(guò)查找CLR頭發(fā)現(xiàn)該目錄不為空,則自動(dòng)將mscoree.dll載入進(jìn)程地址空間中,mscoree.dll一定是唯一的,且總是處于系統(tǒng)目錄的system32下,例如我的機(jī)器為C:\WINDOWS\system32目錄下。.net 2.0的mscoree.dll的大小只有256k左右,這個(gè)dll被叫做shim,它的作用是連接PE文件和CLR之間的一個(gè)橋梁。

3、PE loader接著會(huì)找到entry point,例如本例中圖3所示,這個(gè)PE文件的入口點(diǎn)地址為0040251E,然后通過(guò)這個(gè)地址來(lái)查找.text section的原始數(shù)據(jù)表,由圖6所示,0040251E這個(gè)地址開(kāi)始的6個(gè)字節(jié)的內(nèi)容為【FF 25 00 20 40 00】,這個(gè)內(nèi)容就是由編譯器寫(xiě)入PE文件的.text section的重要信息,FF在x86匯編語(yǔ)言與機(jī)器碼對(duì)照表中代表無(wú)條件轉(zhuǎn)移指令Jmp,這條指令的作用是無(wú)條件跳轉(zhuǎn)到00402000地址處,從圖3可以看到image base 是00400000,2000是import address table的RVA地址,由圖7可以看到,此時(shí)程序會(huì)跳轉(zhuǎn)到00402000這個(gè)地址所引用的mscoree.dll的_CorExeMain(_CorExeMain為mscoree.dll的入口方法)方法,所有的托管應(yīng)用都會(huì)通過(guò)上述過(guò)程找到并執(zhí)行_CorExeMain方法;

4、_CorExeMain方法會(huì)幫助程序找到并載入適當(dāng)?shù)?/SPAN>CLR版本,在.net 2.0以后實(shí)現(xiàn)CLR的程序集為mscorwks.dllmscorsvr.dll,例如,在我的機(jī)器上mscorwks.dll的位置是:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\;

5、啟動(dòng)CLR服務(wù),開(kāi)始初始化工作,這個(gè)初始化工作包括:

-- 分配一塊內(nèi)存空間,建立托管堆及其它必要的堆,由GC監(jiān)控整個(gè)托管堆

-- 創(chuàng)建線程池

-- 創(chuàng)建應(yīng)用程序域(AppDomain):利用sos.dll可以查看CLR創(chuàng)建了哪些AppDomain。

用VS2005打開(kāi)我們的程序,即時(shí)窗口中敲入:.load sos.dll。

在VS2005的即時(shí)窗口中敲入:后的結(jié)果),但是依然可以說(shuō)明問(wèn)題:

敲入后的結(jié)果

圖 8

由圖8可見(jiàn),CLR創(chuàng)建了System Domain、Shared Domain和Domain1,這個(gè)Domain1是默認(rèn)Appdomain。

6、接下來(lái)就會(huì)向默認(rèn)AppDomain中載入mscorlib.dll,由圖八可見(jiàn),任何托管代碼,CLR在創(chuàng)建好默認(rèn)AppDomain后,第一個(gè)載入的組件一定是mscorlib.dll,實(shí)際上這個(gè)組件定義了System.Object、所有基元類型:如System.Int32等,利用sos.dll可以看到有哪些類被載入,依據(jù)Domain 1里的Module地址,在即時(shí)窗口敲入命令!dumpmodule -mt 790c2000,結(jié)果如下,比較長(zhǎng),我只列出部分:

部分命令

圖 9

#p#

從圖9可以看到System.Object被第一個(gè)加載進(jìn)來(lái),接著是System.ICloneable、System.IEnumerable、System.Collection.ICollection、System.Collection.IList、System.Array……

7、產(chǎn)生主線程后可能會(huì)觸發(fā)一些mscorlib.dll里的類型并加載入內(nèi)存,接著,當(dāng)你的PE文件:hello.exe被載入后,默認(rèn)Appdomain的名字被改為你的PE文件的名字,載入過(guò)程完成后的結(jié)果可見(jiàn)圖8。

8、包含在mscorwks.dll中的_CorExeMain2方法接管主線程,它將調(diào)用System Domain中的SystemDomain::ExecuteMainMethod方法,然后由此方法調(diào)用類型加載器的ClassLoader::LoadTypeHandleFromToken方法,該方法會(huì)讀取程序集中的元數(shù)據(jù)表,并在里面查找包含.entrypoint的類型,并返回由EECLASS結(jié)構(gòu)表示的該類型的實(shí)例,EECLASS結(jié)構(gòu)中包含重要信息有:指向當(dāng)前類型父類的指針、指向方法表的指針、實(shí)例字段和靜態(tài)字段等。

 (1)、在即時(shí)窗口敲入命令0097c

圖 10

從圖10可以看到在當(dāng)前模塊中所定義的類型:hello.Program和所引用的類型:System.Object和System.Console。

(2)、在即時(shí)窗口敲入命令后,有如下結(jié)果:

結(jié)果
圖 11

由圖11可以得到如下信息:為hello.Program類型分配的EECLASS在內(nèi)存中的地址為00971260,通過(guò)這個(gè)地址查看其信息,發(fā)現(xiàn)hello.Program的父類地址為: 790f8a18,在即時(shí)窗口敲入命令790f8a

圖 12

方法表Method Table的地址為00972ff8。

 (3)、方法表里存的是什么呢?其實(shí)是當(dāng)前類型中所有定義和引用到的方法的入口點(diǎn),這個(gè)入口點(diǎn)被叫做Method descriptors,從圖11可以看到。

 (4)、實(shí)際上Method descriptors被分為兩個(gè)部分,第一部分是m_CodeOrIL,在當(dāng)前方法沒(méi)有被JIT的時(shí)候,m_CodeOrIL存的是這個(gè)方法的MSIL的RVA,也就是從這個(gè)RVA可以找到當(dāng)前方法的MSIL代碼;第二部分是對(duì)JIT編譯器的一個(gè)Stub(存根),當(dāng)方法是第一次被調(diào)用的時(shí)候,CLR會(huì)通過(guò)這個(gè)Stub調(diào)用mscorjit.dll組件,通過(guò)m_CodeOrIL里存儲(chǔ)的RVA,找到這個(gè)方法對(duì)應(yīng)的MSIL代碼,然后將其編譯為本地CPU指令,假設(shè)這里存到地址RVA1,最后將m_CodeOrIL和Stub的值都修改為RVA1,那么當(dāng)這個(gè)方法第二次被調(diào)用的時(shí)候?qū)?huì)直接通過(guò)RVA1去尋找本地代碼,換句話說(shuō)只有當(dāng)方法第一次被調(diào)用的時(shí)候才會(huì)被Jit編譯器編譯,之后則直接使用編譯好的本地代碼。同時(shí)這也說(shuō)明托管代碼被編譯了兩次,第一次編譯是將托管代碼編譯為MSIL代碼,并同時(shí)生成Metadata元數(shù)據(jù)文件,第二次編譯發(fā)生在方法被調(diào)用時(shí)由Jit編譯器完成。

(5)、在即時(shí)窗口敲入命令!dumpmd 00972fe8和!dumpmd 00972f0可以看到已經(jīng)被Jit過(guò)的和還沒(méi)有被Jit的方法的信息:

圖 13

被Jit過(guò)得方法則會(huì)修改m_CodeOrIL,如Main方法的m_CodeOrIL被指向地址00e50070,而沒(méi)有被Jit的方法m_CodeOrIL的值為ffffffffffffffff。

 (6)、在即時(shí)窗口敲入命令

圖 14

圖14列出helloProgram.Main方法的本地代碼。而如果在即時(shí)窗口敲入命令!u ffffffffffffffff則顯示Unmanaged code。

9、進(jìn)入Main方法,進(jìn)而執(zhí)行后續(xù)程序。

最后,從上述分析也可以看出,.NET的幾個(gè)核心組件的被調(diào)用順序大致是: mscoree.dll -----> mscorwks.dll(mscorsvr.dll)  -----> mscorlib.dll ----->mscorjit.dll。
    一般來(lái)說(shuō)調(diào)試.NET程序使用VS2005就可以了,但是要想得到更詳細(xì)的信息,如內(nèi)存情況等就需要借助其他工具了,個(gè)人覺(jué)得sos.dll和Windbg是很好的工具,Windbg可以在http://www.microsoft.com/whdc/devtools/debugging/default.mspx下載,而如果你裝的是VS2005 Team Version,那么自帶sos.dll。

關(guān)于CLR加載過(guò)程的詳細(xì)內(nèi)容,大家可以通過(guò)微軟的 Shared Source Common Language Infrastructure(SSCLI),來(lái)了解關(guān)于CLR的一些內(nèi)部機(jī)理,大家可以到

http://www.microsoft.com/downloads/details.aspx?FamilyId=8C09FD61-3F26-4555-AE17-3121B4F51D4D&displaylang=en下載,相信會(huì)對(duì)理解CLR有所幫助,另外就是由蔡學(xué)鏞寫(xiě)的http://www.microsoft.com/taiwan/msdn/columns/DoNet/loader.htm,文章挺早,但很經(jīng)典,大家可以看看

本文來(lái)自Leo Zhang博客園文章《深入了解CLR的加載過(guò)程

【編輯推薦】

  1. 淺談CLR 4.0安全模型的運(yùn)作機(jī)制
  2. 淺談CLR線程池的缺點(diǎn)及解決方法
  3. CLR線程池的作用與原理淺析
  4. Mircosoft CLR調(diào)試器的簡(jiǎn)單介紹
  5. 微軟MVP教你如何看懂.NET CLR基本術(shù)語(yǔ)
責(zé)任編輯:彭凡 來(lái)源: 博客園
相關(guān)推薦

2024-07-18 08:46:58

.NET輕量級(jí)計(jì)時(shí)器測(cè)量代碼塊

2011-02-22 18:09:08

Konqueror

2009-10-23 09:36:25

.Net Compac

2009-11-03 13:33:39

VB.NET對(duì)象列表

2019-11-29 16:21:22

Spring框架集成

2017-01-20 08:30:19

JavaScriptfor循環(huán)

2010-11-19 16:22:14

Oracle事務(wù)

2010-07-13 09:36:25

2010-06-23 20:31:54

2009-08-25 16:27:10

Mscomm控件

2022-08-26 13:48:40

EPUBLinux

2020-09-21 09:53:04

FlexCSS開(kāi)發(fā)

2020-07-20 06:35:55

BashLinux

2024-04-07 00:00:00

.NETILSpy操作指南

2009-09-03 14:26:17

Jit編譯

2009-08-04 15:52:58

ASP.NET編譯器

2023-06-06 15:31:13

JavaScript開(kāi)發(fā)

2010-10-25 14:09:01

Oracle觸發(fā)器

2022-06-03 10:09:32

威脅檢測(cè)軟件

2010-11-15 11:40:44

Oracle表空間
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)