原文链接:https://source.android.com/devices/tech/dalvik/dalvik-bytecode.html

一、总体设计

(1)虚拟机模型(machine model)和调用规则(calling conventions)是为了更好的模仿普通真实的架构和C风格的调用规则:

a.虚拟机是基于寄存器的,并且框架(frames)被创建后大小是固定的。每一个框架(frame)组成部分包括:特定数量的寄存器(由方法指定)和任何在执行方法时需要的辅助数据,例如(但是并不仅限于)程序计数器和对包含该方法.dex文件的一个引用。

b.当用于位值(bit values)时(例如整数和浮点数),寄存器是由32 位的。寄存器对用于64位数值。对寄存器对的排列(aliggment)没有要求。

c.当用于对象引用时,寄存器被认为有足够的空间正确的存放该引用。

d.按位表示,(Object)null == (int)0.

e.对于方法中的N的参数,会按顺序存放到方法执行框架(frame)中后N个寄存器中。较大参数使用使用两个寄存器。实例方法第一个参数为this。

(2)指令流中的存储单位是一段16位无符号bit。一些指令中的一些bit被弃用或者被置零。

(3)指令没有必要被限制在一个特定类型。例如,指令move 32-bit并没有指定移动的是int还是float。

(4)分别的枚举和索引常量池,包括:String、types、fields、methods。

(5)指令流中的bitwise literal data被顺序(in-line)表示。

(6)实际上,需要大于16个寄存器的方法是不常见的,并且由于需要大于8个寄存器是合理正常的,很多指令被限制仅寻址前16个寄存器。理论上,指令允许引用前256个寄存器。另外,一些指令的变体会需要更多的寄存器数量,其中全方位(catch-all)move指令可以寻址v0-v65536.在指令变体不可用的情况下寻址期望的寄存器,期望期存器的内容从原始寄存器移动到一个low寄存器(操作之前)及(或)从一个low结果寄存器移动到high寄存器(操作之后)。

(7)"pseudo-instruction"指令被用来存储变长(variable-length)数据,被称为regular instruction(例如:fill-array-data)。这些指令在正常的执行流期间从不会遇到。另外这些指令必须被位于偶数字节码偏移量(也就是说4字节对齐)。为了满足这个需求,dex生成工具必须发行一个额外的nop指令作为一个空间块(spacer)用来对齐这些指令。最后,尽管不需要,人们期望大多说工具将选择在方法的结尾发行这些指令,否则其他的指令会出现分歧。

(8)一旦被安装到一个运行的系统中,一些指令也许会被改变,由于安装时静态链接优化会改变指令的形式。一旦知道可链接这些指令会被更快的执行。

(9)Humman-syntax 和 mnemonics(助记符)

a.参数的顺序:dest then source

b.一些操作符有一个消除二义性名称的后缀,来指明操作的类型:

    I:普通类型32位操作符没有标记;

    II:普通类型64位操作符后缀为-wide;

    III:特定类型的后缀是它们的类型(或是是简单的缩写):-boolean、-byte、-char、-short、

        -int、-long、-float、-double、-object、-string、-class、-void;

c.一些操作符有消除二义性的后缀来分辨otherwise-identical操作,这些操作有不同的指令形式和选项。这些后缀用‘/’于主名称(main name)分离并且大部分在生成可执行代码中与静态常量一对一映射(减少歧义)。

d.这里的描述中,一个数值的宽度(width)(例如一个常量的范围或者被寻址的寄存器的数量)被强调使用每个字符使用4bit的宽度。

e.例如在指令:move-wide/from16 VAA,VBBBB

    ①:move,是基本操作符,说明基本的操作

    ②:wide,是名称后缀,说明它操作的是64位数据

    ③:from16,是操作符后缀,说明具有16位寄存器变量的引用作为source

    ④:vAA,是目的寄存器(被操作隐藏,目的参数总是处于第一位),范围:v0-v255;

    ⑤:vBBBB,是源寄存器,范围:v0-v6535

二、字节码集概述

Op & FormatMnenonic/Syntax(助记符/语法)Arguments(参数)Description(说明)
00 10xnop
空循环,没有操作,表示一条指令执行的时间
01 12xmove vA,vB

