.net core的JIT编译分前端和后端。前端负责把IL指令转换到JIT的高级中间表现HIR。后端负责把HIR转换到低级中间表现LIR后,再转换到架构相关的汇编指令。结构如图:
![](https://img-blog.csdnimg.cn/20200711145340702.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zODY2OTU2MQ==,size_16,color_FFFFFF,t_70)
逆向.net程序(.dll .exe)到IL的工具有ildasm、ILSpy、dnSpy。其中ildasm为官方提供工具,在源码coreclr中生成,可以把.net程序转换到IL代码。与ildasm对应的还有个ilasm工具,可以把IL代码再转换成.net程序集。ILSpy和dnSpy不仅能把.net程序转换到IL代码,还能转换到c#/f#等IL之前的高级语言。
本文只关注JIT编译前的IL指令格式。
一、IL指令
JIT之前的MSIL(Microsoft Intermediate Language 简称:IL),.Net 框架使用的中间件语言。IL语言很像汇编语言(比如MIPS),有栈空间、load/store指令、运算指令等。IL中的栈叫评价堆栈,用于在指令间传递临数据。IL中的评价堆栈原理很像逆波兰式记法(Reverse Polish Notation)。常用IL指令如下表。
域 | 功能 |
Ldc_I4_0 | 将整数值 0 作为 |
Stloc_0 | 从计算堆栈的顶部弹出当前值并将其存储到索引 0 处的局部变量列表中。 |
Br | 无条件地将控制转移到目标指令。 |
Brtrue | 如果 |
Break | 向公共语言结构 (CLI) 发出信号以通知调试器已撞上了一个断点。 |
Add | 将两个值相加并将结果推送到计算堆栈上。 |
Sub | 从其他值中减去一个值并将结果推送到计算堆栈上。 |
Mul | 将两个值相乘并将结果推送到计算堆栈上。 |
Div | 将两个值相除并将结果作为浮点( |
And | 计算两个值的按位“与”并将结果推送到计算堆栈上。 |
Or | 计算位于堆栈顶部的两个整数值的按位求补并将结果推送到计算堆栈上。 |
Neg | 对一个值执行求反并将结果推送到计算堆栈上。 |
Nop | 如果修补操作码,则填充空间。 尽管可能消耗处理周期,但未执行任何有意义的操作。 |
Ldloc | 将指定索引处的局部变量加载到计算堆栈上。 |
Clt | 比较两个值。 如果第一个值小于第二个值,则将整数值 1 |
Call | 调用由传递的方法说明符指示的方法。 |
Ret | 从当前方法返回,并将返回值(如果存在)从被调用方的计算堆栈推送到调用方的计算堆栈上。 |
Throw | 引发当前位于计算堆栈上的异常对象。 |
Volatile | 指定当前位于计算堆栈顶部的地址可以是易失的,并且读取该位置的结果不能被缓存,或者对该地址的多个存储区不能被取消。 |
如果你熟悉某一种体系架构,比如ARM、MIPS、X64。那么在理解上表列举的IL指令会特别的轻松。上表也仅仅列举了IL指令的一部分。更详细语法参看官方 https://docs.microsoft.com/zh-cn/dotnet/api/system.reflection.emit.opcodes?view=netcore-3.0
二、示例
下面通过一个小的例子来简单了解一下IL。首先编写一个c#程序
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
上面这个C#程序,对应的IL代码如下(使用ildasm生成):
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 13 (0xd)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "Hello World!"
IL_0006: call void [System.Console]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: ret
} // end of method Program::Main
下面分别介绍上述IL中每条语句:
- .entrypoint 表示这个方法是主方法
- .maxstack 表示该方法使用的评价堆栈的大小
- nop 空指令什么都不做。d对应MIPS汇编指令中的nop ?
- ldstr 将后面字符串“Hello World!” 存储到评价堆栈(入栈)
- call 函数调用 System.Console::WriteLine(string),参数是栈顶值,即为“Hello World!”
- ret 方法返回