<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      CLR探索系列:深入追蹤托管exe加載執(zhí)行過(guò)程

      在上一篇“CLR探索系列之應(yīng)用程序域世界的上篇中,探討了一些關(guān)于應(yīng)用程序域在托管代碼執(zhí)行過(guò)程中的特性和運(yùn)行機(jī)制,以及一些相關(guān)的概念。

      在接下來(lái)的中篇里,就從如何實(shí)現(xiàn)的角度,換一個(gè)角度來(lái)探討程序集和應(yīng)用程序域是如何加載,執(zhí)行。以及一些有趣的問(wèn)題。

      首先,有一個(gè)有趣的雞和蛋的問(wèn)題。我們知道,一個(gè)應(yīng)用程序集里面的代碼在執(zhí)行的時(shí)候,首先被load,然后經(jīng)過(guò)驗(yàn)證,接著對(duì)IL代碼JIT成為本地代碼才能執(zhí)行。一個(gè)應(yīng)用程序集只有被先加載了才能被執(zhí)行,但是加載程序集的程序集,是被什么程序集加載的呢?或者,第一個(gè)程序集,是如何被加載到CLR的世界中呢?   

      首先,來(lái)查看一下Clix工具作為一個(gè)sscli提供的loader的main函數(shù)都做了些什么:
      int __cdecl main(int argc, char **argv)

      {

          DWORD nExitCode = 1; // error

          WCHAR* pwzCmdLine;

       

          if ( !PAL_RegisterLibrary(L"rotor_palrt")

                  || !PAL_RegisterLibrary(L"sscoree") ) {

              DisplayMessageFromSystem(::GetLastError());

              return 1;

      }


      可以看到,在clixMain函數(shù)里面,就做了兩件事情:注冊(cè)Rotorpalrt模塊,同時(shí),注冊(cè)sscoree模塊。
      在執(zhí)行托管代碼的庫(kù)文件結(jié)構(gòu)中,有三個(gè)層次:
      第一層:Managed libraries
      第二層:
      Execute Engine(CLR)
      第三層:PAL

      第一層里面,主要包含的是BCL;還有一些別的托管系統(tǒng)的庫(kù)文件。例如mscorlib.dll,System.xml.dll或者是別的托管組件之類(lèi)。
      第二層里面,有我們非常熟悉的sscoree.dll,也就是rotor里面的托管程序的執(zhí)行引擎。
      在第三層PAL層里面,主要有兩個(gè)文件:rotor_pal.dllrotor_palrt.dll;在rotor的源代碼解壓后,clr,pal,palrt這三個(gè)文件夾是并列排列的。這也反應(yīng)了這三個(gè)部分之間的關(guān)系。pal是某個(gè)特定的操作系統(tǒng)對(duì)PAL層的實(shí)現(xiàn),而palrt是忽略操作系統(tǒng)的區(qū)別對(duì)PAL層的一般實(shí)現(xiàn)。


      if ( !PAL_RegisterLibrary(L"rotor_palrt")|| !PAL_RegisterLibrary(L"sscoree") )這一行中,首先是加載了托管庫(kù)文件結(jié)構(gòu)里面最下面PAL層的針對(duì)編譯好了的,一個(gè)特定的操作系統(tǒng)的實(shí)現(xiàn)。接著,又是調(diào)用加載了基于這個(gè)PAL_RT層上面的CLI的托管執(zhí)行引擎:sscoree。而對(duì)于托管代碼執(zhí)行需要的庫(kù)文件的第三層,也就是最上面一層,BCL之類(lèi)的庫(kù)文件的加載,則是在創(chuàng)建這個(gè)托管引用程序的內(nèi)存結(jié)構(gòu)的幾個(gè)特定類(lèi)型的應(yīng)用程序域中加載進(jìn)去的。

      這樣,對(duì)于托管代碼執(zhí)行的時(shí)候的需要的一些庫(kù)文件(按照庫(kù)文件的結(jié)構(gòu),從下往上)是如何加載到內(nèi)存中去,以及PAL層和CLR的加載執(zhí)行順序,我們就有了一個(gè)比較清晰的認(rèn)識(shí)了。

            then
      ,在注冊(cè)好了PAL層和CLR之后,我們?cè)賮?lái)看看作為sscli里面提供的一個(gè)loader,是如何實(shí)現(xiàn)load一個(gè)exe(或許是托管的)到執(zhí)行的托管進(jìn)程中去的。打開(kāi)Clix.appLaunch函數(shù):

      //the Launch founction of Clix.Shows how launch of first Assembly.

      //launch the EE of CLI

      DWORD Launch(WCHAR* pFileName, WCHAR* pCmdLine)

      {

             //file name

          WCHAR exeFileName[MAX_PATH + 1];

          DWORD dwAttrs;

             //define the error type

          DWORD dwError;

          DWORD nExitCode;

       

          dwAttrs = ::GetFileAttributesW(pFileName);

       

      //省略若干對(duì)于文件名表示的文件的相關(guān)檢查代碼

       

          if (dwError != ERROR_SUCCESS) {

              // We can't find the file, or there's some other problem. Exit with an error.

              fwprintf(stderr, L"%s: ", pFileName);

              DisplayMessageFromSystem(dwError);

              return 1;   // error

          }

       

             //DWORD Exit Code.

             //這里,調(diào)用導(dǎo)入進(jìn)來(lái)的

          nExitCode = _CorExeMain2(NULL, 0, pFileName, NULL, pCmdLine);

       

          // _CorExeMain2 never returns with success

          _ASSERTE(nExitCode != 0);

       

          DisplayMessageFromSystem(::GetLastError());

       

          return nExitCode;

      }

      首先,我們看這一句:_ASSERTE(nExitCode != 0);程序運(yùn)行到這里的時(shí)候,就是對(duì)一個(gè)托管程序的執(zhí)行已經(jīng)完成了,PALEE和相關(guān)的加載的了的BCL以及相關(guān)的托管模塊和應(yīng)用程序域,這些東西都已經(jīng)退出內(nèi)存,我們對(duì)這個(gè)加載的exe文件的執(zhí)行,就到此為止了。It is the time for us to show down the lightsand went home……^_^

      接著,我們?cè)賮?lái)看這一句: nExitCode = _CorExeMain2(NULL, 0, pFileName, NULL, pCmdLine)這里,就開(kāi)始執(zhí)行外部導(dǎo)入函數(shù)了,也是經(jīng)常看到的非常頻繁的CorExeMain這個(gè)函數(shù)。不同的是,后面多了一個(gè)2。這是商業(yè)版本和開(kāi)源版本的一點(diǎn)小小的區(qū)別了。

       

      在商業(yè)版本的DotNet Framework 中,這個(gè)地方調(diào)用的函數(shù)是_CorExeMain();可以用Dependency walker,PEID,或者是Inspect,來(lái)查看任何一個(gè)本機(jī)上面生成好了的托管的Module。查看某個(gè)Module的導(dǎo)入的庫(kù)。

      下面是我用inspect來(lái)查看一個(gè)托管模塊的導(dǎo)入函數(shù)情況:

      o_zhong_1.jpg


      同時(shí),下面是我用Dependency walker來(lái)查看MSCOREE.dll的內(nèi)部函數(shù):

      r_zhong_2.jpg


      這里,可以看到mscoree.dll里面包含的_CorExeMain這個(gè)函數(shù),同時(shí),如果是一個(gè)dll的話,就間接執(zhí)行_CorDllMain這個(gè)函數(shù)。

       

      下面,就來(lái)看看_CorExeMain這個(gè)函數(shù)都做了些什么。打開(kāi)VM虛擬機(jī)目錄下面的ceemain.cpp文件查看這個(gè)函數(shù)是如何實(shí)現(xiàn)的,都做了些什么。這個(gè)文件中包含了大部分對(duì)ee的操作,初始化,關(guān)閉等等:

      //**********************************************************

      // This entry point is called from the native entry piont of the loaded

      // executable image.  The command line arguments and other entry point data

      // will be gathered here.  The entry point for the user image will be found

      // and handled accordingly.

      //**********************************************************

      __int32 STDMETHODCALLTYPE _CorExeMain2( // Executable exit code.

          PBYTE   pUnmappedPE,                // -> memory mapped code

          DWORD   cUnmappedPE,                // Size of memory mapped code

          __in LPWSTR  pImageNameIn,          // -> Executable Name

          __in LPWSTR  pLoadersFileName,      // -> Loaders Name

          __in LPWSTR  pCmdLine)              // -> Command Line

      {

       

          // This entry point is used by clix

          BOOL bRetVal = 0;

       

          //BEGIN_ENTRYPOINT_VOIDRET;

       

          // Before we initialize the EE, make sure we've snooped for all EE-specific

      // command line arguments that might guide our startup.

      //處理和文件名一起傳遞進(jìn)來(lái)的命令參數(shù)。首先確定是不是一個(gè)托管的模塊,并且對(duì)其進(jìn)行一系列的檢查。如果不是就直接退出托管環(huán)境的加載。

          HRESULT result = CorCommandLine::SetArgvW(pCmdLine);

       

             //把命令行緩存起來(lái)。

          if (!CacheCommandLine(pCmdLine, CorCommandLine::GetArgvW(NULL))) {

              LOG((LF_STARTUP, LL_INFO10, "Program exiting - CacheCommandLine failed\n"));

              bRetVal = -1;

              goto exit;

          }

       

      if (SUCCEEDED(result))

             //如果相關(guān)的檢查成功,就在這里初始化EE,調(diào)用這個(gè)文件里面的CoInitializeEE方法

              result = CoInitializeEE(COINITEE_DEFAULT | COINITEE_MAIN);

       

          if (FAILED(result)) {

              VMDumpCOMErrors(result);

              SetLatchedExitCode (-1);

              goto exit;

          }

       

          // This is here to get the ZAPMONITOR working correctly

          INSTALL_UNWIND_AND_CONTINUE_HANDLER;

       

          // Load the executable

          bRetVal = ExecuteEXE(pImageNameIn);

       

          if (!bRetVal) {

              // The only reason I've seen this type of error in the wild is bad

              // metadata file format versions and inadequate error handling for

              // partially signed assemblies.  While this may happen during

              // development, our customers should not get here.  This is a back-stop

              // to catch CLR bugs. If you see this, please try to find a better way

              // to handle your error, like throwing an unhandled exception.

              EEMessageBoxCatastrophic(IDS_EE_COREXEMAIN2_FAILED_TEXT, IDS_EE_COREXEMAIN2_FAILED_TITLE);

              SetLatchedExitCode (-1);

          }

      UNINSTALL_UNWIND_AND_CONTINUE_HANDLER;

       

      exit:

          STRESS_LOG1(LF_STARTUP, LL_ALWAYS, "Program exiting: return code = %d", GetLatchedExitCode());

      STRESS_LOG0(LF_STARTUP, LL_INFO10, "EEShutDown invoked from _CorExeMain2");

       

      EEPolicy::HandleExitProcess();

         

          //END_ENTRYPOINT_VOIDRET;

          return bRetVal;

      }

       

      這里,就完成了對(duì)一個(gè)exe文件的加載過(guò)程。同時(shí),在bRetVal = ExecuteEXE(pImageNameIn);這一行也調(diào)用了執(zhí)行這個(gè)文件的方法。繼續(xù)查看這個(gè)方法的實(shí)現(xiàn):

      BOOL STDMETHODCALLTYPE ExecuteEXE(HMODULE hMod)

      {

          STATIC_CONTRACT_GC_TRIGGERS;

       

          _ASSERTE(hMod);

          if (!hMod)

              return FALSE;

       

          ETWTraceStartup::TraceEvent(ETW_TYPE_STARTUP_EXEC_EXE);

          TIMELINE_START(STARTUP, ("ExecuteExe"));

       

          EX_TRY_NOCATCH

          {

              // Executables are part of the system domain

              SystemDomain::ExecuteMainMethod(hMod);

          }

          EX_END_NOCATCH;

       

          ETWTraceStartup::TraceEvent(ETW_TYPE_STARTUP_EXEC_EXE+1);

          TIMELINE_END(STARTUP, ("ExecuteExe"));

       

          return TRUE;

      }

      這里,終于找到了我們需要找的東西,調(diào)用了應(yīng)用程序域里面的執(zhí)行Main函數(shù)的方法,接著打開(kāi)Assembly.Cpp文件里面的這個(gè)方法,查看這個(gè)方法是如何實(shí)現(xiàn)在一個(gè)應(yīng)用程序域里面執(zhí)行一個(gè)新加載的ModuleMain函數(shù)的:

      INT32 Assembly::ExecuteMainMethod(PTRARRAYREF *stringArgs)

      {

       

             ………………..

       

          BEGIN_ENTRYPOINT_THROWS;

       

          Thread *pThread = GetThread();

          MethodDesc *pMeth;

          {

              // This thread looks like it wandered in -- but actually we rely on it to keep the process alive.

              pThread->SetBackground(FALSE);

         

              GCX_COOP();

       

              pMeth = GetEntryPoint();

              if (pMeth) {

                  RunMainPre();

                  hr = ClassLoader::RunMain(pMeth, 1, &iRetVal, stringArgs);

              }

          }

            

             //省略執(zhí)行結(jié)束的銷(xiāo)毀相關(guān)內(nèi)容的執(zhí)行邏輯    

       

          return iRetVal;

      }

      到這里,找到了最后執(zhí)行一個(gè)load了的模塊的Main方法的地方,是在ClassLoader里面的RunMain方法中。而上面的ExecuteMainMethod方法,只是為Module的執(zhí)行提供了一個(gè)從應(yīng)用程序域的角度來(lái)控制的環(huán)境,為已經(jīng)加載了的一個(gè)模塊的執(zhí)行分配一個(gè)線程,同時(shí),處理這個(gè)模塊執(zhí)行好了之后相關(guān)的操作。

      我們就接著追蹤最后RunMain最后都干了些啥,最后一段代碼,也是vm虛擬機(jī)目錄下面的clsload.cpp這個(gè)文件里面的方法,(從這里,我們也看到了Rotor中非常好的層次設(shè)計(jì)和架構(gòu)設(shè)計(jì),每一層的事情和相關(guān)的處理邏輯,都控制相關(guān)的層面上面,絕不在上面一層做下面的一層的事情):

      /* static */

      HRESULT ClassLoader::RunMain(MethodDesc *pFD ,

                                   short numSkipArgs,

                                   INT32 *piRetVal,

                                   PTRARRAYREF *stringArgs /*=NULL*/)

      {

          STATIC_CONTRACT_THROWS;

          _ASSERTE(piRetVal);

       

          DWORD       cCommandArgs = 0;  // count of args on command line

          DWORD       arg = 0;

          LPWSTR      *wzArgs = NULL; // command line args

          HRESULT     hr = S_OK;

       

          *piRetVal = -1;

       

          // The exit code for the process is communicated in one of two ways.  If the

          // entrypoint returns an 'int' we take that.  Otherwise we take a latched

          // process exit code.  This can be modified by the app via setting

      // Environment's ExitCode property.

      //設(shè)置返回code的類(lèi)型

          if (stringArgs == NULL)

              SetLatchedExitCode(0);

       

             //pFD這個(gè)指針是指向的每個(gè)在內(nèi)存里面的實(shí)例的instance data的方法列表,也就是一個(gè)叫做ObjHeader的指針。我們?cè)谏钊胙芯?/span>System.Object在內(nèi)存里面布局的時(shí)候,會(huì)看到這個(gè)東西。對(duì)于每個(gè)在內(nèi)存中的instance,實(shí)例的相關(guān)數(shù)據(jù)在內(nèi)存中開(kāi)始的第一個(gè)地址的前一個(gè)位置,保存的是一個(gè)指向這個(gè)MethodTable方法列表的指針。這個(gè)方法列表,是保存在EE的私有內(nèi)存地址中的,用來(lái)方便對(duì)執(zhí)行的時(shí)候的實(shí)例和對(duì)象的控制。而這個(gè)指針的前一個(gè)指針,則表示的是一個(gè)叫做SyncBlock table的東西,也是用于EE對(duì)對(duì)象的控制的。而一個(gè)實(shí)例的數(shù)據(jù),是保存在GC堆里面的。

             //下面一句的用處,就是如果這個(gè)指向這個(gè)實(shí)例的方法的指針是空的時(shí)候,(一個(gè)對(duì)象的方法可以為空,但是指向這個(gè)對(duì)象的實(shí)例的method table的指針不能為空),就會(huì)提示錯(cuò)誤。

          if (!pFD) {

              _ASSERTE(!"Must have a function to call!");

              return E_FAIL;

          }

       

          CorEntryPointType EntryType = EntryManagedMain;

          ValidateMainMethod(pFD, &EntryType);

       

          if ((EntryType == EntryManagedMain) &&

              (stringArgs == NULL)) {

              // If you look at the DIFF on this code then you will see a major change which is that we

              // no longer accept all the different types of data arguments to main.  We now only accept

              // an array of strings.

       

              wzArgs = CorCommandLine::GetArgvW(&cCommandArgs);

              // In the WindowsCE case where the app has additional args the count will come back zero.

              if (cCommandArgs > 0) {

                  if (!wzArgs)

                      return E_INVALIDARG;

              }

          }

       

          ETWTraceStartup::TraceEvent(ETW_TYPE_STARTUP_MAIN);

          TIMELINE_START(STARTUP, ("RunMain"));

       

          EX_TRY_NOCATCH

          {

              MethodDescCallSite  threadStart(pFD);

             

              PTRARRAYREF StrArgArray = NULL;

              GCPROTECT_BEGIN(StrArgArray);

       

              // Build the parameter array and invoke the method.

                    //分為兩種情況來(lái)處理:有參數(shù)和沒(méi)有參數(shù)

              if (EntryType == EntryManagedMain) {

                  if (stringArgs == NULL) {

                      // Allocate a COM Array object with enough slots for cCommandArgs - 1

                      StrArgArray = (PTRARRAYREF) AllocateObjectArray((cCommandArgs - numSkipArgs), g_pStringClass);

       

                      // Create Stringrefs for each of the args

                      for( arg = numSkipArgs; arg < cCommandArgs; arg++) {

                          STRINGREF sref = COMString::NewString(wzArgs[arg]);

                          StrArgArray->SetAt(arg-numSkipArgs, (OBJECTREF) sref);

                      }

                  }

                  else

                      StrArgArray = *stringArgs;

              }

       

      #ifdef STRESS_THREAD

              OBJECTHANDLE argHandle = (StrArgArray != NULL) ? CreateGlobalStrongHandle (StrArgArray) : NULL;

              Stress_Thread_Param Param = {pFD, argHandle, numSkipArgs, EntryType, 0};

              Stress_Thread_Start (&Param);

      #endif

       

              ARG_SLOT stackVar = ObjToArgSlot(StrArgArray);

       

              if (pFD->IsVoid())

              {

                  // Set the return value to 0 instead of returning random junk

                  *piRetVal = 0;

                  threadStart.Call(&stackVar);

              }

              else

              {

                  *piRetVal = (INT32)threadStart.Call_RetArgSlot(&stackVar);

                  if (stringArgs == NULL)

                  {

                      SetLatchedExitCode(*piRetVal);

                  }

              }

              GCPROTECT_END();

       

              fflush(stdout);

              fflush(stderr);

          }

          EX_END_NOCATCH

       

          ETWTraceStartup::TraceEvent(ETW_TYPE_STARTUP_MAIN+1);

          TIMELINE_END(STARTUP, ("RunMain"));

       

          return hr;

      }

      在這個(gè)方法中,還設(shè)計(jì)到了一系列對(duì)COM口的交互,其中每一行,都需要對(duì)托管應(yīng)用程序在內(nèi)存中的結(jié)構(gòu)有個(gè)清晰的了解,對(duì)這個(gè)方法做深入的分析,以及執(zhí)行的流程,就是下一篇博文的事情了。^_^

       

       

      Ps:分析追蹤了大概6,7個(gè)文件,從CLI,到CLR再到應(yīng)用程序域,然后到ClassLoad里面的方法,終于在一定層次上面搞清楚了一個(gè)托管應(yīng)用程序的加載過(guò)程,以及這個(gè)過(guò)程中CLIEEAppDomain的加載執(zhí)行過(guò)程和順序.終于從程序的加載,刨到了應(yīng)用程序域的世界。這個(gè)和我的初衷,寫(xiě)一篇從代碼分析應(yīng)用程序域似乎有些不符合

      為此,就改一個(gè)題目,改成系列中的中篇吧

      另外,我以前是寫(xiě)了一半保存在blog上面的,后來(lái)中途寫(xiě)的時(shí)候,沒(méi)保存好,丟失了,又重新寫(xiě)了一遍不過(guò)靈機(jī)一動(dòng),在baidu里面搜索我的文章的題目,在快照里面找到了我以前的一個(gè)版本,^_^,以后都在word里面先寫(xiě)好了再整過(guò)來(lái)。

       

      文章里面有紕漏的地方,歡迎大家指正!:)

      后記:

      補(bǔ)充說(shuō)明下,一個(gè)托管對(duì)象在內(nèi)存里面的格式:

      托管對(duì)象的結(jié)構(gòu)如下:

                       m_SyncBlockValue

      對(duì)象指針->  m_pMethodTable

                       Data

      在每個(gè)托管對(duì)象的開(kāi)始是該對(duì)象類(lèi)型的方法表。在方法表之前是m_SyncBlockValue

      m_SyncBlockValue的高6位用來(lái)標(biāo)記m_SyncBlockValue的用途。SyncBlockValue的低26位用來(lái)存儲(chǔ)哈希碼,SyncBlock索引或SpinBlock。

      26位值的含義由高6位來(lái)決定。

      posted on 2007-12-21 17:24  lbq1221119  閱讀(4043)  評(píng)論(6)    收藏  舉報(bào)

      導(dǎo)航

      主站蜘蛛池模板: 中文无码乱人伦中文视频在线| 边添小泬边狠狠躁视频| 郓城县| 国产日韩AV免费无码一区二区三区 | 久久99九九精品久久久久蜜桃| 日本高清视频网站www| 激情动态图亚洲区域激情| 国产精品一在线观看| 色婷婷综合久久久久中文一区二区| 国产黄色精品一区二区三区| 乌克兰丰满女人a级毛片右手影院 人妻中文字幕不卡精品 | jlzz大jlzz大全免费| 伊人久久大香线蕉av色婷婷色| 伊人成色综合人夜夜久久| 成人午夜在线观看日韩| 99精品热在线在线观看视| 精品久久人人妻人人做精品| 人妻中文字幕不卡精品| 婷婷亚洲综合五月天小说| 国产偷国产偷亚洲综合av| 国产精品一区中文字幕| 亚洲欧洲美洲无码精品va| 中文字幕一区二区人妻| 国产午夜亚洲精品国产成人| 久久久av波多野一区二区| 五月婷婷中文字幕| 亚洲熟女精品一区二区| 日韩精品有码中文字幕| 999福利激情视频| 色婷婷亚洲精品综合影院| 罗甸县| 玩弄美艳馊子高潮无码| 神池县| 风韵丰满妇啪啪区老老熟女杏吧| 亚洲日本VA午夜在线电影| 北流市| 亚洲精品电影院| 国产成人精品免费视频app软件| caoporn成人免费公开| 尤物yw193无码点击进入| av无码精品一区二区乱子|