2012-10-01 87 views
6

我剛開始注意IL,我很好奇如果我的嘗試(如下所示)從編譯器的輸出中刪除多餘的代碼有任何意外的副作用。關於基於反彙編簡單C#代碼的手編IL的問題

一個關於結果的情侶quesiton的:

  1. 什麼是在原有的NOP操作的目的是什麼?
  2. brs在原始方法結束時的目的是什麼?
  3. 重寫的版本有什麼不妥之處?

原始C#代碼:

class Program { 
    public static int Main() { 
     return Add(1, 2); 
    } 
    public static int Add(int a, int b) { 
     return a + b; 
    } 
} 

編譯時csc.exe並用ildasm.exe(原始)拆開它:

.method public hidebysig static int32 Main() cil managed 
    { 
    .entrypoint 
    .maxstack 2 
    .locals init (int32 V_0) 
    IL_0000: nop 
    IL_0001: ldc.i4.1 
    IL_0002: ldc.i4.2 
    IL_0003: call  int32 Program::Add(int32, int32) 
    IL_0008: stloc.0 
    IL_0009: br.s  IL_000b 
    IL_000b: ldloc.0 
    IL_000c: ret 
    } 
    .method public hidebysig static int32 Add(int32 a, 
              int32 b) cil managed 
    { 
    .maxstack 2 
    .locals init (int32 V_0) 
    IL_0000: nop 
    IL_0001: ldarg.0 
    IL_0002: ldarg.1 
    IL_0003: add 
    IL_0004: stloc.0 
    IL_0005: br.s  IL_0007 
    IL_0007: ldloc.0 
    IL_0008: ret 
    } 

重新編寫(產生相同的輸出):

.method public hidebysig static int32 Main() cil managed 
    { 
    .entrypoint 
     .maxstack 2 
    ldc.i4.1 
    ldc.i4.2 
    call int32 Program::Add(int32, int32) 
    ret 
    } 

    .method public hidebysig static int32 Add(int32 a, int32 b) cil managed 
    { 
    .maxstack 2 
     ldarg.0 
     ldarg.1 
    add 
    ret 
    } 
+4

nop「no operation」是您可以在調試過程中設置斷點的位置。嘗試編譯發佈。 – Johnny

回答

6

你看到所有的「多餘」的代碼是特定於調試版本(通常被優化掉的發佈版本),並允許您執行的東西,你不能在發行版本通常做。

調試構建代碼是這樣的,它允許在調試會話期間最大限度地獨立設置斷點和更改/檢查堆棧值。此外,IL代碼應儘可能模仿較高級別的代碼,以便每個「原因」和「效果」可映射到更高級別的代碼行。

現在要具體到你的問題:

什麼是在原有的NOP操作的目的是什麼?

NOP允許您在未執行的地方設置斷點。對於例如方法,循環或if語句的大括號。在這些不可執行的指令中,打開大括號允許您在塊啓動之前修改/檢查堆棧(儘管承認您可以非常容易地通過在塊的第一行中執行而不是打開大括號來實現此操作,但它很容易實現仍然可以讓你獨立破開大括號)

br.s在最初的方法的最終目的是什麼?

綜觀原代碼,你會發現它是沒有意義的,以「跳」到下一行,而不是允許代碼自然「秋天」到下一行。但它讀作:

「在調試版本,每當一個方法需要返回,跳轉到方法的結尾,讀取堆棧中的返回值,然後返回值」

那麼做有什麼優勢它提供了調試?

如果您的代碼中有多個return語句,它們將在從堆棧讀取返回值之前'跳轉'到代碼的末尾。這可以讓您準確地找到一個位置(方法的大括號),您可以在該位置放置一個斷點並在實際返回到調用方法之前修改返回值。相當有用,不是嗎?

重寫的版本有什麼不妥之處嗎?

沒有什麼不當在你的代碼。事實上,如果您在發佈模式下構建原件並檢查生成的CIL,您會注意到它與您的CIL大部分相同。

2

聲明:我不是無論如何都是IL專家。

  1. nop操作的目的是什麼?

    有一個很大的討論關於x86 ASM在programmers.stackexchange.com方面的一些討論,請參閱這裏:Purpose of NOP instruction and align statement in x86 assembly。它基本上是一樣的。

  2. br.s在原始方法結束時的目的是什麼?

    這只是該方法結束的一個分支。如果你在這個函數中有多個返回路徑,那麼看看會更有意義。就目前而言,編譯器已經包含了它,而不是優化它(可能編譯器開關會優化它)。

  3. 重寫的版本有什麼不妥之處嗎?

    不是我能看到的。您剛剛剝離了編譯器的大部分工作,這對於這樣一個簡單的應用程序來說並不是必需的。如果你想對這段代碼做進一步的補充,那麼在這裏需要額外的IL來完成它的任務。

+0

我認爲IL中的NOP不能真正被惡意地用於你所描述的答案。使用IL,編譯器在JIT編譯之前驗證代碼,我相信也沒有辦法跳轉到可以跳轉到一系列地址的地方。 – svick

+0

@svick雖然這是真的.. theres在該線程上的其他信息豐富:) –