原文链接: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 & Format | Mnenonic/Syntax(助记符/语法) | Arguments(参数) | Description(说明) |
00 10x | nop | 空循环,没有操作,表示一条指令执行的时间 | |
01 12x | move vA,vB | A:目标寄存器(4bit) B:源寄存器(4bit) | 将一个非对象寄存器的内容转移到另一个寄存器中 |
02 22x | move/from16 vAA,vBBBB | A:目标寄存器(8bit) B:源寄存器(16bit) | 同上 |
03 32x | move/16 vAAAA,vBBBB | A:目标寄存器(16bit) B:源寄存器(16bit) | 同上 |
04 12x | move-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 vAA | A:目标寄存器(8bit) | 将最近invoke-kind指令的单字(single-word)、非对象(non-object)的结果转移到指定的寄存器中。在invoke-kind指令(结果不可忽略)之后立即执行该指令;在其他地方执行是无效的 |
0b 11x | move-result-wide vAA | A:目标寄存器(8bit) | 将最近invoke-kind指令的双字(double-word)结果转移到指定的寄存器对。在invoke-kind指令(结果不可忽视)之后;在其他地方执行时无效的 |
0c 11x | move-result-object vAA | A:目标寄存器(8bit) | 将最近invoke-kind指令的object结果转移到指定的寄存器中。该指令必须在invoke-kind或者filled-new-array之后立即执行,它们的(object)结果不可忽略并且在其他地方执行时无效 |
0d 11x | move-exception vAA | A:目标寄存器(8bit) | 将一个仅被catch到的异常保存到指定的寄存器中。该指令必须是任何异常处理的第一条指令,该异常处理捕获到异常是不能忽略的,并且该指令必须只能作为异常处理器的第一条指令;其他地方执行时无效 |
0e 10x | return void | 从一个void方法返回 | |
0f 11x | return vAA | A:返回值寄存器(8bit) | 从一个32位、非对象、有返回值得方法中返回 |
10 11x | return-wide vAA | A:返回值寄存器对(8bit) | 从一个64位、有返回值的方法返回 |
11 11x | return-object vAA | A:返回值寄存器(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/high26 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/high26 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 vAA | A:引用寄存器(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 |
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 vAA | A:异常寄存器(8bit) | 抛出指定异常 |
28 10t | goto +AA | A:带符号的分支偏移量(8bit) | 无条件的跳转到指定的指令; 注意:分支的偏移量不可为0, |
29 20t | goto/16 +AAAA | A:带符号的分支偏移量(16位) | 同上 |
2a 30t | goto/32 +AAAAAAAA | A:带符号的分支偏移量(32位) | 同上 |
2b 31t | packed-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... |
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。