A:目标寄存器(4bit)

B:源寄存器(4bit)

将一个非对象寄存器的内容转移到另一个寄存器中
02 22xmove/from16 vAA,vBBBB

A:目标寄存器(8bit)

B:源寄存器(16bit)

同上
03 32xmove/16 vAAAA,vBBBB

A:目标寄存器(16bit)

B:源寄存器(16bit)

同上
04 12xmove-wide vA,vB

A:目标寄存器(4bit)

B:源寄存器对(4bit)

将一个寄存器对中的内容移动到另一个寄存器对中。

注意:从vN移动到vN-1或者vN+1是合法的,所以在向寄存器对中写数据之前,寄存器的两部分必须是可读的。

05 22x
move-wide/from16 vAA,vBBBB

A:目标寄存器对(8bit)

B:源寄存器对(16bit)

同上
06 32x
move-wide/16 vAAAA,vBBBB

A:目标寄存器对(16bit)

B:源寄存器对(16bit)

同上
07 12x
move-object vA,vB

A:目标寄存器(4bit)

B:源寄存器(4bit)

将对象寄存器中的内容转移到另外一个寄存器中
08 22x
move-object/from16 vAA,vBBBB

A:目标寄存器(8bit)

B:源寄存器(16bit)

同上
09 32x
move-object/16 vAAAA,vBBBB

A:目标寄存器(16bit)

B:源寄存器(16bit)

同上
0a 11x
move-result vAAA:目标寄存器(8bit)将最近invoke-kind指令的单字(single-word)、非对象(non-object)的结果转移到指定的寄存器中。在invoke-kind指令(结果不可忽略)之后立即执行该指令;在其他地方执行是无效的
0b 11x
move-result-wide vAAA:目标寄存器(8bit)将最近invoke-kind指令的双字(double-word)结果转移到指定的寄存器对。在invoke-kind指令(结果不可忽视)之后;在其他地方执行时无效的
0c 11x
move-result-object vAAA:目标寄存器(8bit)将最近invoke-kind指令的object结果转移到指定的寄存器中。该指令必须在invoke-kind或者filled-new-array之后立即执行,它们的(object)结果不可忽略并且在其他地方执行时无效
0d 11x
move-exception vAAA:目标寄存器(8bit)将一个仅被catch到的异常保存到指定的寄存器中。该指令必须是任何异常处理的第一条指令,该异常处理捕获到异常是不能忽略的,并且该指令必须只能作为异常处理器的第一条指令;其他地方执行时无效
0e 10x
return void
从一个void方法返回
0f 11x
return vAAA:返回值寄存器(8bit)从一个32位、非对象、有返回值得方法中返回
10 11x
return-wide vAAA:返回值寄存器对(8bit)从一个64位、有返回值的方法返回
11 11x
return-object vAAA:返回值寄存器(8bit)从一个有对象返回的方法中返回
12 11n
const/4 vA,#+B

A:目标寄存器(4bit)

B:有符号整数(4bit)

将有符号字符数据(32bit)转移到指定的寄存器中
13 21s
const/4 vAA,#+BBBB

A:目标寄存器(8bit)

B:有符号整数(16bit)

将给定的有符号总非数据(32bit)转移到指定的寄存器中
14 31i
const vAA,#+BBBBBBBB

A:目标寄存器(8bit)

B:任意32位常量

将执行的字符数值转移到指定的寄存器中
15 21h
const/high16 vAA,#+BBBB0000

A:目标寄存器(8bit)

B:有符号的整数(16bit)

将给定的字符值(right-zero-extended to 32)转移到指定的寄存器中
16 21s
const-wide/16 vAA,#+BBBB

A:目标寄存器(8bit)

B:有符号整数(16bit)

将给定的字符值转移到指定的寄存器对中
17 31i
const-wide/32 vAA,#+BBBBBBBB

A:目标寄存器(8bit)

B:有符号整数(32位)

将给定的字符值(有符号扩展到64位)转移到指定的寄存器对中
18 51l
const-wide vAA,#+BBBBBBBBBBBBBBBB

A:目标寄存器(8bit)

B:任意64位常量

