[你必须知道的.NET]第二十四回:认识元数据和IL(上)

《你必须知道的.NET》网站 | Anytao技术博客 

[你必须知道的.NET]第二十四回:认识元数据和IL(上)

发布日期:2009.02.24 作者:Anytao
© 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处。

o_icon_your_info.gif说在,开篇之前
很早就有说说Metadata(元数据)和IL(中间语言)的想法了,一直在这篇开始才算脚踏实地的对这两个阶级兄弟投去些细关怀,虽然来得没有《第一回:恩怨情仇:is和as》那么迅速,但是Metadata和IL却是绝对重量级的内容,值得我们在任何时间关注,本文就是开始。

                                                                                      www.anytao.com
1 引言

你可曾想到,我们的C#代码,编译之后究竟为何物?你可曾认知,我们的可执行程序,运行之时的轨迹究竟为哪般?那么,本文通过对Metadata(元数据)和IL(Intermediate Language, 中间语言)的认识开始,来逐步给出答案。在这个探索轨迹上,元数据、IL、程序集、程序域、JIT、虚分派、方法表和托管堆这些形形色色的神秘嘉宾将在某个时刻不期而遇,作为你必须知道的.NET 系列2.0版本的一部分,本文首先从认识元数据和IL这两位重量级选手开始,而其他的嘉宾也将很快登场。

2 初次接触

在事实上,编译之后的cs代码被组织为两种基本的元素:元数据(Metadata)和IL。我们可以以最简单的方式来了解程序集(*.dll)或可执行文件(*.exe)中包含的Metadata和IL的秘密,这种方式就是我们常说的反编译,打开ILDasm并加载实现准备的程序集,我们可以看到托管PE文件的相关内容:

o_anytao_insidenet_metadatail_01.jpg

详细的结构信息和IL代码分析,可以参见[你必须知道的.NET]第3章 “一切从IL开始”的介绍,在此就不做太多的分析。另外,我们可以通过执行“View/MetaInfo/Show!”或者Ctrl+M快捷键来获取该程序集所使用的MetaData信息列表:

File?id=dhb9s74s_41pwsh5bcp_b

其中该程序集使用的元数据主要有:Module、TypeRef、TypeDef、Method、Param、MemberRef、CostomAttribute、Assembly、AssemblyRef等,同时还包括#Strings、#GUID、#Blob、#US堆等。

当然,关于ILDasm工具,还有很多好玩的使用方式来满足我们探索IL代码的好奇心,例如:

021.gif  ildasm Anytao.Insidenet.MetadataIL.exe /output:my.il,将反编译结果导出为il代码格式,生成一个my.il包含了所有的IL代码和一个my.res包含了所有的资源文件。

021.gif  ildasm Anytao.Insidenet.MetadataIL.exe /text,将反编译结果以Console形式输出。

当然我们还是推荐以GUI形式来查看IL细节,组织结构良好的Class View:

021.gif  ildasm Anytao.Insidenet.MetadataIL.exe

下面首先给出参与编译的相关代码文件,然后再展开我们对Metadata和IL的讨论:

//  Release : code01, 2009/02/12                    
//  Author  : Anytao,  http://www.anytao.com
//  List    : One.cs
public   class  One
{
    
public   int  ID {  get set ; }
}
//  Release : code02, 2009/02/12                    
//  Author  : Anytao,  http://www.anytao.com
//  List    : Two.cs
public   class  Two
{
    
public   string  SayHello()
    {
        
return   " Hello, world. " ;
    }
}
//  Release : code03, 2009/02/12                    
//  Author  : Anytao,  http://www.anytao.com  
//  List    : Program.cs
class  Program
{
    
static   void  Main( string [] args)
    {
        
int  id  =   1 ;
        One one 
=   new  One();
        one.ID 
=  id;
        Two two 
=   new  Two();
        Console.WriteLine(two.SayHello());
    }
}
 
 

接着,我们对上述程序的编译执行过程进行一点探索,以命令行编译器来演化其大致的编译过程,以此进一步了解托管模块,程序集和可执行文件之间的关系:

 
 
  • 打开Visual Studio 2008 Command Prompt,并定位到cs代码所在文件夹,编译One.cs为托管模块,执行命令:
    021.gif  csc /t:module One.cs

执行之后,将生成名为One.netmodule文件;

  • 继续执行,将多个模块打包为程序集

021.gif csc /t:library /addmodule:One.netmodule Two.cs
 
  

