公共語言運行時
公共語言運行時(Common Language Runtime,CLR)是一個可由多種編程語言使用的「運行時」
「運行時」!=「程序運行的時候」
「源代碼」-> 「中間碼」(IL)-> 「虛擬機」管理和執行IL
「虛擬機」可以參與和管理程序代碼的執行,解决了垃圾内存回收、安全性檢查等問題
這個「虛擬機」在.NET框架中就是「運行時」(公共語言運行時,CLR)
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頭
-
CLR頭包含模塊生成時所面向的CLR的major(主)和minor(次)版本號
-
托管模塊入口方法MethodDef元數據token
-
模塊的元數據、資源、強名稱、標志及其他的數據項的位置/大小,如:
- 指定模塊入口方法的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
-
-
-
元數據表
-
描述源代碼中定義的類型和成員的表
-
描述源代碼引用的類型和成員的表
-
-
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) => 指向調用方法時構造對象的內存
- 再選擇「檢視」->「MetaInfo」->「顯示!」,打開查看應用程序的元信息
「檢視」->「統計資料」中可以看到文件大小以及文件各部分大小(字節數和百分比)
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