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+頭:

      1. Windows PE文件頭:

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

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

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

      3. 與本機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 Kits\NETFXSDK\4.8\Include\um中

          • 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 : 3584
          PE header size : 512 (496 used) (14.29%)
          PE additional info : 1415 (39.48%)
          Num.of PE sections : 3
          CLR 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 - 512
          CLR meta-data size : 788
          Module - 1 (10 bytes)
          TypeDef - 2 (28 bytes) 0 interfaces, 0 explicit layout
          TypeRef - 6 (36 bytes)
          MethodDef - 2 (28 bytes) 0 abstract, 0 native, 2 bodies
          MemberRef - 6 (36 bytes)
          ParamDef - 1 (6 bytes)
          CustomAttribute- 3 (18 bytes)
          Assembly - 1 (22 bytes)
          AssemblyRef - 1 (20 bytes)
          Strings - 276 bytes
          Blobs - 92 bytes
          UserStrings - 28 bytes
          Guids - 16 bytes
          Uncategorized - 172 bytes
          CLR method headers : 2
          Num.of method bodies - 2
          Num.of fat headers - 0
          Num.of tiny headers - 2
          Managed code : 27
          Ave method size - 13

參考書目

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