Dalvik字節碼——初識

原文鏈接: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...



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章