执行之后,将生成名为Two.dll文件;

  • 最后,编译Main函数和Two.dll为可执行文件

021.gif csc /out:Anytao.Insidenet.MetatdataIL.exe /t:exe /r:Two.dll /r:mscorlib.dll Program.cs
 
 

最终将得到本文开始时所加载的用于反编译的程序集文件Anytao.Insidenet.MetadataIL.exe,在该执行命令中对几个指示符开关做点说明:

  • /out:Anytao.Insidenet.MetadataIL.exe,表示输出的可执行文件,及其名称
  • /t:exe,表示输出的文件类型为CUI(控制台界面程序)程序;而/t:winexe,表示输出为GUI(图形界面程序)程序
  • /r:Two.dll,表示引用刚刚生产的Two.dll程序集
  • /r:mscorlib.dll,表示因为外部程序集mscorlib.dll,因为我们的程序中使用了Console静态方法,而该方法则被定义在mscorlib.dll中。mscorlib.dll是如此的重要,我们将在本文之后的某些时候再次与mscorlib.dll握手,那时在对其进行一个详细的分析,敬请期待。

在cmd中的执行过程可以参考:

o_anytao_insidenet_metadatail_04.jpg

通过分步执行的方式我们对csc编译器的执行过程有个基本的了解,也同时从侧面认识了每次在Visual Studio中执行“Build“或者“ReBuild”的缩影。综上分析,我们可以简单的看到:

File?id=dhb9s74s_42dhjqcvdq_b

Note:在Visual Studio中,编译是分模块进行的,编译结果保存在obj目录中,最后再合并为可执行文件于bin目录,同时默认情况下,编译过程是增量式的,仅编译发生修改的模块,我将在后文给出较为详细的过程。

同时,我们还可以收获以下几个基本的结论:

  • cs代码编译之后将生成元数据和IL,并组成托管模块(Module)的基本单元。

  • 多个托管模块组成程序集,其实还包括一定的资源文件,只是没有在此体现。

  • 程序集或者可执行文件是逻辑组织的基本单元,符合基本的Windows PE文件格式,可以被x86或者x64Windows直接加载执行。

3 继续深入

一个或者多个模块,再加上资源文件就形成了程序集(Assembly),作为逻辑组织的基本单元,

o_anytao_insidenet_metadatail_05.jpg

事实上,此图仅仅从粗粒度对程序集的基本组成有个大致的了解,实际上程序集中包含了复杂的结构和要素,例如PE Signature、Managed Resources、Strong Name Signature Hash,而其中最核心的要素则体现在上图。

  • 程序集清单(MANIFEST)包含了程序集的自描述信息,主要包含AssemblyDef、FileDef、ManifestResourceDef和ExportedTypeDef,在反编译选项中MANIFEST包含了详细的内容。在《你必须知道的.NET》3.1节 “从Hello,world开始认识IL”对其有过详细的描述,此不赘述。
  • PE文件头,标准Windows PE头文件(PE32或PE32+),PE文件的基本信息,例如文件类型,创建时间,本地CPU信息等。
  • CLR头,包含CLR版本、模块元数据、资源等信息。
  • 资源文件。

执行View/Statisctics菜单,可以打开相关的统计信息:

 File size            :  5632
 PE header size       : 
512  ( 496  used)    (  9.09 % )
 PE additional info   : 
1691               ( 30.02 % )
 Num.of PE sections   : 
3
 CLR header size     : 
72                  (  1.28 % )
 CLR meta
- data size  :  2212                ( 39.28 % )
 CLR additional info : 
0                   (  0.00 % )
 CLR method headers  : 
52                  (  0.92 % )
 Managed code         : 
287                (  5.10 % )
 Data                 : 
2048               ( 36.36 % )
 Unaccounted          : 
- 1242              ( - 22.05 % )

 Num.of PE sections   : 
3
   .text    
-   3072
   .rsrc    
-   1536
   .reloc   
-   512

 CLR meta
- data size  :  2212
   Module        
-      1  ( 10  bytes)
   TypeDef       
-      4  ( 56  bytes)       0  interfaces,  0   explicit  layout
   TypeRef       
-     25  ( 150  bytes)
   MethodDef     
-      8  ( 112  bytes)      0   abstract 0  native,  8  bodies
   FieldDef      
-      1  ( 6  bytes)        0  constant
   MemberRef     
-     29  ( 174  bytes)
   ParamDef      
