Dalvik指令集

Dalvik是Google公司自己设计的用于Android平台的虚拟机。Dalvik虚拟机是Google等厂商合作开发的Android移动设备平台的核心组成部分之一。它可以支持已转换为 .dex(即Dalvik Executable)格式的Java应用程序的运行,.dex格式是专为Dalvik设计的一种压缩格式,适合内存和处理器速度有限的系统。Dalvik 经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个Dalvik 应用作为一个独立的Linux 进程执行。独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。一段Dalvik汇编代码由一系列Dalvik指令组成,指令语法由指令的位描述与指令格式标识来决定。

Dalvik虚拟机是基于寄存器架构的,数据的访问通过寄存器单间直接传递。对java的每个线程都有一个pc计数器和一个java栈。Pc计数器类似arm cpu中的PC寄存器和x86 cpu中的IP寄存器,不同的是,PC计数器只对当前方法有效。

语 法 含 义
V void,只用于返回值类型
Z boolean
B byte
S short
C char
I int
J long
F float
D double
L Java类类型
[ 数组类型

1、空操作指令

助记符为nop,值为00,通常用来做对齐代码,无实际操作。

2、数据操作指令 move

3、返回指令 return

“return-void”表示函数从一个void方法返回。

“return vAA表示函数返回一个32位非对象类型的值,回值寄存器为8位的寄存器vAA

“return-wide vAA表示函数返回一个64位非对象类型的值。返回值为8位的寄存器对vAA

“return object vAA”表示函数返回一个对象类型的值。返回值为8位的寄存器vAA

4、数据定义指令 const

用来定义程序中用到的常量、字符串、类等数据。

“const/high16 vAA,#+B000”将数值右边零扩展为32位后赋给寄存器vAA。

“const-wide/16 vAA,#+BBBB”将数值符号扩展为64位后赋给寄存器对vAA.

“const-wide/32 vAA,#+BBBBBBBB”将数值符号扩展为64位后赋给寄存器对vAA

“const-wide vAA,#+BBBBBBBBBBBBBBBB”将数值赋给寄存器对vAA

“const–wide/high16 vAA,#+BB00000000将数值右边零扩展为64位后赋给寄存器对vAA

“cost-string vAA, string@BBBBBBBB”通过字符串索引构造一个字符串并赋给寄存器vAA

“const-string-/jumbo vAA, string@BBBBBBBB”通过字符串索引(较大)构造一个字符串并赋给寄存器vAA。

“const-class- vAA,type@BBBB”通过类型索引获取一个类引用并赋给寄存器vAA

“const-class-/jumbo vAAAA,type@BBBBBBBB”通过给定的类型索引获取一个类引用并赋给寄存器 VAAAA.这条指令占用两个字节,值为 OxOOff( Android 4.4.0中新增的指)

5、实例操作指令

类型转换,检查,新建。

“check-cast vAA,type@BBBB”将vAA寄存器中的对象引用转换成指定的类型,如果失败会抛出 ClassCastException异常。如果类型B指定的是基本类型,对于非基本类型的A来说,运行时始终会失败。

”instance-of vA,vB,type@CCCC”判断vB寄存器中的对象引用是否可以转换成指定的类型,如果可以vA寄存器赋值为1,否则vA寄存器赋值为0。

“new-instance VAA,type@BBBB”构造一个指定类型对象的新实例,并将对象引用赋值给vAA寄存器,类型符tpe指定的类型不能是数组类.

“check-cast/jumbo VAAAA, type@BBBBBBBB”指令功能与“ check-cast vAA,type@BBBB”相同,只是寄存器值与指令的索引取值范围更大( Android4.0中新增的指令)

“ instance-of/jumbo VAAAA, VBBBB,type@ CCCCCCCC”指令功能与“ instance-of vA ,vB ,type @cCCC”相同,只是寄存器值与指令的索引取值范围更大( Android40中新增的指令)。

“new-instance/jumbo VAAA/,type@ BBBBBBBB”指令功能与“new- instance VAA,type@BBBB”相同,只是寄存器值与指令的索引取值范围更大( Android4.0中新增的指令)

6、锁指令 monitor (多用在多线程程序中对同一对象的操作)

“monitor-enter vAA”为指定的对象获得锁。

“monitor -exit vAA” 释放指定的对象的锁。

7、数组操作指令

包括数组长度,新建数组,数组赋值,数组元素取值与赋值等操作。

“array length vA,vB”获取给定vB寄存器中数组的长度并将值赋给vA寄存器,数组长度指的是数组的条目个数。

“new- array vA,vB,type@CCCC”构造指定类型( (type@CCCC)与大小(vB)的数组并将值赋给vA寄存器.

“ filled-new array{ vC,vD,vE,vF},type@BBBB构造指定类型( type@BBBB))与大小(vA)的数组填充数组内容。vA寄存器是隐含使用的,除了指定数组的大小外还指定了参数的个数,vC~vG是使用到的参数寄存器序列。

“filled-new-array/range{vCCCC..vNNNN),type@BBBB指令功能与“ filled-new array{vC,vD,vE, vfF,vG},type@BBBB”相同,只是参数寄存器使用range字节码后指定了取值范围,vC是第一个参数寄存器,N=A+C-1。

“filled-array-data vAA,+ BBBBBBBB”用指定的数据来填充数组,vAA寄存器为数组引用,引用必须为基础类型的数组,在指令后面会紧跟一个数据表。

“new- -array/jumbo vAAAA, VBBBB,type@CCCCCCCC指令功能与“new- array vA,vB,type @CCCC相同,只是寄存器值与指令的索引取值范围更大( Android4.0中新增的指令)

“filled new-array/jumb{vCCCC..vNNNN),type@BBBBBBBB指令功能与 filled-new-array/range fvCCCC.nn), type@BBBE”相同,只是索引取值范围更大( Android4.0中新增的指令)。

