如何解读IL代码
如何解读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代码的更多相关文章
- 【小白学C#】浅谈.NET中的IL代码
一.前言 前几天群里有位水友提问:”C#中,当一个方法所传入的参数是一个静态字段的时候,程序是直接到静态字段拿数据还是从复制的函数栈中拿数据“.其实很明显,这和方法参数的传递方式有关,如果是引用传递的 ...
- 深入理解xLua基于IL代码注入的热更新原理
目前大部分手游都会采用热更新来解决应用商店审核周期长,无法满足快节奏迭代的问题.另外热更新能够有效降低版本升级所需的资源大小,节省玩家的时间和流量,这也使其成为移动游戏的主流更新方式之一. 热更新可以 ...
- 读懂IL代码就这么简单(三)完结篇
一 前言 写了两篇关于IL指令相关的文章,分别把值类型与引用类型在 堆与栈上的操作区别详细的写了一遍 这第三篇也是最后一篇,之所以到第三篇就结束了,是因为以我现在的层次,能理解到的都写完了,而且个人认 ...
- 读懂IL代码就这么简单(二)
一 前言 IL系列 第一篇写完后 得到高人指点,及时更正了文章中的错误,也使得我写这篇文章时更加谨慎,自己在了解相关知识点时,也更为细致.个人觉得既然做为文章写出来,就一定要保证比较高的质量,和正确率 ...
- 读懂IL代码就这么简单 (一)
一前言 感谢 @冰麟轻武 指出文章的错误之处,现已更正 对于IL代码没了解之前总感觉很神奇,初一看完全不知所云,只听高手们说,了解IL代码你能更加清楚的知道你的代码是如何运行相互调用的,此言一出不明觉 ...
- 详解.NET IL代码
一.前言 IL是什么? Intermediate Language (IL)微软中间语言 C#代码编译过程? C#源代码通过LC转为IL代码,IL主要包含一些元数据和中间语言指令: JIT编译器把IL ...
- 如何通过ildasm/ilasm修改assembly的IL代码
原文地址:http://kb.cnblogs.com/page/101162/ 这段时间为跟踪一个Bug而焦头烂额,最后发现是Framework的问题,这让人多少有些绝望.所以到微软论坛提了个帖子,希 ...
- 用ildasm/ilasm修改IL代码
原文地址:http://www.cnblogs.com/dudu/archive/2011/05/17/ildasm_ilasm_il.html 在开发中遇到这样一个场景,需要修改一个dll文件(.N ...
- 时空上下文视觉跟踪(STC)算法的解读与代码复现(转)
时空上下文视觉跟踪(STC)算法的解读与代码复现 zouxy09@qq.com http://blog.csdn.net/zouxy09 本博文主要是关注一篇视觉跟踪的论文.这篇论文是Kaihua Z ...
随机推荐
- mysql死锁问题解决
1. 定位问题 http://blog.csdn.net/beiigang/article/details/43228361 2. 解决死锁 http://www.blogbus.com/ri0day ...
- NOIP2013 货车运输(最大生成树,倍增)
NOIP2013 货车运输(最大生成树,倍增) A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物,司机们想知道 ...
- wamp配置apache虚拟主机支持多域名访问localhost
1.背景: 在进行网站开发的时候,通常需要以http://localhost或者127.0.0.1等地址来访问本地环境的网站.不过随着开发项目的增多,需要每次先访问localhost然后再选项目,显得 ...
- Golang 连接 MongoDB使用连接池
可以免费试用 MongoDB ,500MB 平时做测试没有问题啦,连接数据库可能因为网络有点慢,但是我们是测试啊,不在乎这点吧~ 这是怎么申请试用版的博客,感谢这位大佬.注册好用起来很方便~ 传送门 ...
- P3897 [湖南集训]Crazy Rabbit
\(\color{#0066ff}{ 题目描述 }\) 兔子们决定在自己的城堡里安排一些士兵进行防守. 给出 n 个点的坐标,和城堡里一个圆心在原点的圆形的障碍,兔子们希望从中选出 k 个兔子,使得它 ...
- [HAOI2012]音量调节 BZOJ2748 dp
题目描述 一个吉他手准备参加一场演出.他不喜欢在演出时始终使用同一个音量,所以他决定每一首歌之前他都需要改变一次音量.在演出开始之前,他已经做好一个列表,里面写着每首歌开始之前他想要改变的音量是多少. ...
- springboot开发环境搭建
姓名:陈中娇 班级:软件151 第一步:在Eclipse下面配置Maven环境: 一.使用spring boot新建maven工程不在需要建立maven web工程,只要一般的maven工程就好了 ...
- Qt 学习之路 2(7):MainWindow 简介
Qt 学习之路 2(7):MainWindow 简介 豆子 2012年8月29日 Qt 学习之路 2 29条评论 前面一篇大致介绍了 Qt 各个模块的相关内容,目的是对 Qt 框架有一个高屋建 ...
- innodb的读写参数优化
(1) 读取参数,global buffer pool以及 local buffer Innodb_buffer_pool_size,理论上越大越好,建议服务器50%~80%,实际为数据大小80 ...
- Codeforces Beta Round #87 (Div. 2 Only)-Party(DFS找树的深度)
A company has n employees numbered from 1 to n. Each employee either has no immediate manager or exa ...