C#基础之IL ,轻松读懂中间代码IL 转载
这一类的特点是一看名字就知道是干嘛的,不需要多讲,如下:
名称 |
说明 |
Add |
将两个值相加并将结果推送到计算堆栈上。 |
Sub |
从其他值中减去一个值并将结果推送到计算堆栈上。 |
Div |
将两个值相除并将结果作为浮点(F 类型)或商(int32 类型)推送到计算堆栈上。 |
Mul |
将两个值相乘并将结果推送到计算堆栈上。 |
Rem |
将两个值相除并将余数推送到计算堆栈上。 |
Xor |
计算位于计算堆栈顶部的两个值的按位异或,并且将结果推送到计算堆栈上。 |
And |
计算两个值的按位"与"并将结果推送到计算堆栈上。 |
Or |
计算位于堆栈顶部的两个整数值的按位求补并将结果推送到计算堆栈上。 |
Not |
计算堆栈顶部整数值的按位求补并将结果作为相同的类型推送到计算堆栈上。 |
Dup |
复制计算堆栈上当前最顶端的值,然后将副本推送到计算堆栈上。 |
Neg |
对一个值执行求反并将结果推送到计算堆栈上。 |
Ret |
从当前方法返回,并将返回值(如果存在)从调用方的计算堆栈推送到被调用方的计算堆栈上。 |
Jmp |
退出当前方法并跳至指定方法。 |
Newobj |
New Object创建一个值类型的新对象或新实例,并将对象引用推送到计算堆栈上。 |
Newarr |
New Array将对新的从零开始的一维数组(其元素属于特定类型)的对象引用推送到计算堆栈上。 |
Nop |
如果修补操作码,则填充空间。尽管可能消耗处理周期,但未执行任何有意义的操作。Debug下的 |
Pop |
移除当前位于计算堆栈顶部的值。 |
Initobj |
Init Object将位于指定地址的值类型的每个字段初始化为空引用或适当的基元类型的 0。 |
Isinst |
Is Instance测试对象引用是否为特定类的实例。 |
Sizeof |
将提供的值类型的大小(以字节为单位)推送到计算堆栈上。 |
Box |
将值类转换为对象引用。 |
Unbox |
将值类型的已装箱的表示形式转换为其未装箱的形式。 |
Castclass |
尝试将引用传递的对象转换为指定的类。 |
Switch |
实现跳转表。 |
Throw |
引发当前位于计算堆栈上的异常对象。 |
Call |
调用由传递的方法说明符指示的方法。 |
Calli |
通过调用约定描述的参数调用在计算堆栈上指示的方法(作为指向入口点的指针)。 |
Callvirt |
对对象调用后期绑定方法,并且将返回值推送到计算堆栈上。 |
强调一下,有三种call,用的场景不太一样:
Call:常用于调用编译时就确定的方法,可以直接去元数据里找方法,如静态函数,实例方法,也可以call虚方法,不过只是call这个类型本身的虚方法,和实例的方法性质一样。另外,call不做null检测。
Calli: MSDN上讲是间接调用指针指向的函数,具体场景没见过,有知道的朋友望不吝赐教。
Callvirt: 可以调用实例方法和虚方法,调用虚方法时以多态方式调用,不能调用静态方法。Callvirt调用时会做null检测,如果实例是null,会抛出NullReferenceException,所以速度上比call慢点。
第二类:加载(ld)和存储(st)
有一部分是比较之后跳转的,代码里的 if 就会产生这些指令,符合条件则跳转执行另一些代码:
以b开头:beq, bge, bgt, ble, blt, bne
先把b去掉看看:
eq: equivalent with, ==
ge: greater than or equivalent with , >=
gt: greater than , >
le: less than or equivalent with, <=
lt: less than, <
ne: not equivalent with, !=
这样是不是很好理解了,beq IL_0005就是计算栈上两个值相等的话就跳转到IL_0005, ble IL_0023是第一个值小于或等于第二个值就跳转到IL_0023。
以br(break)开头:br, brfalse, brtrue,
br是无条件跳转;
brfalse表示计算栈上的值为 false/null/0 时发生跳转;
brtrue表示计算栈上的值为 true/非空/非0 时发生跳转
还有一部分是c开头,算bool值的,和前面b开头的有点像:
ceq 比较两个值,相等则将 1 (true) 推到栈上,否则就把 0 (false)推到栈上
cgt 比较两个值,第一个大于第二个则将 1 (true) 推到栈上,否则就把 0 (false)推到栈上
clt 比较两个值,第一个小于第二个则将 1 (true) 推到栈上,否则就把 0 (false)推到栈上
以上就是三类常用的,把这些搞明白了,IL指令也就理解得七七八八了。就像看文章一样,认识大部分字后基本就不影响阅读了,不认识的猜下再查下,下次再看到也就认得了。
例子
IL其实不难,有没有用则仁者见仁,智者见智,有兴趣就学一下,也花不了多少时间,确实也没必要学多深,是吧。
当然,也是要有耐心的,复杂的IL看起来还真是挺头痛。好在有工具ILSpy,可以在option里选择部分不反编译来看会比较简单些。
最后介绍两个工具:
.Net Reflector可以把用户自己编写的IL指令转化为正常代码,大家可以自己下载安装;
IL查看工具可以把正常代码转化为IL指令,vs2010中路径为C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools\ildasm.exe,不同版本目录可能不太一样。
有了这两个工具当我们想用IL指令实现某一功能但不会写时,可以先用正常代码把功能写出来,在IL查看工具中查看IL代码是什么样的,然后自己再根据转化的IL代码逻辑使用IL指令实现想要的功能。