zoukankan      html  css  js  c++  java
  • 加载.NET程序集

    分析.NET应用程序启动过程的最佳方式就是观察一个简单的.NET命令行程序。程序的源代码和程序集分别位于以下文件夹中:

    源代码文件:C:\\adnd\\chapter1\\MDASample

    程序集文件:C:\\adndbin\\01mdasample.exe

    如果运行上面的程序,它会成功执行,如清单2-2所示。

    清单2-2执行02simple.exe

     

    由于.NET应用程序在执行时要预先加载CLR,那么Windows如何知道加载并初始化CLR?我们可能会做出一种假设:系统开发人员对 Windows加载器进行了改动以识别.NET程序集,并且当检测到.NET程序集时自动启动CLR。尽管这种假设只有部分是正确的,但在.NET之前的 Windows上执行.NET应用程序时,必须首先打补丁。回答这个问题的关键在于:对PE格式进行了扩展。在前面已经提到过,PE格式是Windows 可执行程序的文件格式,用来管理PE文件中代码的执行。可执行程序包括EXE、DLL、OBJ、SYS等文件。为了支持.NET,在PE文件格式中增加了 对程序集的支持,如图2-3所示。

    现在,我们来分析当加载器遇到一个.NET应用程序时将发生哪些动作。在这个示例中使用02simple.exe,这个程序位于 C:\\ADNDBin。需要注意的是,这个示例程序是在Windows 2000上运行。之所以要在一个旧版本的Windows上运行,是因为在Windows 2000之后的版本中增加了一些改动,而这些改动将影响Windows加载器加载.NET程序集的方式(在本章的后面将进行介绍)。为了更好地说明这些概 念,我使用了一个工具dumpbin.exe,它能够解析PE文件格式并且以简洁易读的形式转储出PE文件的内容。在02simple.exe上运行 dumpbin.exe的结果被保存在一个文件中:

     
    第一部分值得注意的信息是在Optional Header Values段中,如下所示:
     

    上面的Entry Point域对应于PE文件中的AddressOfEntryPoint域,值为0x00402464。要找出位置0x00402464所对应的代码,需遥看PE映像中的.text段,具体来说就是如下面清单中的RAW DATA段:
     
    上面信息中的粗体字节对应于AddressOfEntryPoint ,这些字节对应的机器指令为:
     

    这里有一个问题:402000 表示什么意思?事实上,0x402000指向的是PE映像文件中的另一部分,也就是import段,在这个段中列出的是PE文件依赖的所有模块。在加载 时,系统将修正导入函数的实际地址,并执行正确的调用。要找到0x402000指向的内容,我们可以查看PE文件的导入段,可以发现以下内容:

     

    可以看到,0x402000指向的是mscoree.dll(Micorsoft对象运行时执行引擎,Microsoft Object Runtime Execution Engine),这个库中包含了一个导出函数_CorExeMain。然后,前面的JMP 指令可以转换为以下伪码:

     

    我们已经看到了,_CorExeMain是mscoree.dll的一部分,这个函数也是在加载.NET程序集时第一个被调用的函数。 mscoree.dll(和_CorExeMain)的主要作用就是启动CLR。 mscoree.dll在启动CLR时将执行一系列的工作:

    1)通过查看PE文件中的元数据,找出.NET程序集是基于哪个版本的CLR构建的。

    2)找出操作系统中正确版本CLR的路径。

    3)加载并初始化CLR。

    在CLR被初始化之后,在PE映像的CLR头中就可以找到程序集的入口点(Main())。然后,JIT开始编译并执行入口点。到目前为止,我们所 谈到的CLR还只是一个逻辑组件,而并没有提到它的各项功能具体由哪些映像来实现。CLR的大部分功能是由mscorwks.dll来实现的。而且,在任 何一台机器上都可能有多个版本的mscorwks.dll。例如,如果安装了.NET 1.1和.NET 2.0,那么在机器上将存在以下CLR DLL:

    C:\\Windows\\Microsoft.NET\\Framework\\v1.1.4322\\mscorwks.dll

    C:\\Windows\\Microsoft.NET\\Framework\\v2.0.50727\\mscorwks.dll

    之所以要安装多个版本,是因危同的.NET应用程序可能需遥同版本的CLR。基于CLR 1.0编写的应用程序需要正确地加载1.0 版本的CLR,即使有.NET 2.0也不行。这种机制本质上是实现.NET中的平行执行模型(Side-By-Side Execution Model)。mscoree.dll的作用是通过查看PE映像文件中的CLR头来找出程序集需要使用哪个版本的CLR。具体来 说,mscoree.dll将查看CLR头中的MajorRuntimeVersion和MinorRuntimeVersion两个域,并且加载正确版 本的CLR。

    到目前为止,我们已经介绍了.NET程序集(后缀名为.EXE)的整个启动流程。正如非托管的Windows应用程序能够支持像动态库这种可执行形 式,.NET也有着同样的方式。就加载器而言,.NET库与.NET可执行程序之间的唯一差别就是,在PE映像中导入的函数不是_CorExeMain, 而是_CorDllMain。

    在加载mscoree.dll时有一个值得注意的问题,即为什么需要非托管的存根函数来调用_CorExeMain?因为PE映像文件包含了一 个.NET头,那么Windows加载器是否也可以将这个PE映像识别为一个.NET程序集并且自动加载mscoree.dll?是的,的确如此。在 Windows XP以及之后的版本中对Windows加载器进行了升级,使其能够识别出一个.NET程序集的PE映像,并且自动加载CLR。

    .NET程序集的加载算法总结如下:

    1)用户执行一个.NET程序集。

    2)Windows加载器查看AddressOfEntryPoint 域,并找到PE映像文件的.text段。

    3)位于AddressOfEntryPoint 位置上的字节只是一个JMP指令,这个指令跳转到mscoree.dll中的一个导入函数。

    4)将执行控制转移到mscoree.dll中的函数_CorExeMain中,这个函数将启动CLR并且把执行控制转移到程序集的入口点。

    PE文件格式是一种多用途的格式(从它可以很容易支持.NET程序集的特性也说明了这一点),它包含了与被加载和执行的PE映像相关的大量信息。本 节内容重点介绍了如何通过扩展PE文件格式来支持.NET程序集的执行。接下来,我们将深入分析CLR中其他的关键内容,首先介绍应用程序域。

  • 相关阅读:
    一个十分诡异的NullReferenceException异常!
    如何去掉TabControl控件默认添加的TabPage
    GDI+发生一般性错误的解决方法
    C#中各种数组的性能比较
    酷享娱乐新生活
    关于ImageList.Images集合的特殊行为!
    WinForm窗体之间交互的一些方法
    实现单实例应用程序的三种方案
    MySql_Close 释放资源
    数组之List
  • 原文地址:https://www.cnblogs.com/shihao/p/2500794.html
Copyright © 2011-2022 走看看