“ arrayop vAA,vBB,vCC对vBB寄存器指定的数组元素进入取值与赋值。vCC寄存器指定数组元素索引,vAA寄存器用来存放读取的或需要设置的数组元素的值。读取元素使用aget类指令,元素赋值使用aput类指令,根据数组中存储的类型指令后面会紧跟不同的指令后,指令列表有aget aget-wide, aget-object, aget-boolean, aget-byte, aget-char aget-short, aput, aput-wide, aput-object, aput-boolean, aput-byte,aput-char,aput-short.

8、异常指令 抛出异常

“throw vAA”抛出vAA寄存器中指定类型的异常。

9、跳转指令 当前地址跳转到指定的偏移量。

1) 无条件跳转(goto)

“goto +AA”无条件跳转到指定偏移处,偏移量AA不能为0.

“goto/16 +AAAA”无条件跳转到指定偏移处,偏移量AAAA不能为0.

“goto/32 +AAAAAAAA”无条件跳转到指定偏移处.

2) 分支跳转(switch)

“ packed- switch vAA +BBBBBBBB”分支跳转指令。vAA寄存器为 switch分支中需要判断的值, BBBBBBBB脂向一个 packed-switch- payload格式的偏移表,表中的值是有规律递增的。

“ sparse- switch VAA,+ BBBBBBBB”分支跳转指令。vAA寄存器为 switch分支中需要判断的值, BBBBBBBB指向一个 sparse-switch- payload格式的偏移表,表中的值是无规律的偏移量。

3)条件跳转 (if)

“if-eq”如果vA等于vB则跳转。Java语法表示为“if(vA==vB)

“if-ne”如果v不等于vB则跳转 Java语法表示为“if(vA!=vB)

“if-lt”如果vA小于vB则跳转 Java语法表示为“if(vA<VB)”

“if-ge”如果vA大于等于vB则跳转 Java语法表示为“if(vA>=vB)

“if-gt”如果vA大于vB则跳转。 Java语法表示为“if(vA>VB)”

“if-le”如果vA小于等于vB则跳转。Java法表示为“if(vA<=vB)

“if -testz vAA,+BBBB”条件跳转指令。拿AA寄存器与0比较,如果比较结果满足或值为0时就跳转到BBBB指定的偏移处。偏移量BBBB不能为0。

if- test类型的指令有以下几条:

“ if-eqz如果vAA为0则跳转。Java语法表示为“if(!vAA)”

“if-nez”如果vAA不为0则跳转。Java语法表示为“if(vAA)”

“if-ltz”如果vAA小于0则跳转。Java语表示为“if(vAA<0)”

“if-gez”如果vAA大于等于0则跳转。Java语法表示为“if(vAA>=0)

“if-gtz如果vAA大于0则跳转。Java语法表示为“if(vAA>0)

“if-lez”如果vAA小于等于0则跳转。Java语法表示为“if(vAA<=0)

10、比较指令 对两个寄存器的值进行比较

格式:compkind vAA,vBB,vCC

注:vBB和vCC是两个需要比较的寄存器,并把比较的结果放到vAA寄存器中

“cmpl-float”比较两个单精度浮点数。如果vBB寄存器大于vCC寄存器,则结果为-1,相等则结果为0,小于的话结果为1.

“cmpg-float”比较两个单精度浮点数。如果vBB寄存器大于vCC寄存器,则结果为1,相等则结果为0,小于的话结果为-1。

“cmpl-double”比较两个双精度浮点数。如vBB寄存器对大于vCC寄存器对,则结果为-1,相等则结果为0,小于的话结果为-1。

