您的位置:首页 > 运维架构

Dalvik字节码——初识

2016-08-16 16:40 351 查看
原文链接: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,vBA:目标寄存器(4bit)
B:源寄存器(4bit)
将一个非对象寄存器的内容转移到另一个寄存器中
02 22xmove/from16 vAA,vBBBBA:目标寄存器(8bit)
B:源寄存器(16bit)
同上
03 32xmove/16 vAAAA,vBBBBA:目标寄存器(16bit)
B:源寄存器(16bit)
同上
04 12xmove-wide vA,vBA:目标寄存器(4bit)
B:源寄存器对(4bit)
将一个寄存器对中的内容移动到另一个寄存器对中。
注意:从vN移动到vN-1或者vN+1是合法的,所以在向寄存器对中写数据之前,寄存器的两部分必须是可读的。
05 22x
move-wide/from16 vAA,vBBBBA:目标寄存器对(8bit)
B:源寄存器对(16bit)
同上
06 32x
move-wide/16 vAAAA,vBBBBA:目标寄存器对(16bit)
B:源寄存器对(16bit)
同上
07 12x
move-object vA,vBA:目标寄存器(4bit)
B:源寄存器(4bit)
将对象寄存器中的内容转移到另外一个寄存器中
08 22x
move-object/from16 vAA,vBBBBA:目标寄存器(8bit)
B:源寄存器(16bit)
同上
09 32x
move-object/16 vAAAA,vBBBBA:目标寄存器(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,#+BA:目标寄存器(4bit)
B:有符号整数(4bit)
将有符号字符数据(32bit)转移到指定的寄存器中
13 21s
const/4 vAA,#+BBBBA:目标寄存器(8bit)
B:有符号整数(16bit)
将给定的有符号总非数据(32bit)转移到指定的寄存器中
14 31i
const vAA,#+BBBBBBBBA:目标寄存器(8bit)
B:任意32位常量
将执行的字符数值转移到指定的寄存器中
15 21h
const/high16 vAA,#+BBBB0000A:目标寄存器(8bit)
B:有符号的整数(16bit)
将给定的字符值(right-zero-extended to 32)转移到指定的寄存器中
16 21s
const-wide/16 vAA,#+BBBBA:目标寄存器(8bit)
B:有符号整数(16bit)
将给定的字符值转移到指定的寄存器对中
17 31i
const-wide/32 vAA,#+BBBBBBBBA:目标寄存器(8bit)
B:有符号整数(32位)
将给定的字符值(有符号扩展到64位)转移到指定的寄存器对中
18 51l
const-wide vAA,#+BBBBBBBBBBBBBBBBA:目标寄存器(8bit)
B:任意64位常量
将给定的字符值转移到指定的寄存器对中
19 21h
const-wide/high16 vAA,#+BBBB000000000000A:目标寄存器(8bit)
B:有符号整数(16bit)
将给定的字符值(right-zero-extended to 64bit)转移到特定的寄存器对中
1a 21c
const-string vAA,string@BBBBA:目标寄存器(8bit)
B:string index
将通过给定的ndex指定的string引用转移到指定的寄存器 中
1b 31c
const-string/jumbo vAA,string@BBBBBBBBA:目标寄存器(8bit)
B:字符串索引
同上
1c 21c
const-class vAA,type@BBBBA:目标寄存器(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@BBBBA:存储引用的寄存器(8bit)
B:类型索引(16bit)
如果给定寄存器中的引用不能转化为指定的类型则抛出一个classCastException;
注意:因为A必须是一个引用(不是一个原始值),如果B是一个原始类型在runtime阶段会抛出一个异常。
20 22c
instance-of vA,vB, type@CCCCA:目标寄存器(4bit)
B:引用寄存器(4bit)
C:类型索引(16bit)
如果指定引用是一个给定类型的实例则目标寄存器中存放1,否则存放0;
注意:因为B必须是一个引用(不是原始类型数值),如果是一个原始类型则寄存器的结果总是0;
21 12x
array-length vA,vBA:目标寄存器(4bit)
B:数组引用寄存器(4bit)
将指定数组的长度存储到给定的目标寄存器中
22 21c
new-instance vAA,type@BBBBA:目标寄存器(8bit)
B:类型索引
构造一个指定类型的新实例,并将它的引用存放到指定目标寄存器中。该类型必须是非数组类
23 22c
new-array vA,vB,type@CCCCA:目标寄存器(8bit)
B:尺寸(size)寄存器
C:类型索引
构造一个执行类型和大小的新的数组,该类型必须是一个数组类型
24 35c
filled-new-array{vC,vD,vE,vF,vG},type@BBBBA:数组大小和参数字数量(4bit)
B:类型索引(16bit)
C...G:参数寄存器(4bit)
根据给定的类型和大小构造一个数组,用提供的内容赋值。类型必须是一个数组类型。数组的内容必须是单字(single-word)(也就是说没有long或者double数组,但是引用类型是可以接受的)。构造实例被作为结果存储起来,就像方法执行指令保存它们的结果一样,所以构造出来的实例必须立即在随后使用move-result-object指令转移到一个寄存器中。
25 3rc
filled-new-array/range{vCCCC..vNNNN},type@BBBBA:数组的胆小和参数字的数量(8bit)
B:类型索引(16bit)
C:第一个参数寄存器(16bit)
N = A + C - 1
构造一个指定类型和大小的数组,使用指定的内容赋值。阐述和约束与filled-new-array一样,同上
26 31t
fill-array-data vAA,+BBBBBBBBA:数组的引用(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,+BBBBBBBBA:测试寄存器
B:数据表伪指令有符号分支偏移量(32bit)
在给定寄存器的基础上跳转到一个新的指令,使用一个偏移量表对应特定整数范围中的每个值,或者如果没有匹配的数值下一条指令会落空。
2c 31t
sparse-switch vAA,+BBBBBBBBA:测试寄存器
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 22ciinstaceop 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 21csstaticop 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 12xunop 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,vBb0:add-int/2addrb1:sub-int/2addrb2:mul-int/2addrb3:div-int/2addrb4:rem-int/2addrb5:and-int/2addrb6:or-int/2addrb7:xor-int/2addrb8:shl-int/2addrb9:shr-int/2addrba:ushr-int/2addrbb:add-long/2addrbc:sub-long/2addrbd:mul-long/2addrbe:div-long/2addrbf:erm-long/2addrc0:add-long/2addrc1:or-long/2addrc2:xor-long/2addrc3:shl-long/2addrc4:shr-long/2addrc5:ushr-long/2addrc6:add-float/2addrc7:sub-float/2addrc8:mul-float/2addrc9:div-float/2addrca:rem-float/2addrcb:add-double/2addrcc:sub-double/2addrcd:mul-double/2addrce:div-double/2addrcf:rem-double/2addrA:目标和第一个源寄存器或寄存器对(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 22bbinop/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-intint32 a;
int32 result=-a;
补码
not-intint32 a;
int32 result=~a;
反码
neg-longint64 a;
int64 result=-a;
补码
not-longint64 a;
int64 result=~a;
反码
neg-floatfloat a;
float result=-a;
negation
neg-doubledouble 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-intint64 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-intdouble 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-shortint32 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-intint32 a,b;
int32 result=b-a;
mul-int
int32 a,b;
int32 result=a*b;
div-intint32 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-intint32 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-floatfloat a,b;float result = a-b;
mul-floatfloat a,b;float result = a*b;
div-floatfloat a,b;float result = a/b;
ren-float
float a,b;float result = a%b;
double...double...
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dalvik opcode set