将给定的字符值转移到指定的寄存器对中
19 21h
const-wide/high16 vAA,#+BBBB000000000000

A:目标寄存器(8bit)

B:有符号整数(16bit)

将给定的字符值(right-zero-extended to 64bit)转移到特定的寄存器对中
1a 21c
const-string vAA,string@BBBB

A:目标寄存器(8bit)

B:string index

将通过给定的ndex指定的string引用转移到指定的寄存器 中
1b 31c
const-string/jumbo vAA,string@BBBBBBBB

A:目标寄存器(8bit)

B:字符串索引

同上
1c 21c
const-class vAA,type@BBBB

A:目标寄存器(8bit)

B:类型索引

将指定索引的特定类型的引用转移到特定寄存器中,在这种情况中指定类型是原始类型,该指令将会把原始类型的degenerate类
1d 11x
monitor-enter vAAA:引用寄存器(8bit)获取指定对象的monitor
1e 11x
monitor-exit vAA同上

释放指定对象的monitor

注意:如果该指令需要抛出异常,它必须就像pc中那些先进的指令一样。它可以有利于判断该指令是否成功的执行,并且要不活该异常必须在该指令执行之后和下一个指令执行之前。这样定义使得一个使用monitor的方法随着monitor被代码块自己清楚而try-catch块也被清除成为可能,as a way to handle the    arbitrary exceptions that might get thrown due to the historical    implementation of Thread.stop()while still managing    to have proper monitor hygiene.

1f 21c
check-cast vAA,type@BBBB

A:存储引用的寄存器(8bit)

B:类型索引(16bit)

如果给定寄存器中的引用不能转化为指定的类型则抛出一个classCastException;

注意:因为A必须是一个引用(不是一个原始值),如果B是一个原始类型在runtime阶段会抛出一个异常。

20 22c
instance-of vA,vB, type@CCCC

A:目标寄存器(4bit)

B:引用寄存器(4bit)

C:类型索引(16bit)

如果指定引用是一个给定类型的实例则目标寄存器中存放1,否则存放0;

注意:因为B必须是一个引用(不是原始类型数值),如果是一个原始类型则寄存器的结果总是0;

21 12x
array-length vA,vB

A:目标寄存器(4bit)

B:数组引用寄存器(4bit)

将指定数组的长度存储到给定的目标寄存器中
22 21c
new-instance vAA,type@BBBB

A:目标寄存器(8bit)

B:类型索引

构造一个指定类型的新实例,并将它的引用存放到指定目标寄存器中。该类型必须是非数组类
23 22c
new-array vA,vB,type@CCCC

A:目标寄存器(8bit)

B:尺寸(size)寄存器

C:类型索引

构造一个执行类型和大小的新的数组,该类型必须是一个数组类型
24 35c
filled-new-array{vC,vD,vE,vF,vG},type@BBBB

A:数组大小和参数字数量(4bit)

B:类型索引(16bit)

C...G:参数寄存器(4bit)

根据给定的类型和大小构造一个数组,用提供的内容赋值。类型必须是一个数组类型。数组的内容必须是单字(single-word)(也就是说没有long或者double数组,但是引用类型是可以接受的)。构造实例被作为结果存储起来,就像方法执行指令保存它们的结果一样,所以构造出来的实例必须立即在随后使用move-result-object指令转移到一个寄存器中。
25 3rc
filled-new-array/range{vCCCC..vNNNN},type@BBBB

A:数组的胆小和参数字的数量(8bit)

B:类型索引(16bit)

C:第一个参数寄存器(16bit)

N = A + C - 1

构造一个指定类型和大小的数组,使用指定的内容赋值。阐述和约束与filled-new-array一样,同上
26 31t
fill-array-data vAA,+BBBBBBBB

A:数组的引用(8bit)

B:signed "branch" offset to table data pseudo-instruction    (32 bits)

使用指定的数据给给定的数组赋值。该引用必须是一个原始数据数组并且数据表必须匹配它的类型并且表中的数据数量不多于数组的大小。也就是说数组大于表的大小,only the initial    elements of the array are set, leaving the remainder alone
27 11x
throw vAAA:异常寄存器(8bit)抛出指定异常
28 10t
goto +AAA:带符号的分支偏移量(8bit)

