1. 程式人生 > >MSIL學習------從HelloWorld開始

MSIL學習------從HelloWorld開始

tsp 關於 托管 tom -a compile tput 知識點 解析

1.有沒有必要學習IL

  前段時間突然想搞搞IL語言,於是在博客園中找到了包建強前輩關於IL的文章學習,並且在包前輩博客裏看到了09年他與趙劼前輩關於是否有必要學習IL語言的爭論,作為一個剛入此行業的新人,沒有站在那個高度不敢去評論什麽,並且我的引路教員在知道我學IL時就跟我說學習IL還不如學習匯編,IL語言就是一堆指令,誰背的多誰就越精通,我那個教員說的也不錯,IL語言就是一堆指令,或許就是站的角度不同,我教員他不止局限於.NET,對C++和匯編都有一定研究,但是現在我還是只局限於.NET體系,學好.NET我感覺對於CIL和CLR一定得有一定的了解。所以我個人的觀點是在.NET平臺幹活的人還是有必要學習學習IL的。現在IL我只是局限於剛學習階段,所以想寫下博客來記錄我的學習記錄

2.反編譯解析HelloWorld

  學習IL,首先需要知道其各種指定的含義,所以需要先創建c#語言進行反編譯來解析,在這裏只需創建一個.CS類並使用命令進行編譯與反編譯即可,沒必要啟動宇宙第一IDE(VS)

技術分享圖片
using System;
namespace HelloWorld
{
    class HelloWorld
    {
        public static void Main(String[] args)
        {
            System.Console.WriteLine("HelloWorld");
        }
    }
}
View Code

  然後需要將.CS文件編譯成.EXE文件,在這裏需要什麽VS的開發者工具(當然應該還有其它方式),

  使用SCS語句進行編譯

csc HelloWorld.cs

  接下來使用ILDASM命令進行反編譯為IL文件

ildasm HelloWorld.exe /output=HelloWorld.il

  然後就會生成一個.IL文件,這個文件進行HelloWorld.exe反編譯後的代碼

技術分享圖片
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                
  .ver 
4:0:0:0 } .assembly HelloWorld { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) .hash algorithm 0x00008004 .ver 0:0:0:0 } .module HelloWorld.exe .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 .corflags 0x00000001 .class private auto ansi beforefieldinit HelloWorld.HelloWorld extends [mscorlib]System.Object { .method public hidebysig static void Main(string[] args) cil managed { .entrypoint .maxstack 8 IL_0000: nop IL_0001: ldstr "HelloWorld" IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: nop IL_000c: ret } .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } }
View Code

  下面來簡單學習下這個IL代碼

  • 可以看到IL中有需要 .assembly .module .class 這樣規則的代碼,它們是定義信息的偽指令,IL語言不像C#聲明類似class時先寫修飾符之類,而是首先定義聲明的偽指令,先來看看每個偽指令的含義
  1. .assembly extern [assemblyRefName] {} [可選]  定義一個AssemblyRef(程序集引用)的元數據項,標記了這個程序使用的外部托管應用程序,類似using語句 mscorlib.dll:.NET程序集類庫的主程序集。
  2. .assembly [assemblyName] {}    定義一個程序集的元數據項,如果一個不定義此項,這個文件就不完全是一個應用程序,無法獨立指定
  3. .module [moduleName] 定義一個模塊元數據項,標識了當前的模塊.
  4. .class  定義一個TypeDef(類聲明)的元數據項
  5. .method  定義一個MethodDef(方法聲明)的元數據項
  • 代碼中像assembly和module只需要定義好就行,主要需要關註的還是class和method,當然還有field。下面再來介紹下修飾與class和method中的關鍵字

  1.class

  1. private  訪問修飾符,沒什麽好說的,IL支持6種訪問修飾符,C#7.2版本才加入第六種(private protected)
  2. auto [可選]  定義類的布局風格,auto是自動布局(默認值),只加載程序時可以使用它認為合適的方式進行布局 其它布局風格有sequential(加載程序時保留實例字段的順序)和explicit(顯示指定類型布局)
  3. ansi [可選]  定義類中字符串與其它非托管代碼進行操作時的轉換模式, ansi指定了會與”標準“C風格的字節字符串進行轉換(默認值),其它有unicode(與UTF-16字符進行轉換)和autochar(有底層平臺定義的默認字符串轉換)
  4. beforefieldinit [可選]  指靜態成員在第一次訪問之前被初始化
  5. extends 繼承 也沒什麽好說的

     另外可以看到C#中寫的命名空間在IL中直接變成了一個完整類名。這是IL2.0時引入的,

  2.method

  1. public 訪問修飾符
  2. hidebysig  用於隱藏父類的同名方法,類似於C#的new關鍵字
  3. specialname  提示編譯器和工具這個函數時特殊的, 只存在與構造函數(.ctor)和靜態構造函數(.cctor)中
  4. rtspecialname  告訴運行時這個函數時特殊的  只存在與構造函數(.ctor)和靜態構造函數(.cctor)中
  5. static/instance  static 聲明這個函數時靜態函數 instance:聲明這個函數時實例函數
  6. cil managed  聲明這個函數時CIL代碼

  了解了IL代碼整體結構後接下來來看下方法,方法體中通常包含三項:指令,標註了指令的標號和偽指令(在方法體外只有偽指令),在方法中像.entrypoint.maxstack這類是偽指令,nop,ldstr這屬於指定,而IL_0000屬於指令標號,指令標號作用是跳轉時使用,所以自己寫代碼時沒必要每行都加,只有在需要時加入即可,另外標號不會對偽指令進行標註

  在上面C#代碼中只定義了一個Main方法,但是在IL文件中卻存在兩個方法,其中一個方法就是定義的Main方法,而另一個則是C#編輯器加上的默認構造函數(.ctor),從這裏可以看出C#的一個知識點(未添加構造函數C#會自動添加一個默認構造函數)

