1. 程式集和CIL: 程式集是由.NET語言的編譯器接受源代碼文件產生的輸出文件,通常分為 exe和dll兩類,其中exe包含Main入口方法可以雙擊執行,dll則需要被其他程式集調用執行。 CIL(Common Intermediate Language): 公共中間語言①,需要被編譯成二進位機 ...
1. 程式集和CIL:
- 程式集是由.NET語言的編譯器接受源代碼文件產生的輸出文件,通常分為 exe和dll兩類,其中exe包含Main入口方法可以雙擊執行,dll則需要被其他程式集調用執行。
- CIL(Common Intermediate Language): 公共中間語言①,需要被編譯成二進位機器碼之後才會被電腦執行。
2. 程式集包含:
- 程式的CIL
- 程式中使用的類型的元數據(metadata)
- 程式集清單
- 一些資源集
程式被編譯成程式集(exe為例)之後,雙擊運行,程式集會被載入入CLR,CLR執行下麵的步驟:
1.檢查程式集的安全特性。
2.進行記憶體分配。
3.把程式集中的可執行代碼發送給JIT(Just-in-Time)編譯器,把其中的一部分代碼編譯成為本機代碼。
其中,JIT只會編譯被調用的部分CIL代碼,並把編譯的結果緩存起來,以備在後面的程式中的多次調用。這保證了編譯與運行的效率。
經過JIT編譯之後的代碼即是本機代碼,本機代碼最終被CPU執行。
我們通過一段簡單的代碼來加深理解:
1. 打開VS,用C#編寫一段如下程式:
using System;
namespace ILTest
{
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Hello Fred");
Console.Read();
}
}
}
2. 使用 ILASM② 工具將程式集反編譯為IL(也可生成為ILTest.txt,尾碼名不影響文本文件內容):
ildasm ILTest.exe /output:ILTest.IL
生成文本文件如下:
1 // Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0
2
3 // Metadata version: v4.0.30319
4 .assembly extern mscorlib
5 {
6 .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
7 .ver 4:0:0:0
8 }
9 .assembly ILTest
10 {
11 .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
12 .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
13 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.
14
15 // --- 下列自定義特性會自動添加,不要取消註釋 -------
16 // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 )
17
18 .custom instance void [mscorlib]System.Reflection.AssemblyTitleAttribute::.ctor(string) = ( 01 00 06 49 4C 54 65 73 74 00 00 ) // ...ILTest..
19 .custom instance void [mscorlib]System.Reflection.AssemblyDescriptionAttribute::.ctor(string) = ( 01 00 00 00 00 )
20 .custom instance void [mscorlib]System.Reflection.AssemblyConfigurationAttribute::.ctor(string) = ( 01 00 00 00 00 )
21 .custom instance void [mscorlib]System.Reflection.AssemblyCompanyAttribute::.ctor(string) = ( 01 00 00 00 00 )
22 .custom instance void [mscorlib]System.Reflection.AssemblyProductAttribute::.ctor(string) = ( 01 00 06 49 4C 54 65 73 74 00 00 ) // ...ILTest..
23 .custom instance void [mscorlib]System.Reflection.AssemblyCopyrightAttribute::.ctor(string) = ( 01 00 12 43 6F 70 79 72 69 67 68 74 20 C2 A9 20 // ...Copyright ..
24 20 32 30 31 38 00 00 ) // 2018..
25 .custom instance void [mscorlib]System.Reflection.AssemblyTrademarkAttribute::.ctor(string) = ( 01 00 00 00 00 )
26 .custom instance void [mscorlib]System.Runtime.InteropServices.ComVisibleAttribute::.ctor(bool) = ( 01 00 00 00 00 )
27 .custom instance void [mscorlib]System.Runtime.InteropServices.GuidAttribute::.ctor(string) = ( 01 00 24 38 34 66 35 62 34 30 65 2D 39 31 61 65 // ..$84f5b40e-91ae
28 2D 34 62 66 63 2D 61 62 38 39 2D 34 61 30 66 66 // -4bfc-ab89-4a0ff
29 66 36 64 30 38 31 61 00 00 ) // f6d081a..
30 .custom instance void [mscorlib]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 31 2E 30 2E 30 2E 30 00 00 ) // ...1.0.0.0..
31 .custom instance void [mscorlib]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = ( 01 00 1C 2E 4E 45 54 46 72 61 6D 65 77 6F 72 6B // ....NETFramework
32 2C 56 65 72 73 69 6F 6E 3D 76 34 2E 36 2E 31 01 // ,Version=v4.6.1.
33 00 54 0E 14 46 72 61 6D 65 77 6F 72 6B 44 69 73 // .T..FrameworkDis
34 70 6C 61 79 4E 61 6D 65 14 2E 4E 45 54 20 46 72 // playName..NET Fr
35 61 6D 65 77 6F 72 6B 20 34 2E 36 2E 31 ) // amework 4.6.1
36 .hash algorithm 0x00008004
37 .ver 1:0:0:0
38 }
39 .module ILTest.exe
40 // MVID: {90543B0E-D1B4-4FFF-9260-57E27FBC4F8B}
41 .imagebase 0x00400000
42 .file alignment 0x00000200
43 .stackreserve 0x00100000
44 .subsystem 0x0003 // WINDOWS_CUI
45 .corflags 0x00020003 // ILONLY 32BITPREFERRED
46 // Image base: 0x00960000
47
48
49 // =============== CLASS MEMBERS DECLARATION ===================
50
51 .class public auto ansi beforefieldinit ILTest.Program
52 extends [mscorlib]System.Object
53 {
54 .method public hidebysig static void Main(string[] args) cil managed
55 {
56 .entrypoint
57 // 代碼大小 19 (0x13)
58 .maxstack 8
59 IL_0000: nop
60 IL_0001: ldstr "Hello Fred"
61 IL_0006: call void [mscorlib]System.Console::WriteLine(string)
62 IL_000b: nop
63 IL_000c: call int32 [mscorlib]System.Console::Read()
64 IL_0011: pop
65 IL_0012: ret
66 } // end of method Program::Main
67
68 .method public hidebysig specialname rtspecialname
69 instance void .ctor() cil managed
70 {
71 // 代碼大小 8 (0x8)
72 .maxstack 8
73 IL_0000: ldarg.0
74 IL_0001: call instance void [mscorlib]System.Object::.ctor()
75 IL_0006: nop
76 IL_0007: ret
77 } // end of method Program::.ctor
78
79 } // end of class ILTest.Program
80
81
82 //
View Code
其中包含了程式的元數據,程式集清單和一些其他資源信息。
它們描述並組成了這段程式的類型信息,安全信息,版本信息以及對其它程式集的引用信息等,使得程式集擁有了自我描述的特性。其中,元數據是反射得以實現的重要條件。
除此之外還包含了IL代碼,IL是經過編譯器(這裡是csc)編譯產生的中間語言代碼。
我們可以通過修改IL代碼來控製程序的執行:
打開生成的文本文件,將Main方法中的輸出字元串修改為"Hello Tommy":
使用ILASM工具將IL文件重新編譯成ILTest2.exe:
雙擊運行exe結果如下:
也許你會覺得納悶,為什麼要把源代碼先翻譯成CIL再翻譯成本機代碼,而不是一步到位呢??
當程式被編譯成程式集之後就脫離了語言的限制,.NET平臺下的語言通過程式集的調用突破了語言的阻礙。
例如C#程式可以調用VB生成的程式集,將語言的特殊性轉換成了CIL這一通用且規範的概念,好比全國各個地方的人講著不同的方言,彼此之間難以溝通,但是先講方言翻譯成了CIL之一普通話,從此便消除了語言的障礙。
除此之外,如果直接從高級語言編譯成機器語言,不同廠商生成的CPU讀取不同的指令,如果有x門高級語言,有y種讀取不同指令集的CPU,那麼需要有x*y種編譯器去將不同的語言與CPU指令一一匹配。
有了CIL之後,我們只需x種編譯器將高級語言轉換成CIL,再經過y種編譯器將CIL轉換成二進位指令。 一共僅需要x+y種不同的編譯器。
原創文章,未經允許禁止轉載。
註:①公共中間語言在一些地方也被叫做MSIL(Microsoft Intermediate Language)或IL(Intermediate Language)。本文中的CIL,IL,MSIL指的都是公共中間語言這一概念。
②ILASM和ILDASM工具詳見:here