无条件的跳转到指定的指令;

注意:分支的偏移量不可为0,

29 20tgoto/16 +AAAAA:带符号的分支偏移量(16位)同上
2a 30t
goto/32 +AAAAAAAAA:带符号的分支偏移量(32位)同上
2b 31tpacked-switch vAA,+BBBBBBBB

A:测试寄存器

B:数据表伪指令有符号分支偏移量(32bit)

在给定寄存器的基础上跳转到一个新的指令,使用一个偏移量表对应特定整数范围中的每个值,或者如果没有匹配的数值下一条指令会落空。
2c 31t
sparse-switch vAA,+BBBBBBBB

A:测试寄存器

B:数据表伪指令有符号分支偏移量(32bit)

在给定寄存器的基础上跳转到一个新的指令,使用一个值-偏移量对的顺序,或者如果没有匹配的数值下一条指令会落空。
2d..31 23x

cmpkind vAA,vBB,vCC

2d:cmpl-float(lt bias)

2e:cmpg-float(gt bias)

2f:cmpl-double(lt bias)

30:cmpg-double(gt bias)

31:cmp-long

A:目标寄存器(8bit)

B:第一个源寄存器或寄存器对

C:第二个源寄存器或寄存器对

执行指定浮点或long比较,如果b==c则a=0;如果b>c,a=1;ruguo b<c,a=-1.浮点操作的“bias”表明如何进行NaN比较:"gt bias"指令返回1,"lt bias"指令返回-1.例如,使用cmpg-float指令比较x<y,结果为-1表明测试正确,如果是其他结果则表明测试不正确或者无效(无效的比较或者其中一个为NaN)
32..37 22t

if-test vA,vB,+CCCC

32:if-eq

33:if-ne

34:if-lt

35:if-ge

36:if-gt

37:if-le

A:测试的第一个寄存器(4bit)

B:测试的第二个寄存器(4bit)

C:有符号的分支偏移量(16bit)

如果两个寄存器的值比较结果确定则指定目标位置的分支确定。

注意:分支的偏移量不能为0;

38..3d 21t

if-testz vAA,+BBBB

38:if-eqz

39:if-nez

3a:if-ltz

3b:if-gez

3c:if-gtz

3d:if-lez

A:测试寄存器(8bit)

B:有符号的分支偏移量(16bit)

如果两个寄存器的值比较结果确定则指定目标位置的分支确定。

注意:分支的偏移量不能为0

3e..43 10x
未使用
未使用
44..5123c

arrayop vAA,vBB,vCC

44:aget

45:aget-wide

46:aget-object

47:aget-boolean

48:aget-byte

49:aget-char

4a:aget-short

4b:aput

4c:aput-wide

4d:aput-object

4e:aput-boolean

4f:aput-byte

50:aput-char

51:aput-short

A:值寄存器或寄存器对;可以是源也可以是目标(8bit)

B:数组寄存器(8bit)

C:索引寄存器(8bit)

在给定数组指定的索引处执行指定的数组操作,把结果存到值寄存器中
52..5f 22c

iinstaceop vA,vB,field@CCCC

52:iget

53:iget-wide

54:iget-object

55:iget-boolean

56:iget-byte

57:iget-char

58:iget-short

59:iput

5a:iput-wide

5b:iput-object

5c:iput-boolean

5d:iput-byte

5e:iput-char

5f:iput-short

A:值寄存器或寄存器对,可以是源或者目标(4bit)

B:对象寄存器(4bit)

C:实例域的引用索引(16bit)

执行拥有特定域的对象实例域的操作,将结果保存到值寄存器中。

注意:这些操作码对静态链接来说是合理的选择,修改域参数将成为更直接的位移

60..6d 21c

sstaticop vAA,field@BBBB

60:sget

61:sget-wide

62:sget-object

63:sget-boolean

64:sget-byte

65:sget-char

66:sget-short

67:sput

68:sput-wide

69:sput-object

6a:sput-boolean

6b:sput-byte

6c:sput-char

6d:sput-short

A:值寄存器或寄存器对,可以使源或目标(8bit)

B:静态域引用的索引(16bit)

