(翻譯)《Expert .NET 2.0 IL Assembler》 第二章 代碼加強 2.1 代碼壓縮

來源:互聯網
上載者:User

返回目錄

 

代碼壓縮

     出現在前面章節的程式碼範例是壓縮的。如果你不相信我,進行一個簡單的實驗:用你喜歡的進階別Microsoft .NET語言寫一個類似的應用程式,將它編譯為一個可執行體(並保證它運行!),反編譯這個可執行體,並將這個結果與第一章提供的樣本進行比較。現在讓我們試著使這段代碼更加簡潔。

     首先,假定你瞭解欄位對應和作為預留位置的實值型別,我不需要繼續介紹這種技術。如果sscanf接受string作為第一個參數,它也會接受string作為它的第二個參數。其次,我可以在IL指令集中使用特定的“捷徑”(我將稍候在本章討論)。

     讓我們看一下這個簡單的樣本,並輕量的修改(源檔案Simple.il),正如清單2-1所示。此刻,我感興趣的部分是改動的部分。

 

清單2-1 修改過的OddOrEven應用程式範例

//----------- Program header
.assembly extern mscorlib { auto }
.assembly OddOrEven { }
.module OddOrEven.exe
//----------- Class declaration
.namespace Odd.or {
     .class public auto ansi Even

               extends [mscorlib]System.Object {
//----------- Field declaration
     .field public static int32 val
//----------- Method declaration
     .method public static void check( ) cil managed {
          .entrypoint
          .locals init(int32 Retval)
     AskForNumber:
          ldstr "Enter a number"
          call void [mscorlib]System.Console::WriteLine(string)
          call string [mscorlib]System.Console::ReadLine()
          ldstr "%d" // CHANGE!
          ldsflda int32 Odd.or.Even::val

            call vararg int32 sscanf(string, string, , int32*) // CHANGE!
          stloc.0 // CHANGE!
          ldloc.0 // CHANGE!
          brfalse.s Error // CHANGE!
          ldsfld int32 Odd.or.Even::val
          ldc.i4.1 // CHANGE!
          and
          brfalse.s ItsEven // CHANGE!
          ldstr "odd!"
          br.s PrintAndReturn // CHANGE!
     ItsEven:
          ldstr "even!"
          br.s PrintAndReturn // CHANGE!
     Error:
          ldstr "How rude!"
     PrintAndReturn:
          call void [mscorlib]System.Console::WriteLine(string)
          ldloc.0 // CHANGE!
          brtrue.s AskForNumber // CHANGE!
          ret
     } // End of method
     } // End of class
} // End of namespace
//----------- Calling unmanaged code
.method public static pinvokeimpl("msvcrt.dll" cdecl)
     vararg int32 sscanf(string, string) cil managed { }

     這個程式頭、類聲明、欄位聲明和方法頭開上去是一樣的。第一個變動來自方法體,在全域欄位Format的地址載入的地方,被載入中繼資料字串常量替代:ldstr "%d"。正如先前提及的,你可以在sscanf方法調用的第二個參數中,放棄聲明和使用ANSI字串常量而使用中繼資料字串常量(內部用Unicode表示),依賴於P/Invoke提供的封送機制來做這個必需的裝換工作。

     因為我不再使用ANSI字串常量、全域欄位Format的聲明、作為該欄位類型的實值型別預留位置、以及該欄位對應到的資料,都被省略了。正如你確實看到的,我不需要使用ILAsm顯示聲明一個中繼資料字串常量——在原始碼中唯一提及到的這個常量,對於ILAsm編譯器自動省略這個中繼資料項是足夠的了。

     改變了sscanf方法調用的第二個參數的本性後,我需要改變sscanf這個P/Invoke thunk的簽名,從而能夠提供必要的封送。因此,你會看到sscanf簽名的改變,包括方法聲明和調用方。

     另一組變動產生於本地變數的載入/儲存指令ldloc Retval和stloc Retval分別被ldloc.0和stloc.0代替。IL定義了特殊的作業碼用來載入/儲存清單中第一組編號從0到3的四個本地變數。這麼做是有好處的,因為這個指令的常規形式(ldloc Retval)編譯在作業碼(ldloc)中,以一個不帶正負號的整數(這裡是0)作為本地變數的索引,而ldloc.n指令編譯在沒有參數的單位元組作業碼中。

     你可能注意到,check方法中所有的分支指令(br,brfalse和brtrue)都被他們各自的簡寫形式(br.s,brfalse.s和brtrue.s)取代。指令的標準(長)形式編譯到緊跟著一個4位的參數作業碼中(在分支指令的情形中,位移當前的位置),反之,一個短形式編譯到緊跟著一個1位的參數的作業碼中。這就限制了分支的範圍,從IL流的當前指標向後最大128位,向前127位,但是在此情形中,你可以安全切換到短形式,因為方法相當的小。

     帶有一個整數或不帶正負號的整數參數的短形式,是為IL指令的所有類型定義的。因此,即使你聲明的變數多於4個,你仍能夠節省一些位元組——通過使用ldloc.s和stloc.s指令來取代ldloc和stloc,只要本地變數的索引值不超過255。

     進階語言編譯器,省略了IL代碼,自動估計範圍和選擇在每個特定情形中使用長形式或短形式的指令。ILAsm編譯器,當然不會對排序有什麼影響。如果你指定了長形式或短形式的指令,編譯器會擷取它的表麵價值——你是老闆,而且你被認為瞭解的更好。但是一旦你指定了一個短形式分支指令並講目標標籤放在了範圍之外,ILAsm編譯器就會診斷出一個錯誤。

     有一次,我的一位同事到我這裡抱怨IL編譯器明顯不能編譯ILDASM產生的程式碼。反編譯器和編譯器被假定為工作決定一致的,因此我被這個發現極大地震驚了。一個簡短的研究揭開了嚴酷的真相。在設計一個用於自動化的測試程式產生的特殊方法的過程中,我的同事編譯C#和VB.NET寫的初始程式,然後反編譯產生的可執行體,插入特定於測試的ILAsm部分,然後重新編譯這段修改過的代碼,成為新的執行體,在這個初始的可執行體中的方法,是由C#和VB.NET編譯器產生的,是非常小的,因此反編譯器會省略這個小的分支語句,這就是在反編譯中所顯示的。每次我同事的自動化工具在簡短分支指令和其目的地之間插入相當多額外的ILAsm代碼,這個分支指令,象徵性地,就和他的目的地說再見了。

     注意到樣本中的另一個改變是:指令ldc.i4 1被ldc.i4.1代替。這裡的邏輯和用ldloc Retval代替ldloc.0一樣——換句話說,一個作業碼的捷徑而不需要1個4位元組的整型參數。這種捷徑ldc.i4.n,n的值從0到8,而-1可以通過ldc.i4.m1這樣的代碼載入。ldc.i4指令的簡寫形式為:ldc.i4.s,s的位元組整數範圍從-128到127。

     現在從Apress網站複製Simple.il源檔案,使用控制台命令ilasm simple1將其編譯成一個Simple1.exe可執行檔,並保證它可以如同Simple.exe一樣運行。接著並行反編譯這兩個可執行檔,使用控制台命令ildasm simple.exe /bytes和ildasm simple1.exe /bytes(/bytes選項使反編譯器顯示準確的組成IL流程的位元組值)。在ILDASM視圖樹中尋找這兩個執行個體的check方法,雙擊開啟反編譯表單,在這個表單中,你可以比較同一個方法的兩種實現,從而看到代碼壓縮是否在工作。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.