MSIL 教程(一)

在网上发现了一个非常好的MSIL教程,可惜是英文版的,于是就翻译了一下,与大家共享,
原文http://www.codeguru.com/Csharp/.NET/net_general/il/article.php/c4635,初次翻译文章,请大家指正。


介绍
微软中间语言 (MSIL) 是一种语言,是许多编译器 (C#,VB.NET ) 的输出 . ILDasm ( 中间语言反汇编器 ) 程序和 .Net Framework SDK(FrameworkSDK/Bin/ildasm.exe) 打包在一起,让用户以人可阅读的格式查看 MSIL 代码。通过该工具,我们可以打开任何 .net 可执行文件 (exe dll) 并查看其 MSIL 代码。
ILAsm程序(中间语言编译器)MSIL语言生成可执行文件。我们可以在WINNT / Microsoft.NET /Framework /vn.nn.nn目录中找到这个程序。
许多Visual C++程序员开始.net开发是因为他们对.NET框架的底层发生了一些什么感兴趣。学习MSIL给了用户理解某些对C#程序员或VB.NET程序员来说是透明的东西的机会。通晓MSIL给.NET程序员更多的能力。我们从不需要直接用MSIL编写程序,但是在某些情况下是非常有用的,我们可以用ILDasm打开程序的MSIL代码,查看它到底做了一些什么。
一个 Doc 格式的 MSIL 参考对 .NET 开发人员来说比较有用,它也许可以在 Framework SDK 目录下找到:
  • FrameworkSDK/Tool Developers Guide/docs/Partition II Metadata.doc (元数据定义和术语). 在这个文件中,我发现了所有MSIL指令的说明,例如.entrypoint, .locals等.
  • FrameworkSDK/Tool Developers Guide/docs/Partition III CIL.doc (CIL命令集)包含了一个MSIL命令的完整列表。
在工作中,我也用到了一个MSDN的ILDAsm教程,一篇2001年5月由 John Robbins发表在MSDN杂志的优秀的文章: "ILDASM is Your New Best Friend"
我想学习一门语言最好的途径就是用它写一些程序,所以我决定写一些小的 MSIL 程序。实际上,我们有写这些代码——是 C# 编译器生成的,我只是做一了一些小的更改,并加了许多注释以描述 MSIL 是如何工作的。
通过阅读附在本文的例子可以帮助.NET程序员理解中间语言,帮助其在需要的时候更易读懂MSIL代码。

一般信息
MSIL 中,所有的操作都在栈上完成。当调用一个函数的时候,其参数和局部变量都被分配到栈上。函数的代码从该栈开始,把一些值压入栈,对这些值进行一些操作,从栈上取出值。
执行 MSIL 名利和函数由 3 个步骤完成:
1.      把命令操作数和函数参数压入栈。
2.      执行命令或者调用函数。命令或函数从栈中取出他们的操作数(参数)并把他们压入结果栈 (返回值)。
3.      从栈中读取结果值。
步骤 1~3 是可选的,例如, void函数不会压入一个结果值到栈。
栈包含值类型对象和引用类型对象的引用。引用类型对象本身保存在堆中。
用来把一个值压入栈中的MSIL命令是ld... (装载),用来从栈中取出值的命令是st... (存储),因为值都存在变量中。我们可以把入栈操作叫做装载,出栈操作叫做存储。

示例项目
本文附上的代码中包含了许多用 MSIL 写的控制台程序 . 如果需要编译他们,请确定 ILAsm程序可以通过PATH访问。每个项目都是一个Visual Studio解决方案,IL源文件可以用VS的文本编辑器打开,Build命令运行ILAsm程序在项目所在目录生成exe文件,run命令执行该文件。在每个程序的末尾,我加了几行代码,他们可以用C#来写:
    Console.WriteLine("Press Enter to continue");
    Console.Read();
这样,当从 Windows Explorer 运行的时候,就可以看到程序的输出。
下面是所含项目的列表:
1.      打印字符串 打印字符传到控制台。
2.      赋值 给一个int变量赋值并把它打印到控制台。
3.      运算 从控制台读取2个数字,惊醒+,-和乘的操作,并显示结果。
4.      数组 分配一个int类型的数组,给他的元素赋值,打印其元素和数组的长度。
5.      比较 输入2个数字并打印出最小的那个。
6.      数组2 用循环填充数组元素并打印某些元素。
7.      不安全代码 使用unsafe指针访问数组元素。
8.      PInvoke 调用Win32 API。
9.      和类一起工作。
10.   异常 异常处理。
我假设你以在这所说的顺序阅读这些项目。在下面的项目描述中,我用程序来解释每一条MSIL命令,并给出一些代码片段。

打印字符串
PrintString 就是MSIL版的 Hello, World
在代码中用到的MSIL指令如下:
l          .entrypoint 定义程序的入口点(该函数在程序启动的时候由 .NET 运行库调用)
l          .maxstack 定义函数代码所用堆栈的最大深度。 C# 编译器可以对每个函数设置准确的值, 在例子中,我把他设为 8
用到的MSIL命令如下:
  • ldstr string把一个字符串常量装入堆栈。
  • call function(parameters)调用静态函数。函数的参数必须在函数调用前装入堆栈。
  • pop取出栈顶的值。当我们不需要把值存入变量时使用。
  • ret从一个函数中返回。
调用静态函数很简单。我们把函数的参数压入堆栈,调用函数,然后从堆栈中读取函数的返回值(如果是非 void 函数)。 Console.WriteLine 就是一个这样的函数。
下面是代码:
.assembly PrintString {}
/*
    Console.WriteLine("Hello, World)"
*/
.method static public void main() il managed
{
    .entrypoint             // 该函数是程序的入口
    .maxstack 8
    // *****************************************************
    // Console.WriteLine("Hello, World)";
    // *****************************************************
    ldstr "Hello, World"        // 把字符串压入堆栈
    // 调用静态的System.Console.Writeline函数
    // ( 函数移除栈顶的字符串)
    call   void [mscorlib]System.Console::WriteLine
                                 ( class System.String)
    // *****************************************************
    ldstr "Press Enter to continue"
    call   void [mscorlib]System.Console::WriteLine
                                 ( class System.String)
    // 调用 System.Console.Read 函数
    call int32 [mscorlib]System.Console::Read()
    // pop 指令移除栈顶元素
    // ( 移除由Read()函数返回的数字
    pop
    // *****************************************************
    ret
}

赋值
该程序给一个变量赋与int值并把它打印到控制台窗口。
命令:
  • ldc.i4.n把一个 32位的常量(n从0到8)装入堆栈
  • stloc.n把一个从堆栈中返回的值存入第n(n从0到8)个局部变量
代码:
.assembly XequalN {}
// int x;
// x = 7;
// Console.WriteLine(x);
.method static public void main() il managed
{
    .entrypoint
    .maxstack 8
    .locals init ([0] int32 x)  // 分配一个局部变量
    // *****************************************************
    // x = 7;
    // *****************************************************
    ldc.i4.7                    // 把常量装入堆栈
    stloc.0                     // 把堆栈中的值存入第0个变量
    // *****************************************************
    // Console.WriteLine(x);
    // *****************************************************
    ldloc.0                     // 把第0个变量转入堆栈
    call void [mscorlib]System.Console::WriteLine(int32)
    ret
}

数据运算
本程序从控制台读取2个数字,对它们进行简单的运算,然后显示结果。
命令:
  • add—2个值相加。命令的参数必须在调用前装入堆栈,该函数从堆栈中移除参数并把运算后的结果压入堆栈。
  • sub— 2个值相减。
  • mul— 2个值相乘。
代码片段:
.assembly Operations {}
/*
// 程序的C#代码:
            int x, y, z;
            string s;
 
            Console.WriteLine("Enter x:");
            s = Console.ReadLine();
            x = Int32.Parse(s);
 
            Console.WriteLine("Enter y:");
            s = Console.ReadLine();
            y = Int32.Parse(s);
 
            z = x + y;
            Console.Write("x + y = ");
            Console.Write(z);
            Console.WriteLine("");
 
            z = x - y;
            Console.Write("x - y = ");
            Console.Write(z);
            Console.WriteLine("");
 
            z = x * y;
            Console.Write("x * y = ");
            Console.Write(z);
            Console.WriteLine("");
*/
 
.method static public void main() il managed
{
    .entrypoint
    .maxstack 8
 
    .locals init ([0] int32 x,
           [1] int32 y,
           [2] int32 z,
           [3] string s)
 
    // *****************************************************
    // Console.WriteLine("Enter x:");
    // *****************************************************
    ldstr      "Enter x:"       // 把字符装入堆栈
call       void [mscorlib]System.Console::WriteLine( string )
 
    // *****************************************************
    // s = Console.ReadLine();
    // *****************************************************
    call       string [mscorlib]System.Console::ReadLine()
    stloc.3                     // 把值存入第3个变量
 
    // *****************************************************
    // x = Int32.Parse(s);
    // *****************************************************
    ldloc.3                     // 把第3个变量装入堆栈
 
    // 调用 System.Int32::Parse(string)函数
    // 把字符串从堆栈中移除并把解析的结果——int值压入堆栈
    call       int32 [mscorlib]System.Int32::Parse( string )
 
    stloc.0                     // 把值存入第0个变量
 
    // *****************************************************
     // 和变量y的一些运算
    // *****************************************************
    ldstr      "Enter y:"
               // 装入字符串
    call       void [mscorlib]System.Console::WriteLine( string )
               // 调用
    call       string [mscorlib]System.Console::ReadLine()
               // 调用
    stloc.3
               // 把值存入第3个变量
    ldloc.3
               // 把第3个变量装入堆栈
    call       int32 [mscorlib]System.Int32::Parse( string )
               // 调用
    stloc.1
               // 把值存入第1个变量
 
    // *****************************************************
    // z = x + y;
    // *****************************************************
    ldloc.0             // 把第0个变量装入堆栈
    ldloc.1             // 把第1个变量装入堆栈
 
    // 把这2个值从堆栈中移除,把结果压入堆栈
add
 
    stloc.2             // 把值存入第2个变量
 
    // *****************************************************
    // Console.Write("x + y = ");
    // *****************************************************
    ldstr      "x + y = "          // load string onto stack
    call       void [mscorlib]System.Console::Write( string )
 
    // *****************************************************
    // Console.Write(z);
    // *****************************************************
    ldloc.2                    // 把第2个变量装入堆栈
    call       void [mscorlib]System.Console::Write(int32)
 
    // *****************************************************
    // Console.WriteLine("");
    // *****************************************************
    ldstr      ""                  // 装入字符串
    call       void [mscorlib]System.Console::WriteLine( string )
 
    // 相减和相乘运算过程与上面相同
 
    ret
}

未完待续。  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值