CLR引擎初始化分析

在以前的一篇叫做<深入追蹤Exe加載過程>的文章裏面,從clixlaunch函數開始,再到CorExeMain2函數,在CorExeMain2裏面有一個叫做CoInitializeEE的函數:

       result = CoInitializeEE(COINITEE_DEFAULT | COINITEE_MAIN)

       一路找到EnsureEEStarted,這個是確保EE啓動的方法。在這個方法裏面,首先做一系列的狀態判斷,然後判斷ee (Execute Engine)是否已經啓動。如果沒有啓動,就啓動EE

      

if (!g_fEEStarted && !g_fEEInit && SUCCEEDED (g_EEStartupStatus))<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

{

EEStartup(flags);

bStarted=g_fEEStarted;

hr = g_EEStartupStatus;

        }

else

{

hr = g_EEStartupStatus;

if (SUCCEEDED(g_EEStartupStatus))

{

hr = S_FALSE;

}

}

 

這裏首先做了幾個狀態檢查:

if (!g_fEEStarted && !g_fEEInit && SUCCEEDED (g_EEStartupStatus))

 

需要三個條件同時滿足的時候,纔會執行EEStartup函數來啓動EE

1.         EE沒有啓動。

2.         EE沒有被正在啓動,當然,在執行這個函數之前是設置了Lock的。

3.         EEStartup在上一次啓動的時候失敗。

 

殺入EEStartup,找到最關鍵的幾行代碼:


      PAL_TRY

       {

           EEStartupHelper(fFlags);

       }

       PAL_EXCEPT_FILTER (FilterStartupException, NULL)

       {

           // The filter should have set g_EEStartupStatus to a failure HRESULT.

           _ASSERTE(FAILED(g_EEStartupStatus));

       }

PAL_ENDTRY


   這裏使用了
PAL_TRY來確保這個這個過程的acid性質。現在這篇文章的主角登場了:

EEStartupHelper(fFlags);

首先說一下這個fFlags類型,這個是在frameworkcor.h裏面的一個枚舉類型函數:


typedef enum tagCOINITEE

{

       // Default initialization mode.

COINITEE_DEFAULT        = 0x0,  

// Initialization mode for loading DLL.        

COINITEE_DLL            = 0x1,            

// Initialize prior to entering the main routine  

COINITEE_MAIN           = 0x2        

} COINITIEE;

這裏的fFlags=COINITEE_DEFAULT | COINITEE_MAIN

 

下面,來具體敘說下CLREE在啓動的時候所做的事情,就一段一段的說吧,比較長,基本上對於每句都加上了註釋:

 

       g_fEEInit = true;

       //set Ctrl event Control handler

    ::SetConsoleCtrlHandler(DbgCtrlCHandler, TRUE/*add*/);

 

    // Initialize event tracing early so we can trace CLR startup time events.

    InitializeEventTracing();

 

    ETWTraceStartup trace(ETW_TYPE_STARTUP_EESTARTUP);

    TIMELINE_AUTO(STARTUP, "EEStartup");

      

       首先置g_fEEInittrue,這個是標識EE是否在啓動的全局變量,每次只運行一個EE的啓動,每次EE啓動的時候,會lock上相關的資源並且檢查g_fEEInit的值。

       SetConsoleCtrlHandler方法設置了響應Ctrl鍵的方法。InitializeEventTracing方法調用的挺早,這個是爲了儘早啓動事件跟蹤機制,這樣就能夠跟蹤CLR在啓動的時候的相關的事件。所以在設置了對Ctrl的響應模式以後立馬就調用了這個方法。

       而對於ETWTraceStartup trace(ETW_TYPE_STARTUP_EESTARTUP),這記錄了CLR這個啓動的這個事件。ETW_TYPE_STARTUP_EESTARTUPeventtrace.h中記錄的一系列不同的事件類型。在每次運行到某個特定的事件的時候,就trace下來。Eventtrace,實現了windows操作系統的事件記錄功能。如果你打開響應的事件記錄級別的話。在eventtrace中,還定義了許多關於GCThreadPoolExceptionsMonitorStart Up EventFusion building log響應的事件。有興趣的可以去看看。最後的一行,是一個簡單的定時的手動的事件日誌記錄,這兩行放在了一起。這個主要是用於profiling的用途。

      

       接下來的第二段啓動代碼:


      
IPCFuncCallSource::DoThreadSafeCall();

 

       //GSCookies (guard-stack cookies) for detecting buffer overruns

       InitGSCookie();

       Frame::Init();

 

       // Initialize EEConfig

       if (!g_pConfig)

       {

           IfFailGo(EEConfig::Setup());

           initEEConfig = TRUE;

   }

 

       GetStartupInformation();

 

       // get any configuration information from the registry

       if (initEEConfig)

        IfFailGo(g_pConfig->SetupConfiguration());

 

 

首先說下IPCFuncCallSource是幹嘛的,他的主要用途,是爲了支持一個跨越進程的函數之間的相互調用。如果在EE啓動的時候,有一部分代碼需要被別的進程hook到,就需要在這段代碼的前後都調用這樣的一個方法來制定可以safeCall的範圍。因此,在代碼的下面也就可以再看到這樣的相同的一行。這裏調用這樣的一個方法,主要是爲了給性能監視程序,PerMon.exe一個機會來hookee的啓動內部來監視相關的活動

GSCookiesGuard-stack cookies的縮寫,啓動檢測buffer overrun的功能。

然後是Frame的初始化。這裏的初始化主要是基礎類的初始化,而EE需要的Frame主要是由JITStub Manager生成放到Stack上面的。

