如何解读IL代码

关于IL代码,我有将从三个方面去揭开它神秘的面纱。IL代码是什么?我们为什么要去读懂IL代码?我们如何去读懂IL代码?这三个问题的解答,将是我解读IL代码的整体思路。

IL代码是什么?IL(Intermediate Language),它也称为CIL或者MSIL,翻译成中文就是“中间语言”。C#的JIT编译器可以将C#源程序编译为.exe或.dll文件,但此时编译出来的程序代码并不是CPU能直接执行的二进制代码,而是传说中的IL代码。因此,.exe或者.dll文件都可以被VS安装自带的ILDASM打开,查看IL代码。

我们为什么要去读懂IL代码?无论精通哪一门技术,我们都应该从其本质出发,探明最核心的运作机理,最终有效地理解和掌握它。对于C#语言来说,掌握IL语言就如同抓住了C#语言的本质,它可以有效地帮助我们去理解编译器的运作机理。对于一些C#高手而言,深入地研究IL语言后,能对IL语言直接进行修改后编译。但就我目前的水准,读懂IL代码是为了更深入地理解C#各种特性,比如C#中最重要的一些基本特性,委托、事件。

我们又将如何去读懂IL代码?在读懂IL代码之前,我必须先给大家介绍一下,C#代码编译环境下的内存结构。就我目前已知分为5大块内存区。分别为,托管堆,线程堆栈,计算栈,调用栈,代码区。

托管堆(Managed Heap):存放引用类型的数据,引用类型的数据由GC(Garbage Collection)负责管理。

线程堆栈(Thread Heap Stack):存放值类型的数据和引用类型地址,值类型的数据由操作系统直接负责管理。

计算栈(Evaluation Stack):临时存放值类型的数据和引用类型的地址的堆栈。符合先进后出(FILO)基本栈规则。

调用栈(Call Stack):其中的Record Frame用于存放.locals init(int32 V_0)指令的参数值,是一个局部变量表,不符合先进后出(FILO)基本栈规则。

代码区(Code):存放各种程序指令集。

数据内存区有4个,按等级可以划分成3个级别。托管区为最高级,只有存放引用类型数据;线程堆栈为第二级,存放值类型数据与引用类型地址;计算栈和调用栈为第三级,在线程中调用方法时,主要在这两个栈中操作数据。要看懂IL代码第一步便是需要清晰地了解计算栈与调用栈互相操作数据的关系。下面我将引入一个IL代码实例来说明几个内存区是如何互相操作数据的。

C#源代码

using System;

namespace ILDemo
{
class Program
{
static void Main(string[] args)
{
int i = 1;
int j = 2;
int k = 3;
int answer = i + j + k;
Console.WriteLine("i+j+k=" + answer);
Console.ReadKey();
}
}
}

IL代码

.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint //程序入口
// 代码大小 42 (0x2a)
.maxstack 2 // 计算出计算堆栈的能存几个值

.locals init ([0] int32 i,
[1] int32 j,
[2] int32 k,
[3] int32 answer) //定义int32类型的i,j,k,answer放入调用栈

IL_0000: nop //无操作

IL_0001: ldc.i4.1 //把i的值从线程堆栈放到计算堆栈上
IL_0002: stloc.0 //把计算堆栈顶部的值(i的值)放到调用堆栈索引0处
IL_0003: ldc.i4.2 //把j的值从线程堆栈放到计算堆栈上
IL_0004: stloc.1 //把计算堆栈顶部的值(j的值)放到调用堆栈索引1处
IL_0005: ldc.i4.3 //把k的值从线程堆栈放到计算堆栈上
IL_0006: stloc.2 //把计算堆栈顶部的值(k的值)放到调用堆栈索引2处

IL_0007: ldloc.0 //把调用堆栈索引为0处的值复制到计算堆栈
IL_0008: ldloc.1 //把调用堆栈索引为1处的值复制到计算堆栈
IL_0009: add //相加
IL_000a: ldloc.2 //把调用堆栈索引为2处的值复制到计算堆栈
IL_000b: add //相加
IL_000c: stloc.3 //把计算堆栈顶部的值(add的值)放到调用堆栈索引3处
IL_000d: ldstr "i+j+k=" //推送对元数据中存储的字符串的新对象引用。
IL_0012: ldloc.3 //把调用堆栈索引为3处的值复制到计算堆栈

IL_0013: box [mscorlib]System.Int32 //装箱
IL_0018: call string [mscorlib]System.String::Concat(object,object) //调用内部方法
IL_001d: call void [mscorlib]System.Console::WriteLine(string) //调用WriteLine
IL_0022: nop //无操作
IL_0023: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey() //调用ConsoleKey
IL_0028: pop //无操作
IL_0029: ret //return
} // end of method Program::Main

总结:1.ldloc.0前缀ld,load含义载入,即压入计算栈;loc,locals含义调用栈,即取值于调用栈第0位参数。

2.其他的ld前缀都是取值于线程堆栈。比如ldc.i4.0,ldstr,ldfld,ldfld。

3.st前缀,store含义存储,即弹出计算栈。弹出的值存放于调用栈。

基于以上3条结论,大家基本可以看懂大部分IL代码的方法内部计算栈和调用栈的数据交互。在随后的博文中,我会叙述一下如何通过IL代码去深入理解委托、事件。希望大家能够支持一下。