-      2  ( 12  bytes)
   CustomAttribute
-     16  ( 96  bytes)
   StandAloneSig 
-      4  ( 8  bytes)
   PropertyMap   
-      1  ( 4  bytes)
   Property      
-      1  ( 6  bytes)
   MethodSemantic
-      2  ( 12  bytes)
   Assembly      
-      1  ( 22  bytes)
   AssemblyRef   
-      1  ( 20  bytes)
   Strings       
-     920  bytes
   Blobs         
-     328  bytes
   UserStrings   
-      68  bytes
   Guids         
-      16  bytes
   Uncategorized 
-     192  bytes

 CLR method headers : 
52
   Num.of method bodies  
-   8
   Num.of fat headers    
-   4
   Num.of tiny headers   
-   4

 Managed code : 
287
   Ave method size 
-   35

 我们将在后篇《深入程序集和模块》中对PE头,CLR头和资源文件进行详细论述。

  • IL代码被组织为
    . class   public  auto ansi beforefieldinit Anytao.Insidenet.MetadataIL.Two
       extends [mscorlib]System.Object
    {
      .method 
public  hidebysig instance  string  
              SayHello() cil managed
      {
        
//  Code size       11 (0xb)
        .maxstack   1
        .locals init ([
0 string  CS$ 1 $ 0000 )
        IL_0000:  nop
        IL_0001:  ldstr      
" Hello, world. "
        IL_0006:  stloc.
0
        IL_0007:  br.s       IL_0009

        IL_0009:  ldloc.
0
        IL_000a:  ret
      } 
//  end of method Two::SayHello

      .method 
public  hidebysig specialname rtspecialname 
              instance 
void   .ctor() cil managed
      {
        
//  Code size       7 (0x7)
        .maxstack   8
        IL_0000:  ldarg.
0
        IL_0001:  call       instance 
void  [mscorlib]System.Object::.ctor()
        IL_0006:  ret
      } 
//  end of method Two::.ctor

    } 
//  end of class Anytao.Insidenet.MetadataIL.Two

包装在类似于汇编模样的外衣下,我看依稀可见class, System.Object, method, public, string这些面向对象高级语言中的熟悉面孔,不同的只是多了很多benforefieldinit(参考:[你必须知道的.NET]第二十三回:品味细节,深入.NET的类型构造器), ret, maxstack, ldstr, stloc这些陌生的指令。然而IL并非一个怪胎,而正是基于其本身面向对象的汇编式风格,才造就了IL代码成为名副其实的“中间语言”的重任。通过IL代码,CLR就可在编译时由JIT编译转换为Native Code,我们将在下节继续分析这个过程的来龙去脉。

o_icon_your_info.gif欲知后事如何,且听下文继续:-)
  • 元数据是什么?
  • IL是什么?
  • 元数据和IL在JIT编译时

                                                                                      www.anytao.com

 

 

anytao | © 2009 Anytao.com

2009/02/24 | http://anytao.cnblogs.com/ | http://www.cnblogs.com/anytao/archive/2009/02/24/must_net_24.html

本文以“现状”提供且没有任何担保,同时也没有授予任何权利。 | This posting is provided "AS IS" with no warranties, and confers no rights.

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

参考文献

温故知新

[开篇有益]
[第一回:恩怨情仇:is和as]
[第二回:对抽象编程:接口和抽象类]
[第三回:历史纠葛:特性和属性]
[第四回:后来居上:class和struct]
[第五回:深入浅出关键字---把new说透]
[第六回:深入浅出关键字---base和this]
[第七回:品味类型---从通用类型系统开始]
[第八回:品味类型---值类型与引用类型(上)-内存有理]
[第九回:品味类型---值类型与引用类型(中)-规则无边]
[第十回:品味类型---值类型与引用类型(下)-应用征途]
[第十一回:参数之惑---传递的艺术(上)]
[第十二回:参数之惑---传递的艺术(下)]
[第十三回:从Hello, world开始认识IL]
[第十四回:认识IL代码---从开始到现在]
[第十五回:继承本质论]
[第十六回:深入浅出关键字---using全接触]
[第十七回:貌合神离:覆写和重载]
[第十八回:对象创建始末(上)]
[第十九回:对象创建始末(下)]
[第二十回:学习方法论]
[第二十一回:认识全面的null]
[第二十二回:字符串驻留(上)---带着问题思考]
[第二十三回:品味细节,深入.NET的类型构造器]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值