执行拥有指定静态域的对象的静态域操作,保存结果到值寄存器中;

注意:同上

6e..72 35c

invoke-kind{vC,vD,vE,vF,vG},meth@BBBB

6e:invoke-virtual

6f:invoke-super

70:invoke-direct

71:invoke-static

72:invoke-interface

A:参数字数(4bit)

B:方法的引用索引(16bit)

C..G:参数寄存器(4bit)

调用指定方法。在随后立即执行的指令中使用合适的move-result*来保存结果。

invoke-virtual:被用来执行一个一般的virtual method(不是private/static/final,也不是一个构造方法);

invoke-super:被用来执行一个最亲近父类的virtual method(与调用类有相同的method_id)。与invoke-virtual有相同的方法约束。

invoke-direct:被用来执行一个非静态的direct method(一个实例自己方法,非重写的方法,或是一个私有的实例方法或者是构造方法)

invoke-static:被用来执行一个静态方法,且这个方法是一个direct method。

invoke-interface:用来执行interface method,也就是说一个具体类型未知的对象,使用一个接口的method_id.

注意:同上

73 10x 
未使用
未使用
74..78 3rc

invoke-kind/range{vCCCC..cNNNN},meth@BBBB

74:invoke-virtual/range

75:invoke-super/range

76:invoke-direct/range

77:invoke-static/range

78:invoke-interface/range

A:参数字数(8bit)

B:方法的引用索引(16bit)

C:第一个参数寄存器(16bit)

N=A+C-1

调用指定的方法。同上

79..7a 10x

未使用
未使用
7b..8f 12x

unop vA,vB

7b:neg-int

7c:not-int

7d:neg-long

7e:not-long

7f:neg-float

80:neg-double

81:int-to-long

82:int-to-float

83:int-to-double

84:long-to-int

85:long-to-float

86:long-to-double

87:float-to-int

88:float-to-long

89:float-to-double

8a:double-to-int

8b:double-to-long

8c:double-to-float

8d:int-to-byte

8e:int-to-char

8f:int-to-short

A:目标寄存器或寄存器对(4bit)

B:源寄存器或寄存器对(4bit)

在源寄存器上执行一个一元的操作,将结果保存到目标寄存器中

90..af 23x

binop vAA,vBB,vCC

90:add-int

91:sub-int

92:mul-int

93:div-int

94:rem-int

95:and-int

96:or-int

97:xor-int

98:shl-int

99:shr-int

9a:ushr-int

9b:add-long

9c:sub-long

9d:mul-long

9e:div-long

9f:erm-long

a0:add-long

a1:or-long

a2:xor-long

a3:shl-long

a4:shr-long

a5:ushr-long

a6:add-float

a7:sub-float

a8:mul-float

a9:div-float

aa:rem-float

ab:add-double

ac:sub-double

ad:mul-double

ae:div-double

af:rem-double

A:目标寄存器或寄存器对(8bit)

B:第一个源寄存器或寄存器对(8bit)

C:第二个源寄存器或寄存器对(8bit)

在两个源寄存器上执行指定的双目操作,将结果存储到目标寄存器中。

注意:与其他的-long数学操作(它们的第一和第二源都是寄存器对),shl-long,shr-long和ushr-long的第一个源使用寄存器对,但是第二个源使用单个寄存器。

b0..cf 12x

binop/2addr vA,vB

b0:add-int/2addr

b1:sub-int/2addr

b2:mul-int/2addr

b3:div-int/2addr

b4:rem-int/2addr

b5:and-int/2addr

b6:or-int/2addr

b7:xor-int/2addr

b8:shl-int/2addr

b9:shr-int/2addr

ba:ushr-int/2addr

bb:add-long/2addr

bc:sub-long/2addr

bd:mul-long/2addr

be:div-long/2addr

bf:erm-long/2addr

c0:add-long/2addr

c1:or-long/2addr

c2:xor-long/2addr

c3:shl-long/2addr

c4:shr-long/2addr

c5:ushr-long/2addr

c6:add-float/2addr

c7:sub-float/2addr

c8:mul-float/2addr

c9:div-float/2addr

ca:rem-float/2addr

cb:add-double/2addr

cc:sub-double/2addr

