← 筆記

C#筆記 – CLR的執行模型(一) 托管模塊與元數據

公共語言運行時
  • 公共語言運行時(Common Language Runtime,CLR)是一個可由多種編程語言使用的「運行時」

  • 「運行時」!=「程序運行的時候」

    • 「源代碼」-> 「中間碼」(IL)-> 「虛擬機」管理和執行IL

    • 「虛擬機」可以參與和管理程序代碼的執行,解决了垃圾内存回收、安全性檢查等問題

      • 這個「虛擬機」在.NET框架中就是「運行時」(公共語言運行時,CLR)
    • 參考:https://www.cnblogs.com/shourenwangzi/p/6492625.html

  • CLR的核心功能可由面向CLR的所有語言使用

    • 如:內存管理、程序集加載、安全性、異常處理、線程同步
編譯源代碼的過程

托管模塊(managed module)
  • 32位Microsoft Windows可移植執行體(PE32)文件 或 64位Microsoft Windows可移植執行體(PE32+)文件

    • PE文件都需要CLR才能執行

  • PE32/PE32+頭:

    • Windows PE文件頭:

      • PE32文件能在32位/64位的Windows上運行

      • PE32+能在64位的Windows上運行

    • 文件類型標識:如GUI、CUI、DLL、生成時間

    • 與本機CPU代碼有關的信息(包含native CPU代碼時)

  • CLR頭

    1. CLR頭包含模塊生成時所面向的CLR的major(主)和minor(次)版本號

    2. 托管模塊入口方法MethodDef元數據token

    3. 模塊的元數據、資源、強名稱、標志及其他的數據項的位置/大小,如:

      • 指定模塊入口方法的MethodDef token

      • 一個可選的強名稱數字簽名

      • 模塊內部元數據表的大小和偏移量

      • 具體格式可參考CorHdr.h頭文件定義的IMAGE_COR20_HEADER

        • 該文件位於C:Program Files (x86)Windows KitsNETFXSDK4.8Includeum中

        •   typedef struct IMAGE_COR20_HEADER
            {
                // Header versioning
                //CLR版本號
                DWORD                   cb;              
                WORD                    MajorRuntimeVersion;
                WORD                    MinorRuntimeVersion;
                
                // Symbol table and startup information
                //元數據表
                IMAGE_DATA_DIRECTORY    MetaData;    
            
                //一些標志
                DWORD                   Flags;          
               //模塊的入口方法
               union {
                    DWORD               EntryPointToken;
                    DWORD               EntryPointRVA;
               };
               
                IMAGE_DATA_DIRECTORY    Resources
                //強名稱簽名
                IMAGE_DATA_DIRECTORY    StrongNameSignature;
            
                IMAGE_DATA_DIRECTORY    CodeManagerTable; // Depricated, not used 
                IMAGE_DATA_DIRECTORY    VTableFixups;
                IMAGE_DATA_DIRECTORY    ExportAddressTableJumps;
            
                IMAGE_DATA_DIRECTORY    ManagedNativeHeader;
                
            } IMAGE_COR20_HEADER, *PIMAGE_COR20_HEADER
  • 元數據表

    1. 描述源代碼中定義的類型和成員的表

    2. 描述源代碼引用的類型和成員的表

  • IL(中間語言)代碼

    • 編譯器編譯源代碼時生成

    • 將被CLR編譯成本機CPU指令

  • 編譯器與托管代碼

    • 本機代碼編譯器(native code compilers)生成的是面向特定CPU架構(x86、x64、ARM)的代碼

    • 面向CLR的編譯器(如Visual Studio內置的C#編譯器)生成的都是IL代碼

      • IL代碼的執行需要由CLR來管理,因此被稱為托管代碼

      • 除了IL,還要在托管模塊中生成完整的元數據(metadata)

元數據
  • 數據表的集合,包含了

    • 定義表(definition table)

      • 元數據定義表名稱說明
        ModuleDef- 對模塊進行標識的一個記錄項 - 該記錄項包含模塊文件名、擴展名(不包含路徑)以及模塊版本ID
        TypeDef- 模塊定義的每個類型 - 每個記錄項包含類型的: - 名稱 - 基類 - 標志(public、private等) - 一些索引,指向MethodDef表、FieldDef表、PropertyDef表、EventDef表下該類型的方法、字段、屬性、事件
        MethodDef- 模塊定義的每個方法 - 包含了方法的名稱、標志(public、static、virtual等)、簽名、方法的IL代碼在模塊中的偏移量 - 每個記錄項還引用了ParamDef表中的一個記錄項,包含與該方法參數相關的信息
        FieldDef- 模塊定義的每個字段,包含了字段標志(public、private等)、名稱和類型
        ParamDef- 模塊定義的每個參數,包含了字段標志(in, out等)、名稱和類型
        PropertyDef- 模塊定義的每個屬性,包含了標志、類型和名稱
        EventDef- 模塊定義的每個事件,包含了標志和名稱
      • 編譯器編譯源代碼時,代碼定義的任何東西都會導致上表列出的某個表中創建一個記錄項

    • 引用表(reference table)

      • 引用元數據表名稱說明
        AssemblyRef- 模塊引用的每個程序集,包含了綁定該程序集所需的信息 - 程序集名稱、版本號、語言文化、公鑰(token) - 每個記錄項還包含一些flag和一個hash值
        ModuleRef- 實現該模塊所引用的類型的每個PE模塊 - 包含模塊的文件名和擴展名 - 可能是別的模塊實現了需要的類型,該表的作用就是建立與那些類型的綁定關係
        TypeRef- 模塊引用的每個類型 - 包含類型的名稱和一個引用(指向類型的位置) - 如果類型在另一個類型中實現,引用指向一個TypeRef記錄項 - 如果在同一模塊中實現,指向ModuleDef - 如果在調用程序集的另一個模塊中實現,指向ModuleRef - 如果在不同的程序集中實現,指向AssemblyRef
        MemberRef- 模塊引用的每個成員(字段/方法/屬性/事件) - 包含成員的名稱和簽名,並指向對成員進行定義的那個類型的TypeRef記錄項
      • 編譯器會檢測源代碼引用的類型、字段、方法、屬性、事件,並創建相應的記錄項。

    • 清單表(manifest table)

      • 清單元數據表名稱說明
        AssemblyDef- 如果模塊被標識為程序集,就會包含一個記錄項列出程序集名稱、版本、語言文化、flag、hash算法、發布者公鑰
        FileDef- 作為程序集一部分的每個PE文件和資源文件都有一個記錄項(清單所在文件除外) - 包含文件名和擴展名、Hash值和一些flags - 如果程序集只包含它自己的文件,FileDef表將無記錄。(指程序集只包含它的主模塊,不包含其他非主模塊和資源文件)
        ManifestResourceDef- 作為程序集一部分的每個資源都有一個記錄項 - 包含資源名稱、一些flags、FileDef表的一個索引(指出資源或流包在哪個文件中) - 嵌入資源還包含一個偏移量,指出資源流在PE文件中的起始位置
        ExportedTypesDef- 程序集的所有PE模塊導出的每個public類型都有一個記錄項 - 包含了類型名稱、FileDef表的一個索引(指出類型在哪個文件實現)、TypeDef表的一個索引(類型內信息)
      • 包含清單的程序集文件還有一個AssemblyRef表,程序集全部文件引用的每個程序集都有一個記錄項

        • 只要打開程序集的清單,就可以知道它引用的全部程序集
  • 元數據嵌入EXE/DLL文件中,與包含IL代碼的文件關聯

  • 編譯器同時生成元數據和代碼,把它們綁定在一起,並嵌入最終生成的托管模塊

  • 用途

    • 實現類型/成員的IL代碼文件包含有關引用類型/成員的所有信息,編譯器直接從托管模塊讀取元數據,避免了編譯時對原生C/C++頭和庫文件的需求

    • CLR的代碼驗證過程使用元數據來確保只執行「類型安全」的操作

    • 元數據允許將對象的字段序列化到內存塊中,然後在另一台機器上反序列化,在遠程機器上重建對象狀態

    • 元數據允許GC跟蹤對象生存期:

      • GC能判斷任何對象的類型,並從元數據中知道該對象的哪些字段引用了其他對象
  • 我們可以使用Visual Studio提供的ILDasm.exe(IL Disassembler, IL反匯編器)檢查托管PE文件中的元數據。

    • 在Visual Studio提供的命令行工具中執行 ildasm.exe [剛剛生成的可執行應用程序名],打開平常查看IL的窗口

  • 再選擇「檢視」->「MetaInfo」->「顯示!」,打開查看應用程序的元信息
    • 源代碼:

      namespace CLR_Via_CSharp_4._0
      {
          public sealed class Program
          {
              public static void Main(string[] args)
              {
                  System.Console.WriteLine("Hello World!");
                  System.Console.ReadLine();
              }
          }
      }
    • 元數據:

      •   ===========================================================
          ScopeName : MyProject.exe
          MVID      : {B0B1828F-8FEC-46CB-BB73-CCA8C0BD50CA}
          ===========================================================
          Global functions
          -------------------------------------------------------
          
          Global fields
          -------------------------------------------------------
          
          Global MemberRefs
          -------------------------------------------------------
          
          TypeDef #1 (02000002)
          -------------------------------------------------------
              TypDefName: CLR_Via_CSharp_4._0.Program  (02000002)
              Flags     : [Public] [AutoLayout] [Class] [Sealed] [AnsiClass] [BeforeFieldInit]  (00100101)
              Extends   : 01000005 [TypeRef] System.Object
              Method #1 (06000001) [ENTRYPOINT]
              -------------------------------------------------------
                  MethodName: Main (06000001)
                  Flags     : [Public] [Static] [HideBySig] [ReuseSlot]  (00000096)
                  RVA       : 0x00002050
                  ImplFlags : [IL] [Managed]  (00000000)
                  CallCnvntn: [DEFAULT]
                  ReturnType: Void
                  1 Arguments
                      Argument #1:  SZArray String
                  1 Parameters
                      (1) ParamToken : (08000001) Name : args flags: [none] (00000000)
          
              Method #2 (06000002) 
              -------------------------------------------------------
                  MethodName: .ctor (06000002)
                  Flags     : [Public] [HideBySig] [ReuseSlot] [SpecialName] [RTSpecialName] [.ctor]  (00001886)
                  RVA       : 0x00002064
                  ImplFlags : [IL] [Managed]  (00000000)
                  CallCnvntn: [DEFAULT]
                  hasThis 
                  ReturnType: Void
                  No arguments.
          
          
          TypeRef #1 (01000001)
          -------------------------------------------------------
          Token:             0x01000001
          ResolutionScope:   0x23000001
          TypeRefName:       System.Runtime.CompilerServices.CompilationRelaxationsAttribute
              MemberRef #1 (0a000001)
              -------------------------------------------------------
                  Member: (0a000001) .ctor: 
                  CallCnvntn: [DEFAULT]
                  hasThis 
                  ReturnType: Void
                  1 Arguments
                      Argument #1:  I4
          
          TypeRef #2 (01000002)
          -------------------------------------------------------
          Token:             0x01000002
          ResolutionScope:   0x23000001
          TypeRefName:       System.Runtime.CompilerServices.RuntimeCompatibilityAttribute
              MemberRef #1 (0a000002)
              -------------------------------------------------------
                  Member: (0a000002) .ctor: 
                  CallCnvntn: [DEFAULT]
                  hasThis 
                  ReturnType: Void
                  No arguments.
          
          TypeRef #3 (01000003)
          -------------------------------------------------------
          Token:             0x01000003
          ResolutionScope:   0x23000001
          TypeRefName:       System.Diagnostics.DebuggableAttribute
              MemberRef #1 (0a000003)
              -------------------------------------------------------
                  Member: (0a000003) .ctor: 
                  CallCnvntn: [DEFAULT]
                  hasThis 
                  ReturnType: Void
                  1 Arguments
                      Argument #1:  ValueClass DebuggingModes
          
          TypeRef #4 (01000004)
          -------------------------------------------------------
          Token:             0x01000004
          ResolutionScope:   0x01000003
          TypeRefName:       DebuggingModes
          
          TypeRef #5 (01000005)
          -------------------------------------------------------
          Token:             0x01000005
          ResolutionScope:   0x23000001
          TypeRefName:       System.Object
              MemberRef #1 (0a000006)
              -------------------------------------------------------
                  Member: (0a000006) .ctor: 
                  CallCnvntn: [DEFAULT]
                  hasThis 
                  ReturnType: Void
                  No arguments.
          
          TypeRef #6 (01000006)
          -------------------------------------------------------
          Token:             0x01000006
          ResolutionScope:   0x23000001
          TypeRefName:       System.Console
              MemberRef #1 (0a000004)
              -------------------------------------------------------
                  Member: (0a000004) WriteLine: 
                  CallCnvntn: [DEFAULT]
                  ReturnType: Void
                  1 Arguments
                      Argument #1:  String
              MemberRef #2 (0a000005)
              -------------------------------------------------------
                  Member: (0a000005) ReadLine: 
                  CallCnvntn: [DEFAULT]
                  ReturnType: String
                  No arguments.
          
          Assembly
          -------------------------------------------------------
              Token: 0x20000001
              Name : MyProject
              Public Key    :
              Hash Algorithm : 0x00008004
              Version: 0.0.0.0
              Major Version: 0x00000000
              Minor Version: 0x00000000
              Build Number: 0x00000000
              Revision Number: 0x00000000
              Locale: 
              Flags : [none] (00000000)
              CustomAttribute #1 (0c000001)
              -------------------------------------------------------
                  CustomAttribute Type: 0a000001
                  CustomAttributeName: System.Runtime.CompilerServices.CompilationRelaxationsAttribute :: instance void .ctor(int32)
                  Length: 8
                  Value : 01 00 08 00 00 00 00 00                          >                <
                  ctor args: (8)
          
              CustomAttribute #2 (0c000002)
              -------------------------------------------------------
                  CustomAttribute Type: 0a000002
                  CustomAttributeName: System.Runtime.CompilerServices.RuntimeCompatibilityAttribute :: instance void .ctor()
                  Length: 30
                  Value : 01 00 01 00 54 02 16 57  72 61 70 4e 6f 6e 45 78 >    T  WrapNonEx<
                                : 63 65 70 74 69 6f 6e 54  68 72 6f 77 73 01       >ceptionThrows   <
                  ctor args: ()
          
              CustomAttribute #3 (0c000003)
              -------------------------------------------------------
                  CustomAttribute Type: 0a000003
                  CustomAttributeName: System.Diagnostics.DebuggableAttribute :: instance void .ctor(value class DebuggingModes)
                  Length: 8
                  Value : 01 00 07 01 00 00 00 00                          >                <
                  ctor args: (  )
          
          
          AssemblyRef #1 (23000001)
          -------------------------------------------------------
              Token: 0x23000001
              Public Key or Token: b7 7a 5c 56 19 34 e0 89 
              Name: mscorlib
              Version: 4.0.0.0
              Major Version: 0x00000004
              Minor Version: 0x00000000
              Build Number: 0x00000000
              Revision Number: 0x00000000
              Locale: 
              HashValue Blob:
              Flags: [none] (00000000)
          
          
          User Strings
          -------------------------------------------------------
          70000001 : (12) L"Hello World!"
          
          
          Coff symbol name overhead:  0
          ===========================================================
          ===========================================================
          ===========================================================
        • 重點是以下幾個部分:

          • MyProject.exe包含名為「CLR_Via_CSharp_4._0.Program」的TypeDef

            •   TypeDef #1 (02000002)
                -------------------------------------------------------
                    TypDefName: CLR_Via_CSharp_4._0.Program  (02000002)
                    Flags     : [Public] [AutoLayout] [Class] [Sealed] [AnsiClass] [BeforeFieldInit]  (00100101)
                    Extends   : 01000005 [TypeRef] System.Object
                    Method #1 (06000001) [ENTRYPOINT]
                    -------------------------------------------------------
                        MethodName: Main (06000001)
                        ...
                
                    Method #2 (06000002) 
                    -------------------------------------------------------
                        MethodName: .ctor (06000002)
                        ...
            • Program是public、sealed的(Flags)

            • Program從System.Object派出(Extends)

          • Program定義了兩個方法:Main和.ctor(構造器) => Method#1和Method#2

            • Method#1: Main

              •   Method #1 (06000001) [ENTRYPOINT]
                      -------------------------------------------------------
                          MethodName: Main (06000001)
                          Flags     : [Public] [Static] [HideBySig] [ReuseSlot]  (00000096)
                          RVA       : 0x00002050
                          ImplFlags : [IL] [Managed]  (00000000)
                          CallCnvntn: [DEFAULT]
                          ReturnType: Void
                          1 Arguments
                              Argument #1:  SZArray String
                          1 Parameters
                              (1) ParamToken : (08000001) Name : args flags: [none] (00000000)
              • Main是public、static的方法(Flags)

              • 用IL代碼實現(ImplFlags)

              • 返回類型是Void(ReturnType)

              • 有一個參數(1 Arguments)

            • Method#2:.ctor

              •   Method #2 (06000002) 
                      -------------------------------------------------------
                          MethodName: .ctor (06000002)
                          Flags     : [Public] [HideBySig] [ReuseSlot] [SpecialName] [RTSpecialName] [.ctor]  (00001886)
                          RVA       : 0x00002064
                          ImplFlags : [IL] [Managed]  (00000000)
                          CallCnvntn: [DEFAULT]
                          hasThis 
                          ReturnType: Void
                          No arguments.
              • .ctor(構造器)是public的(Flags)

              • 用IL代碼實現(ImplFlags)

              • 返回類型是Void(ReturnType)

              • 無參(No arguments)

              • 有一個this指針(hasThis) => 指向調用方法時構造對象的內存

    • 「檢視」->「統計資料」中可以看到文件大小以及文件各部分大小(字節數和百分比)

      •   File size : 3584PE header size : 512 (496 used) (14.29%)PE additional info : 1415 (39.48%)Num.of PE sections : 3CLR header size : 72 ( 2.01%)CLR meta-data size : 788 (21.99%)CLR additional info : 0 ( 0.00%)CLR method headers : 2 ( 0.06%)Managed code : 27 ( 0.75%)Data : 2048 (57.14%)Unaccounted : -1280 (-35.71%)Num.of PE sections : 3.text - 1024.rsrc - 1536.reloc - 512CLR meta-data size : 788Module - 1 (10 bytes)TypeDef - 2 (28 bytes) 0 interfaces, 0 explicit layoutTypeRef - 6 (36 bytes)MethodDef - 2 (28 bytes) 0 abstract, 0 native, 2 bodiesMemberRef - 6 (36 bytes)ParamDef - 1 (6 bytes)CustomAttribute- 3 (18 bytes)Assembly - 1 (22 bytes)AssemblyRef - 1 (20 bytes)Strings - 276 bytesBlobs - 92 bytesUserStrings - 28 bytesGuids - 16 bytesUncategorized - 172 bytesCLR method headers : 2Num.of method bodies - 2Num.of fat headers - 0Num.of tiny headers - 2Managed code : 27Ave method size - 13

參考書目

  • 《CLR via C#》(第4版) Jeffrey Richter