2013-08-28 80 views
1

我有三年.NET(C#和VB)全職工作經驗。我對MSIL具有良好的工作知識,可以將其用作調試工具。.NET JIT編譯天真

我對編譯過程的下一步沒有太多的瞭解,即當Jitter產生彙編代碼(顯示在拆散窗口中)時。 Hans Passant在這裏發佈了一個問題的答案:What is the difference between native code, machine code and assembly code?。我的經驗更豐富的同事說,這是一個輝煌的答案,但我還是不明白下面的代碼:

static void Main(string[] args) { 
      Console.WriteLine("Hello world"); 
00000000 55    push  ebp       ; save stack frame pointer 
00000001 8B EC    mov   ebp,esp      ; setup current frame 
00000003 E8 30 BE 03 6F call  6F03BE38      ; Console.Out property getter 
00000008 8B C8    mov   ecx,eax      ; setup "this" 
0000000a 8B 15 88 20 BD 02 mov   edx,dword ptr ds:[02BD2088h] ; arg = "Hello world" 
00000010 8B 01    mov   eax,dword ptr [ecx]   ; TextWriter reference 
00000012 FF 90 D8 00 00 00 call  dword ptr [eax+000000D8h]  ; TextWriter.WriteLine() 
00000018 5D    pop   ebp       ; restore stack frame pointer 
     } 
00000019 C3    ret          ; done, return 

任何人都可以提供每行發生了什麼,更具體爲什麼每個寄存器例如選擇的更多信息爲什麼選擇eax代替edx?或者任何人都可以推薦一本書?

回答

3

我對此有點生疏,但我也對低級別的東西感興趣。這裏所說:

push ebp; save stack frame pointer 

推送存儲在EBP壓入堆棧,這樣,當我們從這個方法返回,我們知道我們從哪裏來值。

mov ebp,esp; setup current frame 

將當前堆棧位置值從ESP移到EBP,以便EBP位於當前方法的上下文中。

前面兩行代碼是確保堆棧中存在固定位置(存儲在EBP寄存器中)的約定,用於確定局部變量的相對位置。

call 6F03BE38; Console.Out property getter 

不用猜,這是Console.Out

mov ecx,eax; setup "this" 

從方法返回值的調用存儲在EAX,這是調用約定的問題。因此從Console.Out返回的值將被存儲在EAX中。在這裏,該值被複制到ECX中供以後使用,使EAX可用於其他目的。

mov edx,dword ptr ds:[02BD2088h]; arg = "Hello world" 

寄存器EDX被賦予字符串「Hello World」的內存位置。 dword ptr ds:[02BD2088h]表示取消引用內存位置ds:[02BD2088h],其中dsdata segment(存儲了初始化字符串等內容)。 [02BD2088h]是內存區域ds中的偏移量。

mov eax,dword ptr [ecx]; TextWriter reference 

請記住撥打Console.Out?我們把這個返回的值放到ECX中。這裏,ECX的內存地址被解除引用,以便TextWriter的內存地址被複制到EAX中。所以EAX現在將包含TextWriter對象的實際內存地址。如果我們做了mov eax,dword ptr ecx;那麼EAX將包含指向TextWriter的內存地址的指針,而不是TextWriter的實際內存地址。 (我自己仍然感到困惑)。

call dword ptr [eax+000000D8h]; TextWriter.WriteLine() 

這裏打電話給TextWriter.WriteLine()。我假定TextWriter.WriteLine()使用_fastcall調用約定(這裏可以找到調用約定的一個很好的解釋),這意味着它使用EDX寄存器來查找傳遞給該方法的參數。

pop ebp; restore stack frame pointer 

我們去掉最上面的(或最底層真的像棧向下實際增長)值到EBP,所以在EBP幀指針現在對應於調用方法。

ret 

返回到堆棧頂部找到的位置,返回到調用方法。在這種情況下,調用Main()時,控件將返回到系統代碼,應用程序將退出。

+0

如果我編輯,你做得很好,介意嗎? –

+0

繼續,請隨時:) –

+0

把我的兩美分。 –

1

前兩行用於設置堆棧幀。 EBP寄存器用於存儲當前方法幀的基地址。

push  ebp 

將調用方法幀的基地址保存在堆棧上。這是恢復功能之前與最終ret指令之前,

pop   ebp 

指令退出。

正如評論指出,該

call  6F03BE38 

指令調用Console.Out屬性的getter。這是一個靜態方法,因此當編譯當前方法時,該方法的地址可以直接由JIT插入。

windows上的函數通常使用_stdcall調用約定。調用約定指定參數應該如何傳入函數(通過堆棧或通過寄存器),順序應該傳遞到堆棧(從左到右還是從右到左)以及誰負責清理呼叫後的堆棧(主叫方或被叫方)。由於沒有參數,所以不清楚getter的約定是什麼,但是看起來返回值是放在EAX寄存器中的。

以下三行建立呼叫到TextWriter.WriteLine

線:

mov   ecx,eax 

在EAX移動值到ECX。 EAX包含從Console.Out獲取器返回的值。

mov   edx,dword ptr ds:[02BD2088h] 

的字符串 「Hello World」 的地址移動到EDX寄存器。

mov   eax,dword ptr [ecx] 

副本字的地址指向ECX到EAX。 EAX包含從Console.Out返回的值。由於這是一個引用類型,所以這個值是一個指向存儲在堆上的對象的指針。所有對象都有一個對象頭,由一個同步塊索引和一個指向方法表的指針組成。該參考本身直接指向方法表。因此,[ECX]是該方法將被調用的引用的方法表指針的地址。

最後,該方法被調用

call  dword ptr [eax+000000D8h] 

000000D8h是偏移到對應於TextWriter.WriteLine方法的方法表。

由於this指針和string參數存儲在ECX和EDX中,因此此方法似乎使用_fastcall約定。