“cmpg-double””比较两个双精度浮点数。如果vBB寄存器对大于vCC寄存器对,则结果为1,相等则结果为0,小于的话结果为-1。

“cmp-long”比较两个长整型数。如果vBB寄存器大于vCC寄存器,则结果为1,相等则结果为0,小于的话结果为1。

11、字段操作指令 用来对对象实例的字段进入读写操作

1)普通字段指令前缀为i,静态字段的指令前缀为是s。例:iget 、iput

2)根据访问字段类型不同,字段操作指令后面会紧跟字段类型的后缀。例i get-byte读取实例字段的值的类型为字节类型、iput-short 设置实例字段的值类型为短整型。

3)普通字段指令:iget、iget-wide、iget-object、iget-boolean、iget-byte、iget-char、iget-short、iput、iput-wide、iput-object、iput-boolean、iput-byte、iput-char、iput-short。

4)静态字段操作指令:sget、sget-wide、sget-object、sget-boolean、sget-byte、sget-char、sget-short、sput、sput-wide、sput-object、sput-boolean、sput-byte、sput-char、sput-short。

12、方法调用指令 负责调用类实例 invoke

格式:“nvokek-ind{vC,vD,vE,vF,vG},meth@BBBB”、“invokekind/rangce{vCCCC..vNNNN},meth@B BBB”两类,两类指令在作用上并无不同,只是后者在设置参数寄存器时使用了range来指定寄存器的范围。

13、数据转换指令 将一种类型的数值转换成另一种类型。

格式:unop,vA,vB vB寄存器或vB寄存器对存放需要转换的数据,转换后的结果保存在vA寄存器或vA寄存器中。

“neg-int”对整型数求补。

“not-int”对整形数求反。

“neg-long”对长整型数求补。

“not-long”对长整型数求反。

“neg-float”对单精度浮点数求补。

“neg-double”对双精度浮点型数求补。

“int-to-long”将整型换为长整型。

“int-to-float”将数转换为单精度浮点型 。

“int.-to-double”将整型数转换为双精度浮点型 。

“long-to-int”将长整型数转换为整型。

“long-to-float”长型数转换为单精度浮点型。

“long-to- double”将长型数转换为双精度浮点型。

“float-to-int”将单精度点型数转换为整型。

“float-o-long”将单精度浮点型数转换为长整型。

“float-to-double将单精度浮点型数转换为双精度浮点型。

“double-to-int”将双精度浮点型数转换为整型。

“double-to-long”将双精度浮点型数转换为长型。

“double.-to-float”将双精度浮点型数转换为单精度浮点型 。

“int-to-byte”将整型转换为字节型。

“int-to-char”将整形转换为字符串。

“int-to-short”将整型转换为短整型。

14、数据运算指令

包括算数运算指令和逻辑运算指令

数据运算指令有如下四类

1)binop vAA,vBB,vCC"将vBB寄存器与vCC寄存器进行运算,结果保存到AA寄存器。

2)binop/2 addr vA,vB”将vA寄存器与vB寄存器进行运算,结果保存到vA寄存器 。

3)binp/lit6 vA,vB,#+CCCC”将vB寄存器与常量CCCC进行运算,结果保存到vA寄存器。

4)binp/lit VAA, VBB,#+CC"将vBB寄存器与常量CC进行运算,结果保存到vAA寄存器。

后面3类指令比第1类指令分别多出了2addr、lit6、lit8等指令后缀。

四类指令中基础字节码相同的指令执行的运算操作是类似的。第1类指令中,根据数据的类型不同会在基础字码后面加上数据类型后缀,如-int或-long分别表示操作的数据类型为整型与长整型。

第一类指令可归类如下

“add-type”vBB寄存器与vCC寄存器值进行加法运算(vBB+vCC)。

“sub-type”vBB寄存器与vCC寄存器值进行减法运算(vBB-vCC)。

“mul-type”vBB寄存器与vCC寄存器值进行乘法运算( vBB x vCC) 。

“div-type”vBB寄存器与vCC寄存器值进行除法运算(vBB/vCC)。

“rem-type"BB寄存器与vCC寄存器值进行模运算(vBB%vCC)。

“and-type"vBB寄存器与vCC寄存器值进行与运算( vBB AND vCC)。

“ or-type"BB寄存器与vCC寄存器值进行或运算( VBB OR VCC)。

“xor-type"vBB寄存器与CC寄存器值进行异或运算( VBB XOR VCC)。

“shi-type"vBB寄存器值(有符号数)左移vCC位(vBB<<vCC)。

“shr-type"wB寄存器值(有符号数)右移vCC位(vBB>>CC)。

“ ushr-type"wBB寄存器值(无符号数)右移vCC位(vBB>>vCC)。

其中基础字节码后面的-type可以是-int、-long,-float,-double。