.Net8罕見的技術:MSIL的機器碼簡析

2023-06-09 18:01:26

前言

一般的只有最終的組合程式碼才有機器碼錶示,然一個偶然的機會發現,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)上,歡迎關注