一般的只有最終的組合程式碼才有機器碼錶示,然一個偶然的機會發現,MSIL(Microsoft intermediate language)作為一箇中間語言表示,居然也有機器碼,其實這也難怪,計算機裡面萬物都是二進位制,本篇來看下,以下以.Net8 PreView Source Code分析為主。原文:在此處
1.C# And IL
先上C#程式碼:
static void Main()
{
Program pm=new Program();
GC.Collect();
Console.WriteLine("CeShi JITDUMP");
Console.ReadLine();
}
非常簡單的一段程式碼,把這段程式碼編譯後的DLL匯入到微軟官方的ILDASM工具裡面去,可以看到如下程式碼:
.method private hidebysig static void Main() cil managed
{
.entrypoint
// 程式碼大小 28 (0x1c)
.maxstack 8
IL_0000: newobj instance void Program::.ctor()
IL_0005: pop
IL_0006: call void [System.Runtime]System.GC::Collect()
IL_000b: ldstr "CeShi JITDUMP"
IL_0010: call void [System.Console]System.Console::WriteLine(string)
IL_0015: call string [System.Console]System.Console::ReadLine()
IL_001a: pop
IL_001b: ret
} // end of method Program::Main
這裡注意下標號IL_0000的那段程式碼:
IL_0000: newobj instance void Program::.ctor()
以此為例子作為觀察。
2.JIT Import IL
來看下JIT把這段IL程式碼匯入後的一個情況
IL to import:
IL_0000 73 04 00 00 06 newobj 0x6000004
IL_0005 26 pop
IL_0006 28 0e 00 00 0a call 0xA00000E
IL_000b 72 01 00 00 70 ldstr 0x70000001
IL_0010 28 0f 00 00 0a call 0xA00000F
IL_0015 28 10 00 00 0a call 0xA000010
IL_001a 26 pop
IL_001b 2a ret
注意到JIT匯入這段IL程式碼之後,多了機器碼,多了十六進位制的表示。以IL_0000段程式碼為例
匯入之前:
IL_0000: newobj instance void Program::.ctor()
這裡newobj之前沒有機器碼,newobj之後是呼叫了函數instance void Program::.ctor()。
匯入之後:
IL_0000 73 04 00 00 06 newobj 0x6000004
這裡很明顯看到變化,newobj之前有一連串的機器碼:73 04 00 00 06。newobj之後,則有十六進位制0x6000004取代了上面的函數呼叫:instance void Program::.ctor()。
3.分析
那麼IL裡面的這些機器碼和十六進位制數值是幹什麼用的呢?
首先看下機器碼:73 04 00 00 06。一個個的看。
最先的0x73,它表示的是:newobj的機器碼。它的原型是:
OPDEF(CEE_NEWOBJ, "newobj",VarPop,PushRef,InlineMethod, IObjModel, 1,0xFF,0x73,CALL)
後面的04 00 00 06這四個位元組的機器碼可以看做一個整體,小端取值那麼它的值是:6000004。
那麼這個6000004到底表示什麼東西呢?通過ILDASM的快捷鍵Ctrl+M開啟後設資料資訊,裡面可以看到6000004表示的就是.ctor函數的後設資料描述,它的原型如下:
Method #2 (06000004)
-------------------------------------------------------
MethodName: .ctor (06000004)
Flags : [Public] [HideBySig] [ReuseSlot] [SpecialName] [RTSpecialName] [.ctor] (00001886)
RVA : 0x00002084
ImplFlags : [IL] [Managed] (00000000)
CallCnvntn: [DEFAULT]
hasThis
ReturnType: Void
No arguments.
那麼這段程式碼
IL_0000 73 04 00 00 06 newobj 0x6000004
的整體意思就很清楚了,73 04 00 00 06裡面的73是表示newobj,後面的04 00 00 06表示呼叫.ctor非靜態建構函式。它實際上跟ILDASM裡面顯示的IL程式碼是同一個意思,但是因為在記憶體裡面操作,所以它只能是十六進位制或者二進位制,JIT匯入的時候只不過把字母的含義替換成了具體數位的含義。其它的IL程式碼依次類推。
作者:江湖評談
文章首發在公眾號(jianghupt)上,歡迎關注