cd:mul-double/2addr

ce:div-double/2addr

cf:rem-double/2addr

A:目标和第一个源寄存器或寄存器对(4bit)

B:第二个源寄存器或寄存器对(4bit)

在两个源寄存器上执行指定的双目操作,并家结果保存到第一个源寄存器中。

注意:与其他的-long/2addr数学运算操作(它们目标或第一个源和第二个源都使用寄存器对)相反,shl-long/2addr,shr-long/2addr和ushr-long/2addr,它们的目标和第一个源都使用寄存器对,但是它们的第二个源使用单个寄存器

d0..d722s

binop/lit16 vA,vB,#+CCCC

d0:add-int/lit16

d1:rsub-int(reverse subtract)

d2:mul-int/lit16

d3:div-int/lit16

d4:rem-int/lit16

d5:and-int/lit16

d6:or-int/lit16

d7:xor-int/lit16

A:目标寄存器(4bit)

B:源寄存器(4bit)

C:有符号的整数常量(16bit)

在指定的寄存器和字符上执行指定的双目操作,并将结果保存到目标寄存器中。

注意:rsub-int does not have a suffix since this version is the main opcode of its family.

d8..e2 22b

binop/lit8 vAA,vBB,#+CC

d8:add-int/lit8

d9:rsub-int/lit8

da:mul-int/lit8

db:div-int/lit8

dc:rem-int/lit8

dd:and-int/lit8

de:or-int/lit8

df:xor-int/lit8

e0:shl-int/lit8

e1:shr-int/lit8

e2:ushr-int/lit8

A:目标寄存器(8bit)

B:源寄存器(8bit)

C:有符号的整数常量(8bit)

同上

e3..ff

10x

未使用
未使用

三、sparse-switch-payload format

四、fill-array-data-payload format

五、数学操作说明

注意:浮点操作必须符合IEEE754规则,using round-to-nearest and gradual underflow,except where stated otherwise

操作码C 语义注意
neg-int

int32 a;

int32 result=-a;

补码
not-int

int32 a;

int32 result=~a;

反码
neg-long

int64 a;

int64 result=-a;

补码
not-long

int64 a;

int64 result=~a;

反码
neg-float

float a;

float result=-a;

negation
neg-double

double a;

double result=-a;

negation
int-to-long

int32 a;

int64 result=(int64)a;

有符号的扩展:int32->int64
int-to-float

int32 a;

float result=(float)a;

使用round-to-nearest进行int32到float的转换。会丢失精度。
int-to-double

int32 a;

double result=(double)a;

int32->double
long-to-int

int64 a;

int32 result=(int32)a;

int64->int32
long-to-float


long-to-double


float-to-int

float a;

int32 result=(int32)a;


float-to-long

float a;

int64 result=(int64)a;


float-to-double

float a;

double result = (double)a;


double-to-int

double a;

int32 result = (int32)a;


double-to-long

double a;

int64 result=(int64)a;


double-to-float

double a;

float result=(float)a;


int-to-byte

int32 a;

int32 result=(a<<24)>>24


int-to-char

int32 a;

int32 result=a&0xffff


int-to-short

int32 a;

int32 result=(a<<16)>>16


add-int

int32 a,b;

int32 result = a + b;


sub-int 

int32 a,b;

int32 result = a-b;


rsub-int

int32 a,b;

int32 result=b-a;


mul-int

int32 a,b;

int32 result=a*b;


div-int

int32 a,b;

int32 result=a/b;


rem-int

int32 a,b;

int32 result = a%b;


and-int

int32 a,b;

int32 result=a&b;


or-int

int32 a,b;

int32 result=a|b;


xor-int

int32 a,b;

int32 result=a^b;


shl-int

int32 a,b;

int32 result=a<<(b&0x1f)


shr-int

int32 a,b;

int32 result=a>>(b&0x1f)


ushr-int

uint32 a,b;

int32 result=a>>(b&0x1f)


long......int64...
add-float

float a,b;

float result = a+b;


sub-float

float a,b;

float result = a-b;


mul-float

float a,b;

float result = a*b;


div-float

float a,b;

float result = a/b;


ren-float

float a,b;

float result = a%b;


double...double...