如何解读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 ...
随机推荐
- javascript jquery封装对象时的错误,求解!我想知道为什么
jquery 封装对象时的错误 --------------------------------------------<input id="name" name=&qu ...
- 解决“System.Data.OracleClient 需要 Oracle 客户端软件 version 8.1.7 或更高版本”的问题
以server2008为例: 首先确保使用sqlplus能访问数据库. 1.管理工具->计算机管理->本地用户和组->组->administrators属性,添加,高级,立即查 ...
- 字串变换 bfs + 字符串
题目描述 已知有两个字串A,BA,BA,B及一组字串变换的规则(至多666个规则): A1A_1A1 ->B1 B_1B1 A2A_2A2 -> B2B_2B2 规则的含义为:在 ...
- Jenkins利用官网上的rpm源安装
官网网址:https://pkg.jenkins.io/redhat/ (官网上有安装的命令,参考网址) 安装jdk yum install -y java-1.8.0- ...
- Bootstrap中的Glyphicon 字体图标
在Bootstrap框架中也为大家提供了近200个不同的icon图片,而这些图标都是使用CSS3的@font-face属性配合字体来实现的icon效果. 1 <!DOCTYPE html> ...
- 使用Mybatis-plus发生org.apache.ibatis.binding.BindingException: Invalid bound statement (not found):
容我慢慢说来,之前是使用springboot+mybatis.我一直采用xml配置文件写sql. 后来采用了mybatis-plus之后,在本地上面测试没有一点问题.一放到服务器就发生这种情况 在本地 ...
- Request库基本使用
基本实例 import requests url= 'https://www.baidu.com/' response = requests.get(url) print(type(response) ...
- day_10 函数名,闭包,迭代器
1. 函数名的使用 1.函数名是一个变量,函数名储存的是函数的内存地址 2.函数名可以赋值给其他变量 3.函数名可以当容器类对象的元素 4.函数名可以当其他函数的参数 5.函数名可以做函数的返回值 2 ...
- mongo察看replica set副本集primary主节点
printjson(db.adminCommand( { isMaster: 1 } ));
- Kendo UI 简单使用
1 准备工作 引入JS文件和CSS文件 <link href="js/kendoui/styles/kendo.common.min.css" rel="style ...