乱涂IL中间语言

    技术2022-05-11  87

    【文章标题】: 乱涂IL中间语言【文章作者】: 有酒醉【作者邮箱】: wuqr32@sina.com【下载地址】: 自己搜索下载【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!--------------------------------------------------------------------------------【详细过程】  一、序      .NET中间语言即IL,IL本身以二进制格式存在.对于IL指令大家可以自己查阅.NET Framework SDK 的 Tool Developers Guide/Docs      子文件夹的Word文档中的说明            二、第一个IL程序      现在让我们学习一下第一个IL程序,代码如下:            // HelloWorld.il      // This is our first IL Program!      .assembly extern mscorlib{}            .assembly HelloWorld      {         .ver 1:0:1:0      }            .module HelloWorld.exe            .method static void Main() cil managed      {         .maxstack 1         .entrypoint               ldstr "Hello,World!"         call void [mscorlib]System.Console::WriteLine(string)         ret      }            编译运行:      D:/>ilasm HelloWorld.il            Microsoft (R) .NET Framework IL Assembler.  Version 1.1.4322.2032      Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.      Assembling 'helloworld.il' , no listing file, to EXE --> 'HelloWorld.EXE'      Source file is ANSI            Assembled global method Main      Creating PE file            Emitting members:      Global  Methods: 1;      Writing PE file      Operation completed successfully            D:/>HelloWorld.exe      Hello,World!            至此我们第一个IL程序出炉了! 下面我解释一下各个关键字的意思.            .assembly extern mscorlib{}            这一句对外指出要引用的程序集,当然你可以写多个由extern限定的.assembly指令,把该要的程序集全部包罗近来            .assembly HelloWorld            这一句指出程序集的名字.用大括号括起中的.ver指令表示它的版本号,大家有兴趣自己查查里面都可以写什么指令,这里就不废话咯            .module HelloWorld.exe            指出存储该模块的文件名称.据说如果不显示声明模块,ilasm.exe自动采取与程序集相同的模块.本人做了个测试,好象不起作用.难      道又是人品问题?            .method static void Main() cil managed            .method 指令声明一个静态方法,并且用.entrypoint来指明这是一个程序的入口函数.所以说嘛,在C#中Main必须为入口函数的说法      在IL中可不适用哦!在IL中,你可以取个变态的入口函数,没有人可以管你.            cil managed 表明此方法包含IL代码,这可有些重要,因为.NET也允许在方法中使用可执行代码来代替IL代码.             .maxstack 1            指出计算栈容量为1.计算栈不能多也不能少,否则编译不过去,至于计算栈怎么计算,真麻烦,说只要两三句,写就要一大堆!怎么办?:-)            ldstr "Hello,World!"            加载字符串"Hello,World!"到计算栈(计算栈满咯...,因为它的容量被声明为1)            call void [mscorlib]System.Console::WriteLine(string)            调用System.Console.WriteLine(string)函数,将计算栈上的字符串弹出并输出(因为该函数需要一个字符串,这时候计算栈为空).调用      函数时指出了程序集.                  三、IL原理      IL基于虚拟机的概念!计算栈又是IL中的一个重要课题,我们通过下面图来稍微了解一下它!             |---------------------------------------------------------|       | 通常可用的内存        |-------------------------------| |       |                       |  当前方法可用的局部内存       | |       | |---------------|     | |--------------|              | |       | | 静态字段      ___FCKpd___0nbsp;   | | 方法参数表   |               | |       | |               |/    | |------$-------|              | |       | |---------------| /   |                |              | |       |                    /  | |------$-------|   |----------| |       | |---------------|   / | ___FCKpd___0nbsp;            |<->|动态内存池 | |       | | 托管堆        |    /|/|              |   |----------| |       | |               |     / |   计算栈     |              | |       | |               |    /|/|              |              | |       | |               |   / | ___FCKpd___0nbsp;            |              | |       | |               |  /  | |------$-------|              | |       | |               | /   |        |                      | |       | |               |/    |        |                      | |       | |               $     | |------$-------|              | |       | |---------------|     | | 局部变量表   |              | |       |                       | |--------------|              | |       |                       |-------------------------------| |       |---------------------------------------------------------|         方法的内存区域        真辛苦啊,画这个破图竟然花了我347秒!        从图中可以了解到,所有的数据传输都必须经过计算栈,那么图里面的各个元素又代表什么呢?LOOK!        局部变量表 --它是存储局部变量的内存区域,必须在每个方法的开始位置声明    参数表  --本内存区域包含作为参数传递给方法的变量,如果当前方法是一个实例方法,那么它应该还包含this引用    局部内存池 --这是可以动态分配的内存区域.它和参数表的区别是:池所需要的内存可以在运行时确定.    计算栈  --最重要的内存区域!它是唯一执行实际运算的区域.        计算栈嘛,好象不太好理解.怎么办?打个比方说,你要测试数据,那么数据就得先复制到计算栈然后再测试.如果执行一些运算,比如加法之    类的,你也要先把它复制到计算栈,然后再进行加法运算.栈在通俗的讲是'先进后出'滴,所以这里的计算栈也不例外.好了,让我们GO ON!        静态字段 --当运行的方法当然也可以访问任意类的静态成员    托管堆 --它是存储引用数据类型和装相值(boxed value)类型的地方.           解释完毕!废话少说,我们来个实例瞧瞧计算栈是怎么计算的:    // Add Sample    // AddDemo.il    .assembly extern mscorlib{}    .assembly AddDemo    {     .ver 1:0:1:0    }    .module AddDemo.exe    .method static void Main() cil managed    {     .entrypoint     .maxstack 2     ldstr "The sum of the numbers is "     call void [mscorlib]System.Console::WriteLine(string)     ldc.i4.s 47     ldc.i4 345     add     call void [mscorlib]System.Console::WriteLine(int32)     ret    }        在分析该程序之前有一点要明确:计算栈的大小是以变量数目为单位计算,而不以字节或者任意固定单位来测量        分析:        ldstr "The sum of the numbers is "        ldstr的栈增量是1    加载一个字符串到计算栈: ...->...,string        call void [mscorlib]System.Console::WriteLine(string)        方法System.Console.WriteLine(string)需要从栈中找一个字符串来执行,所以它的栈增量是-1    ...,string -> ...        ldc.i4.s 47        加载短整数47到栈中.所以它的栈增量是1.    指令ldc.i4.s加载范围是(-128~127).    ...->...,value        ldc.i4 345        ldc.i4加载一个4字节的有符号整数到计算栈.现在,计算栈已经达到了由.maxstack指令指定的最大尺寸.        add        add指令执行加法运算,它从计算栈中弹出两个数(必须具有相同的数据类型),对它们进行加法运算,再把结果放回计算栈.所以,add指令具    有偏移量-1,它可以用下面来表示:    ...,value,value --> ...,result        call void [mscorlib]System.Console::WriteLine(int32)        弹出一个数据项并输出.此时数据栈为空.            关于IL数据类型(请查看文档):        IL数据类型可以应用于类型标识,返回方法的签名中的类型.比如:        .method static int32 DoSomething(int16,float32,object)    或    call instance void [System.Drawing]System.Drawing.Rectangle::Intersect(          valuetype [System.Drawing]System.Drawing.Rectangle)        再谈谈IL对象引用,托管指针,非托管指针之间的区别:    1、object是托管堆中的一个对象引用.它与C#中的object类似.例如:     MyClass myClass = new MyClass();     myClass.DoSomething();     可以被转化为使用对象引用的IL代码         2、&是托管指针的IL表示.它通常用于向方法传递引用值.例如:      int x = 30      DoSomething(ref 30)      生成的IL代码将使用托管指针.      托管指针和对象引用不同.托管指针用以指向对象实例中的数据本身,而object实际指向的是引用对象的实例数据之前的头信息,包括一      些方法表头.          注意:如果对值类型调用方法(如 Int32.ToString()),你必须通过托管指针而不是对象引用来完成调用.我们来做个测试:     // T.il     .assembly extern mscorlib{}        .assembly T    {     .ver 1:0:1:0    }        .module T.exe        .method static void Main() cil managed    {     .entrypoint     .maxstack 1          ldc.i4.s 47     box int32     //unbox int32     call instance string int32::ToString()     call void [mscorlib]System.Console::WriteLine(string)     ret    }        编译运行:    D:/>ilasm t.il        Microsoft (R) .NET Framework IL Assembler.  Version 1.1.4322.2032    Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.    Assembling 't.il' , no listing file, to EXE --> 't.EXE'    Source file is ANSI        Assembled global method Main    Creating PE file        Emitting members:    Global  Methods: 1;    Writing PE file    Operation completed successfully        D:/>t.exe    2042268872        是不是感到有些惊讶?因为int32是值类型,它的ToString实现要求所传入的第一个参数是需要被转换的int32类型的一个托管指针.而我们    却是用一个对象引用来代替栈中的托管指针,所以导致了错误.修正很简单,只要把注释部分去掉即可.        3、非托管指针    非托管指针用来指向任何字面量值.它和托管指针的操作相同.只是,单元收集器决定对那些对象进行收集时,它所关注的是托管指针而不    是非托管指针.当它在堆中移动对象时,会更新托管指针.如果非托管指针指向的对象被移动,那就完蛋鸟,它将指向一个无用的对象.所以    在C#中,对非托管指针有fixed关键字来锁定对象.            关于IL指令列表    <略>            四、IL编程    这里简单讲解几种基本的定义方式.    1、定义类型的命名空间,采用关键字.namespace.示例:  // This is a HelloWorld app with a class!  .assembly extern mscorlib{}  .assembly T  {   .ver 1:0:1:0  }    .module T.exe    .namespace com.yzl.chp1  {   .class public auto ansi T extends [mscorlib]System.Object   {    .method public static void EntryMethod() cil managed    {     .maxstack 1     .entrypoint     ldstr "Hello,World"     call void [mscorlib]System.Console::WriteLine(string)     ret    }   }  }      分析:    在IL中,我们使用.namespace,.class指令.我们使用extends关键字来指示一个基类.这里的入口点方法为EntryMethod,主要是为了说明C#  中Main为入口函数在IL中并不适用.    public --访问修饰符,与C#中相同.      IL语言中的访问修饰类型                                |-----------------------|-----------------------|-------------------|  |访问类型               |可见性                 |C#中对应的访问类型 |  |-----------------------|-----------------------|-------------------|  |public                 |所有代码可见           |public             |  |-----------------------|-----------------------|-------------------|  |private                |只在相同类中可见       |private            |  |-----------------------|-----------------------|-------------------|  |family                 |此类及基类可见         |protected          |  |-----------------------|-----------------------|-------------------|  |assembly               |同一程序集代码中可见   |internal           |  |-----------------------|-----------------------|-------------------|  |familyandassem         |同一程序集的派生类中   |N/A                |  |-----------------------|-----------------------|-------------------|  |familyorassem          |派生类及同一程序集     |protected internal |  |-----------------------|-----------------------|-------------------|  |privatescope           |与private类似          |N/A                |  |-----------------------|-----------------------|-------------------|    auto --指定类的内存中的布局方式.有三个选项:auto - 自动方式,Sequential - 字段在内存中依次排放,Explicit - 显示指定每个   字段的偏移量    ansi --表示字符串被转换为本机非托管字符串的方式.ansi - 指定字符串被转换为ASNI字符串,unicode - 采用UNICODE格式的字符   autochar - 右运行平台所决定     extends --指示一个基类    提示:如果在访问修饰类型前缀nested,那么我们也可以把上面的访问修饰类型应用于其他类型,示例:    .class public OuterClass  {   // 将访问修饰符作用于类   .class nested family TestClass   {    // ...   }  }    2、条件语句和分支    br --无条件跳转,对栈没影响.该命令还有一个简单的形式br.s(偏移量127~-128). 示例:    br Finish    ...    Finish:    ble --比较栈中的两个数值,如果第一个数小于或等于第二个数,则分支流程.示例:    ldc.i4 -21  ldc.i4 10  ble Smaller    ..    Smaller:    ...    对栈影响:...,value,value -> ...    ble.un --无符号比较,如果一个负数和正数比较的话,负数大!    brfalse,brfalse.s --栈顶元素为零  brtrue,brtrue.s  --栈顶元素不为零    示例:  .assembly T{}  .method static void Main() cil managed  {   .maxstack 2   .entrypoint      ldstr "Input first number"   call void [mscorlib]System.Console::WriteLine(string)   call string [mscorlib]System.Console::ReadLine()   call int32 [mscorlib]System.Int32::Parse(string)   ldstr "Input second number"   call void [mscorlib]System.Console::WriteLine(string)   call string [mscorlib]System.Console::ReadLine()   call int32 [mscorlib]System.Int32::Parse(string)   ble.s FirstSmaller   ldstr "The first number was larger then the second one"   call void [mscorlib]System.Console::WriteLine(string)   br.s Finish     FirstSmaller:   ldstr "The first number was less then or equals to the second one"   call void [mscorlib]System.Console::WriteLine(string)  Finish:   ldstr "Thank you"   call void [mscorlib]System.Console::WriteLine(string)   ret  }    编译运行:  D:/>ilasm T.il    Microsoft (R) .NET Framework IL Assembler.  Version 1.1.4322.2032  Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.  Assembling 'T.il' , no listing file, to EXE --> 'T.EXE'  Source file is ANSI    Assembled global method Main  Creating PE file    Emitting members:  Global  Methods: 1;  Writing PE file  Operation completed successfully    D:/>T.exe  Input first number  23  Input second number  32  The first number was less then or equals to the second one  Thank you    D:/>      3、定义有参数的方法,示例:    .assembly extern mscorlib{}    .assembly T  {   .ver 1:0:1:0  }    .module T.exe    .namespace com.yzl.chp1  {   .class T extends [mscorlib]System.Object   {    .method public static bool FirstIsGreater(int32 x,int32 y) cil managed    {     .maxstack 2     ldarg.0     ldarg.1     ble.s FirstSmaller     ldc.i4.1     ret    FirstSmaller:     ldc.i4.0     ret    }        // 入口方法    .method public static void Main() cil managed    {     .maxstack 2     .entrypoint          ldstr "Input first number"     call void [mscorlib]System.Console::WriteLine(string)     call string [mscorlib]System.Console::ReadLine()     call int32 [mscorlib]System.Int32::Parse(string)     ldstr "Input second number"     call void [mscorlib]System.Console::WriteLine(string)     call string [mscorlib]System.Console::ReadLine()     call int32 [mscorlib]System.Int32::Parse(string)          // 调用FirstIsGreater方法     call bool com.yzl.chp1.T::FirstIsGreater(int32,int32)          brfalse.s FirstSmaller     ldstr "The first number was larger then the second one"     call void [mscorlib]System.Console::WriteLine(string)     br.s Finish         FirstSmaller:     ldstr "The first number was less then or equals to the second one"     call void [mscorlib]System.Console::WriteLine(string)    Finish:     ldstr "Thank you!"     call void [mscorlib]System.Console::WriteLine(string)     ret    }   }  }    说明:  ldarg.0 --加载第一个参数到栈中  ldarg.1 --加载第二个参数到栈中    相应的:  starg.0 --将栈中数值赋值给第一个参数  starg.1 --同上道理    4、局部变量,由指令.locals完成.示例1:    .method static void DoSomething() cil managed  {   .locals init(unsigned int32,string)   // code for method  }  init --初始化局部变量    注意:变量使用之前必须初始化,否则代码会变得不可验证.  操作局部变量的指令是:ldloc.s,stloc.s,ldloc.0,stloc.0,...  --------------------------------------------------------------------------------【版权声明】: 本文原创于泉州软件基地, 转载请注明作者并保持文章的完整, 谢谢!                                                        2007年02月06日 17:25:49  

    最新回复(0)