如何解读IL代码的更多相关文章

  1. 【小白学C#】浅谈.NET中的IL代码

    一.前言 前几天群里有位水友提问:”C#中,当一个方法所传入的参数是一个静态字段的时候,程序是直接到静态字段拿数据还是从复制的函数栈中拿数据“.其实很明显,这和方法参数的传递方式有关,如果是引用传递的 ...

  2. 深入理解xLua基于IL代码注入的热更新原理

    目前大部分手游都会采用热更新来解决应用商店审核周期长,无法满足快节奏迭代的问题.另外热更新能够有效降低版本升级所需的资源大小,节省玩家的时间和流量,这也使其成为移动游戏的主流更新方式之一. 热更新可以 ...

  3. 读懂IL代码就这么简单(三)完结篇

    一 前言 写了两篇关于IL指令相关的文章,分别把值类型与引用类型在 堆与栈上的操作区别详细的写了一遍 这第三篇也是最后一篇,之所以到第三篇就结束了,是因为以我现在的层次,能理解到的都写完了,而且个人认 ...

  4. 读懂IL代码就这么简单(二)

    一 前言 IL系列 第一篇写完后 得到高人指点,及时更正了文章中的错误,也使得我写这篇文章时更加谨慎,自己在了解相关知识点时,也更为细致.个人觉得既然做为文章写出来,就一定要保证比较高的质量,和正确率 ...

  5. 读懂IL代码就这么简单 (一)

    一前言 感谢 @冰麟轻武 指出文章的错误之处,现已更正 对于IL代码没了解之前总感觉很神奇,初一看完全不知所云,只听高手们说,了解IL代码你能更加清楚的知道你的代码是如何运行相互调用的,此言一出不明觉 ...

  6. 详解.NET IL代码

    一.前言 IL是什么? Intermediate Language (IL)微软中间语言 C#代码编译过程? C#源代码通过LC转为IL代码,IL主要包含一些元数据和中间语言指令: JIT编译器把IL ...

  7. 如何通过ildasm/ilasm修改assembly的IL代码

    原文地址:http://kb.cnblogs.com/page/101162/ 这段时间为跟踪一个Bug而焦头烂额,最后发现是Framework的问题,这让人多少有些绝望.所以到微软论坛提了个帖子,希 ...

  8. 用ildasm/ilasm修改IL代码

    原文地址:http://www.cnblogs.com/dudu/archive/2011/05/17/ildasm_ilasm_il.html 在开发中遇到这样一个场景,需要修改一个dll文件(.N ...

  9. 时空上下文视觉跟踪(STC)算法的解读与代码复现(转)

    时空上下文视觉跟踪(STC)算法的解读与代码复现 zouxy09@qq.com http://blog.csdn.net/zouxy09 本博文主要是关注一篇视觉跟踪的论文.这篇论文是Kaihua Z ...

随机推荐

  1. 改变HTML文件上传控件样式

    思路: 1.重写一个新的样式 2.将默认样式设置display:none;,即设为不可见 3.在js里调用:当点击新样式的时候,调用这个input的点击事件 html: <div class=& ...

  2. Jquery学习理解 (课堂)

    Jquary概述 3 1.Jquary简介 3 1.1什么是jquery 3 2.使用jquary 4 2.1浏览器如何解析HTML文件 4 2.2利用选择器定位节点 4 2.3调用方法操作节点 5 ...

  3. 阿里云服务器 ECS 部署lamp:centos+apache+mysql+php安装配置方法 (centos7)

    阿里云服务器 ECS 部署lamp:centos+apache+mysql+php安装配置方法 (centos7) 1.效果图 1 2. 部署步骤 1 1. mysql安装附加(centos7) 7 ...

  4. vue_cli下开发一个简单的模块权限系统之实现登录

    因为我们需要和后端数据交互,所以我们需要安装axios,安装好以后在main.js引入 v-model是标识空间,v-on:click="doLogin"是登录事件 doLogin ...

  5. c# 中virtual与abstract

    在C#的学习中,容易混淆virtual方法和abstract方法的使用,现在来讨论一下二者的区别.二者都牵涉到在派生类中与override的配合使用. 一.Virtual方法(虚方法) virtual ...

  6. 获取WPF窗体/控件的句柄/当前进程的句柄

    1.在WPF中,获取当前窗体的句柄与WINFORM中不一样: WINFORM直接获取:this.Handle----------this是窗体的类名,handle就是句柄. 2.WPF中先引用命名空间 ...

  7. 平衡树学习笔记(5)-------SBT

    SBT 上一篇:平衡树学习笔记(4)-------替罪羊树 所谓SBT,就是Size Balanced Tree 它的速度很快,完全碾爆Treap,Splay等平衡树,而且代码简洁易懂 尤其是插入节点 ...

  8. springboot整合dubbo注解方式(三)

    笔者用自己代码测试时候发现这个版本信息,可以没有,也可以制定一个就可以了,没有太大的问题,只是需要注意引入的依赖问题,两个jar与一个jar的问题, 然后就是接口公共依赖问题,案例是把我找的几个案例进 ...

  9. Java快速IO(ACM)必备

    en.... 无非用到的是 1. new Scanner(System.in); 2.new BUfferReader(new InputStreamReader(System.in); 3.Syst ...

  10. echarts设置线条粗细

    series: [ { name:"buy", type:'line', data:[], itemStyle: { normal: { color: '#6cb041', lin ...