IL代码大致分为三部分:程序头,各类声明,代码段
程序头:主要是包括编译后生成的可执行文件的一些属性的定义,一般三个关键字
.assembly 声明本程序集名称
.assembly extern 声明外部程序集名称
.module 声明主模块名称
各类声明:
.namespace 名称空间声明
.class 类声明
.method 方法声明
.field 字段声明
.data 数据声明
.custom 自定义属性声明
代码段:
主要由MSIL指令以及个别关键字构成,通常一个IL指令由操作码(opcode)和指令参数(操作数operrand)构成,操作码长度为1或2字节,当为2字节时,第一个字节总是0xFE
流程控制指令:
1.直接跳转指令
br 从当前位置移动到指定的位置
br .s br的短指令格式
2.条件跳转指令
brfalse value为0则跳转
brfalse.s 短指令格式
brtrue 不为0跳转
brtrue.s 短指令格式
3.比较跳转
beq 等于跳转 beq.s
bne.un 不等于跳转 bne.nu.s 无符号
bge[.un.s] 大于或等于
bgt[.un.s] 大于则跳转
ble[.un.s] 小于等于跳转
blt[.un.s] 小于跳转
un 标识无符号 s标识短指令
4.选择指令
switch
5中断指令
break
6 托管异常指令
leave[.s] 清空堆栈,跳至当前指令的偏移处。用于从try。。catch跳出
endfilter 表示filter块的终结。
endfinally 表示finally或fault终结,并清空堆栈
7 返回指令
ret 从一个方法返回
算术指令
1.堆栈操作
nop 空指令 无任何操作
dup 赋值当前栈顶元素
pop 出栈 堆栈为空时出错
2.常数载入
用于将常数入栈
ldc.i4 载入int32
ldc.i4.s 载入int8
ldc.i4.ml 载入-1
ldc.i4.[0 ~8] 载入0~8;
ldc.i8 载入int64
ldc.r4 载入float32
ldc.r8 载入float64
3.间接载入
间接载入指令从栈顶取出一个托管指针(&类型)或非托管指针(native int 类型),载入该指针所指向的数值,并入栈。指令中小数点后的名称表明了载入数据的类型
ldind.i1/ ldind.u1 载入1字节无符号/有符号整数
ldind.i2/ldind.u2 2字节
ldind.i4/ldind.u4 4字节
ldind.i8/ldind.u8 8字节
ldind.i 载入native int 大小为当前系统的指针大小
ldind.r4/ldind.r8 载入单精度/双精度浮点数据
ldind.ref 载入引用对象
4.间接存储
间接存储从栈顶先后取出一个值和指针,并将改值存储至指针所指位置。
stind.ref 存储一个对象引用
stind.i1,i2,i4,i8 存储1.2.4.8字节整数
stind.i存储一个指针大小的数据
stind.r4,r8 存储单精度、双精度浮点数
5.算数运算
add 加 sub 减 mul 乘 div除法 rem 模运算 neg 取反运算
两个特殊值:无限(infinity)和NaN(Not a Number) 0*infinity=NaN
0/0=NaN infinity/infinity=NaN x/infinity=0
6.位操作
and 按位与 or 按位或 xor 按位异或 not 按位求反
7 移位操作
shl 左移位 shr 右移位 shr.un无符号右移位
8.转换指令
从栈顶取值,进行相应转换,将结果入栈。
conv.[类型]
conv.ovf.[类型]
9.逻辑条件检测
ceq 是否相等 cgt[.un] 是否大于 clt [.un]是否小于 ckfiniter 取栈顶元素。当为NaN infinity抛出异常,否则入栈
10.块操作
cpblk 复制一块内存 initblk 初始化一块内存
参数、局部变量与字段寻址
方法参数载入
ldarg 载入一个方法参数
ldarg.s短指令格式
ldarg[.0~4]载入第0~4个参数
方法参数地址载入 :ldarga[.s] 载入参数并入栈
方法参数存储: starg[.s] 从栈顶取一个参数并存入参数
方法参数列表 arglist 取得方法参数句柄 。。
局部变量载入 ldloc[.s][.0~3]载入第n个局部变量
局部变量引用载入 ldloca[.s] 载入局部变量的引用并入栈
局部变量保存 stloc[.s] 从栈顶取值并存入局部变量
局部内存块分配 localloc 分配一块局部内存块
方法调用
直接调用:
jmp <token> (..) 放弃当前方法执行,直接跳转至 token所指目标方法。
call <token>(..) 以不通过v-able的方式调用一个instance或static方法
callvirt<token>通过实例的v-table调用oken所指的方法。
间接调用
ldftn ldvirtftn calli
尾部调用
tail
类与值操作
ldnull 读取空的对象引用并入栈
ldobj 从堆栈获取值类型的托管指针
stobj 依次从堆栈取得值类型的值
ldstr 读取一个字符串
newobj 分配内存创建某个类的新实例
box 装箱 unbox 拆箱