frame.h文件中,有一個關於Frame很清晰的架構圖,可以參考一下。

接下來,就是初始化EEConfig文件。主要是從註冊表裏面獲取相關的一些配置信息,譬如是不是該Jit啊,是不是需要檢查一下GC啊,等等。

下面的幾行,也是和讀取和配置配置信息相關的,不多說了。

 

Go on 第三波,還有很多呢,對了,聲明下,clr instense和一些調試信息,還有一些記錄到log文件裏面的信息我都沒列出來:

 

     // SString

    SString::Startup();

 

    // Fusion

    {

        ETWTraceStartup traceFusion(ETW_TYPE_STARTUP_FUSIONINIT);

        IfFailGoLog(InitializeFusion());

}

 

// A class to maintain a pool of available events.

       //static EventStore s_EventStore;

    InitEventStore();

 

    if (g_pConfig != NULL)

    {

        IfFailGoLog(g_pConfig->sync());       

    }

// Initialize all our InterProcess Communications with COM+

IfFailGoLog(InitializeIPCManager());

 

首先是SString的初始化。String地球人都知道SString是嘛玩意呢?

SStringEE中一個非常重要的東西。它的存在,主要是基於兩個目的的:首先是提供一個相對來說針對所有的API來說比較標準的string class,和一個易用的,性能相對來說的比System.String更加底層的string實現類。另外一個功能,就是把string操作裏面的所有不安全的功能都封裝了起來。

SString裏面包含的數據的編碼方式,都是用的是Unicode。這種編碼方式可以在需要的時候轉換成爲ASCIIUTF-8,或者是ANSI

接下來,就是啓動Fusionfusion中主要是包含了配件相關的綁定,策略相關檢查和全局配件緩存的實現代碼。

InitEventStore是一個實現了把現有的Event都存儲到一個pool裏面的class。在來不及都處理的時候,形成一個隊列。

InitializeIPCManager實現了對進程間通信的初始化,主要是COM+的使用。主要分配一個IPCManager的實現,然後和相關的接口hook到一起,同時調用相關的函數來激活IPC相關的模塊。

 

Then,第四段:

// Set up the cor handle map. This map is used to load assemblies in

    // memory instead of using the normal system load

    PEImage::Startup();

 

       // Implementation of the ICodeInfo interface

    EECodeInfo::Init();

    HardCodedMetaSig::Init();

    Stub::Init();

       // StubLinker with extensions for generating X86 code.

    StubLinkerCPU::Init();

 

    // weak_short, weak_long, strong; no pin

       //Wraps handle table to implement various handle types (Strong, Weak, etc.)

    Ref_Initialize();

 

    // Initialize remoting

    CRemotingServices::Initialize();

 

    // Initialize contexts

    Context::Initialize();

 

       // Initialize ThreadManager,One-time initialization. Called during Dll initialization.

    InitThreadManager();

 

       // Event to synchronize EE shutdown.

    g_pEEShutDownEvent = new CLREvent();

       // If TRUE, initial state is signalled

    g_pEEShutDownEvent->CreateManualEvent(FALSE);

 

    // Initialize RWLocks(Read Write Lock)

    CRWLock::ProcessInit();

 

    // Initialize debugger manager

    CCLRDebugManager::ProcessInit();

 

       CLR的啓動過程中,每個地方都是重要的部分..

       首先調用一個靜態方法,來set corhandle map,這個map,是在需要把完成的PE文件格式整到成一個內存image的時候用的,而不是systemload方法。

       特別聲明一下:

       PEImage是一個PE文件,它由“simulated loadlibrary”機制load,一個PEImage可以以兩種方式被加載,一種是FLAT方式,這種加載方式加載以後,其佈局和在磁盤上面的佈局相同。另外一種加載方式是MAPPED方式。這種方式對PE Sections的各個部分都map到了不同的虛擬地址上面。

       也可以參考rick的最新的一篇博文也是講的這個問題:

       http://www.cnblogs.com/rick/archive/2008/05/15/1199197.html

       昨天剛發的,呵呵。

 

       EECodeInfo::Init初始化了一個實現了ICodeInfo的類,這個interface的主要作用,是來讓ICodeManager來獲取正在執行的MethodGCinfo,說是比較有用的東西,不過我沒體會到

       接下來是和loadmoudle相關的簽名的初始化,然後是stub的初始化。然後把stub鏈接到特定CPU的架構。

       然後是一些引用類型的初始化。不同的handle類型,強連接弱連接等,不包括pin

       接着初始化Remoting,以及Context上下文。

       然後啓動ThreadManager,啓動線程管理。

       然後是同步pEEShutDownEvent事件。

       接着初始化Read Write LockThen,啓動調試器響應處理邏輯,啓動位於後臺的Debugger Thread

 

 

       到這裏,EE的啓動,就完成了一半的工作。恩,僅僅是一半。

 

       EE啓動的過程中所做的事情認識的越深刻了解的越詳細,對了解EE的各個部分的功能就越容易,條理就越清晰,架構就越熟悉。

       只有完全熟悉了EE的啓動過程中所做的事情,才能說:welcome to the EE World.

      

       這個對以後通過編寫代碼來調用CLR的特定的功能也是非常有幫助的。同時,對於Crack和加密解密的研究也是非常有幫助的。

 

       To be continued..

 

       5/16/2008 10:53:19 AM 首發 http://sscli.cnblogs.com

發佈了119 篇原創文章 · 獲贊 0 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章