構造函數在此不介紹,只說一下Main方法中的內容,

    .entrypoint和.maxstack是兩個偽指令,它們的作用分別是.

      .entrypoint:將定義此偽指令的方法標識為應用程序入口方法,也就是說在IL中程序入口並不是方法名稱為Main的,

      .maxstack:棧中存在的最大數量數據,比如Main方法maxstack=8則說明stack中最多容納8個數據。(IL棧元素不是字節或字,而是槽,當談論IL棧深度時,指的是放在棧中的項,而不考慮項的大小)

    nop  指令代表如果修補操作碼,則填充空間,但時是並不執行任何有意義的操作

  ldstr  代表加載一個字符串到棧頂

    call   方法調用指令,還有另一個方法調用指令為callvirl,在IL中調用方法使用是“::” 而不是C#中的“.”,並且調用前要先聲明其返回值類型和參數並不是C#的那種實參變量而是參數的類型,因為IL是一種嚴格基於棧的語言,方法時會按照參數列表去棧頂進行獲取數據,調用完成後如果有返回值也會將返回值放入棧頂

    ret  從當前方法返回,並將返回值(如果存在)放入調用方的計算棧中

3.編寫一個IL語言的HelloWorld

  通過上面的解析可以看出手寫一個簡單的IL語言的HelloWorld其實挺簡單,只需要依葫蘆畫瓢就可以

技術分享圖片
.assembly extern mscorlib{ auto}
.assembly HelloWorld_IL{}
.module HelloWorld.HelloWorld_IL.exe 
.class private auto ansi HelloWorld.Program extends [mscorlib]System.Object
{
    .method public hidebysig rtspecialname specialname instance void .ctor()cil managed
    {
        ldarg.0
        call instance void [mscorlib]System.Object::.ctor()
        ret 
    }
    .method public hidebysig static void MyMain()cil managed
    {
        .entrypoint
        .maxstack 1
        ldstr "HelloWorld"
        call void [mscorlib]System.Console::WriteLine(string)
        ret 
    }
}
View Code

  在上面代碼中程序集引用中使用的是auto,這是IL2.0版本加入,會自動搜索指定名稱程序集

  然後在開發者工具使用命令進行編輯

ilasm HelloWorld_IL.il /output=HelloWorld_IL.exe

  然後指定就可以看出HelloWorld輸出

 技術分享圖片

    

  

MSIL學習------從HelloWorld開始