1. 程式人生 > >【轉】進入Android Dalvik虛擬機器之Dalvik指令集

【轉】進入Android Dalvik虛擬機器之Dalvik指令集

Dalvik指定在呼叫格式上模仿了C語言的呼叫約定。Dalvik指令的語法與助詞符有如下特點:

  • 引數採用從目標(destination)到源(source)的方式。

  • 根據位元組碼的大小與型別不同,一些位元組碼添加了名稱字尾以消除岐義。

    • 32位常規型別的位元組碼末新增任何字尾。

    • 64位常規型別的位元組碼新增 -wide字尾。

    • 特殊型別的位元組碼根據具體型別新增字尾。它們可以是 -boolean,-byte,-char,-short,-int,-long,-float,-double,-object,-string,-class,-void之一。

  • 根據位元組碼的佈局與選項不同,一些位元組碼添加了位元組碼字尾以消除岐義。這些字尾通過在位元組碼主名稱後新增斜槓“/”來分隔開。

  • 在指令集的描述中,寬度值中每個字母表示寬度為4位。

例如這條指令:“move-wide/from16 vAA, vBBBB”:

move為基礎位元組碼(base opcode),標識這是基本操作。wide為名稱字尾(name suffix),標識指令操作的資料寬度(64位)。from16為位元組碼字尾(opcode suffix),標識源為一個16位的暫存器引用變數。vAA為目的暫存器,它始終在源的前面,取值範圍為v0~v255。vBBBB為源暫存器,取值範圍為v0~v65535。

Dalvik指令集中大多數指令用到了暫存器作為目的運算元或源運算元,其中 A/B/C/D/E/F/G/H 代表一個4位的數值,可用來表示0~15的數值或v0~v15的暫存器,而 AA/BB/CC/DD/EE/FF/GG/HH 代表一個8位的數值,可用來表示0~255的數值或v0~v255的暫存器,AAAA/BBBB/CCCC/DDDD/EEEE/FFFF/GGGG/HHHH 代表一個16位的數值,可用來表示0~65535的數值或v0~v65535的暫存器。注意:Android官方指令文件描述暫存器時,對不同取值範圍的暫存器以括號說明其大小,如A:destination register(4 bits),A:destination register(16 bits)。請注意,Dalvik虛擬機器中的每個暫存器都是32位的。描述指令時所說的位數表示的是暫存器數值的取值範圍。

2. 空操作指令

空操作指令的助記符為nop。它的值為00,通常nop指令被用來作對齊程式碼之用,無實際操作。

3. 資料操作指令

資料操作指令為move。move指令的原型為“move destination,source”,move指令根據位元組碼的大小與型別不同,後面會跟上不同的字尾。

  • “move vA, vB”:將vB暫存器的值賦給vA暫存器,源暫存器與目的暫存器都為4位。

  • “move/from16 vAA, vBBBB”:將vBBBB暫存器的值賦給vAA暫存器,源暫存器為16位,目的暫存器為8位。

  • “move/16 vAAAA, vBBBB”:將vBBBB暫存器的值賦給vAAAA暫存器,源暫存器與目的暫存器都為16位。

  • “move-wide vA, vB”:為4位的暫存器對賦值。源暫存器與目的暫存器都為4位。

  • “move-wide/from16 vAA, vBBBB”與“move-wide/16 vAAAA, vBBBB”實現與“move-wide”相同。

  • “move-object vA, vB”:為物件賦值。源暫存器與目的暫存器都為4位。

  • “move-object/from16 vAA, vBBBB”:為物件賦值。源暫存器為16位,目的暫存器為8位。

  • “move-object/16 vAA, vBBBB”:為物件賦值。源暫存器與目的暫存器都為16位。

  • “move-result vAA”:將上一個invoke型別指令操作的單字非物件結果賦給vAA暫存器。

  • “move-result-wide vAA”:將上一個invoke型別指令操作的雙字非物件結果賦給vAA暫存器。

  • “move-result-object vAA”:將上一個invoke型別指令操作的物件結果賦給vAA暫存器。

  • “move-exception vAA”:儲存一個執行時發生的異常到vAA暫存器,這條指令必須是異常發生時的異常處理器的一條指令。否則的話,指令無效。

4. 返回指令

返回指令指的是函式結尾時執行的最後一條指令。它的基礎位元組碼為teturn,共有以下四條返回指令:

  • “return-void”:表示函式從一個void方法返回。

  • “return vAA”:表示函式返回一個32位非物件型別的值,返回值暫存器為8位的暫存器vAA。

  • “return-wide vAA”:表示函式返回一個64位非物件型別的值,返回值為8位的暫存器對vAA。

  • “return-object vAA”:表示函式返回一個物件型別的值。返回值為8位的暫存器vAA。

5. 資料定義指令

資料定義指令用來定義程式中用到的常量,字串,類等資料。它的基礎位元組碼為const。

  • “const/4 vA, #+B”:將數值符號擴充套件為32位後賦給暫存器vA。

  • “const/16 vAA, #+BBBB”:將資料符號擴充套件為32位後賦給暫存器vAA。

  • “const vAA, #+BBBBBBBB”:將數值賦給暫存器vAA。

  • “const/high16 vAA, #+BBBB0000“:將數值右邊零擴充套件為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, #+BBBB000000000000”:將數值右邊零擴充套件為64位後賦給暫存器對vAA。

  • “const-string vAA, [email protected]”:通過字串索引構造一個字串並賦給暫存器vAA。

  • “const-string/jumbo vAA, [email protected]”:通過字串索引(較大)構造一個字串並賦給暫存器vAA。

  • “const-class vAA, [email protected]”:通過型別索引獲取一個類引用並賦給暫存器vAA。

  • “const-class/jumbo vAAAA, [email protected]”:通過給定的型別索引獲取一個類引用並賦給暫存器vAAAA。這條指令佔用兩個位元組,值為0xooff(Android4.0中新增的指令)。

6. 鎖指令

鎖指令多用在多執行緒程式中對同一物件的操作。Dalvik指令集中有兩條鎖指令:

  • “monitor-enter vAA”:為指定的物件獲取鎖。

  • “monitor-exit vAA”:釋放指定的物件的鎖。

7. 例項操作指令

與例項相關的操作包括例項的型別轉換,檢查及新建等:

  • “check-cast vAA, [email protected]”:將vAA暫存器中的物件引用轉換成指定的型別,如果失敗會丟擲ClassCastException異常。如果型別B指定的是基本型別,對於非基本型別的A來說,執行時始終會失敗。

  • “instance-of vA, vB, [email protected]”:判斷vB暫存器中的物件引用是否可以轉換成指定的型別,如果可以vA暫存器賦值為1,否則vA暫存器賦值為0。

  • “new-instance vAA, [email protected]”:構造一個指定型別物件的新例項,並將物件引用賦值給vAA暫存器,型別符type指定的型別不能是陣列類。

  • “check-cast/jumbo vAAAA, [email protected]”:指令功能與“check-cast vAA, [email protected]”相同,只是暫存器值與指令的索引取值範圍更大(Android4.0中新增的指令)。

  • “instance-of/jumbo vAAAA, vBBBB, [email protected]”:指令功能與“instance-of vA, vB, [email protected]”相同,只是暫存器值與指令的索引取值範圍更大(Android4.0中新增的指令)。

  • “new-instance/jumbo vAAAA, [email protected]”:指令功能與“new-instance vAA, [email protected]”相同,只是暫存器值與指令的索引取值範圍更大(Android4.0中新增的指令)。

8. 陣列操作指令

陣列操作包括獲取陣列長度,新建陣列,陣列賦值,陣列元素取值與賦值等操作。

  • “array-length vA, vB”:獲取給定vB暫存器中陣列的長度並將值賦給vA暫存器,陣列長度指的是陣列的條目個數。

  • “new-array vA, vB, [email protected]”:構造指定型別([email protected])與大小(vB)的陣列,並將值賦給vA暫存器。

  • “filled-new-array {vC, vD, vE, vF, vG},[email protected]”構造指定型別([email protected])與大小(vA)的陣列並填充陣列內容。vA暫存器是隱含使用的,除了指定陣列的大小外還指定了引數的個數,vC~vG是使用到的引數寄存序列。

  • “filled-new-array/range {vCCCC  ..vNNNN}, [email protected]”指令功能與“filled-new-array {vC, vD, vE, vF, vG},[email protected]”相同,只是引數暫存器使用range位元組碼字尾指定了取值範圍 ,vC是第一個引數暫存器,N = A +C -1。

  • “fill-array-data vAA, +BBBBBBBB”用指定的資料來填充陣列,vAA暫存器為陣列引用,引用必須為基礎型別的陣列,在指令後面會緊跟一個數據表。

  • “new-array/jumbo vAAAA, vBBBB,[email protected]”指令功能與“new-array vA,vB,[email protected]”相同,只是暫存器值與指令的索引取值範圍更大(Android4.0中新增的指令)。

  • “filled-new-array/jumbo {vCCCC  ..vNNNN},[email protected]”指令功能與“filled-new-array/range {vCCCC  ..vNNNN},[email protected]”相同,只是索引取值範圍更大(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。

9. 異常指令

Dalvik指令集中有一條指令用來丟擲異常。

  • “throw vAA”丟擲vAA暫存器中指定型別的異常。

10. 跳轉指令

跳轉指令用於從當前地址跳轉到指定的偏移處。Dalvik指令集中有三種跳轉指令:無條件跳轉(goto),分支跳轉(switch)與條件跳轉(if)。

  • “goto +AA”:無條件跳轉到指定偏移處,偏移量AA不能為0。

  • “goto/16 +AAAA”:無條件跳轉到指定偏移處,偏量AAAA不能為0。

  • “goto/32 +AAAAAAAA”:無條件跳轉到指定偏移處。

  • “packed-switch vAA, +BBBBBBBB”:分支跳轉指令。vAA暫存器為switch分支中需要判斷的值,BBBBBBBB指向一個packed-switch-payload格式的偏移表,表中的值是有規律遞增的。

  • “sparse-switch vAA, +BBBBBBBB”:分支跳轉指令。vAA暫存器為switch分支中需要判斷的值,BBBBBBBB指向一個sparse-switch-payload格式的偏移表,表中的值是無規律的偏移量。

  • “if-test vA, vB, +CCCC”:條件跳轉指令。比較vA暫存器與vB暫存器的值,如果比較結果滿足就跳轉到CCCC指定的偏移處。偏移量CCCC不能為0。if-test型別的指令有以下幾條:

    • “if-eq”:如果vA等於vB則跳轉。Java語法表示為“if(vA == vB)”

    • “if-ne”:如果vA不等於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”:條件跳轉指令。拿vAA暫存器與0比較,如果比較結果滿足或值為0時就跳轉到BBBB指定的偏移處。偏移量BBBB不能為0。if-testz型別的指令有以下幾條:

    • “if-eqz”:如果vAA為0則跳轉。Java語法表示為“if(vAA == 0)”

    • “if-nez”:如果vAA不為0則跳轉。Java語法表示為“if(vAA != 0)”

    • “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)”

11. 比較指令

比較指令用於對兩個暫存器的值(浮點型或長整型)進行比較。它的格式為“cmpkind vAA, vBB, vCC”,其中vBB暫存器與vCC暫存器是需要比較的兩個暫存器或暫存器對,比較的結果放到vAA暫存器。Dalvik指令集中共有5條比較指令:

  • “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

12. 欄位操作指令

欄位操作指令用來對物件例項的欄位進入讀寫操作。欄位的型別可以是Java中有效的資料型別。對普通欄位與靜態欄位操作有兩種指令集,分別是“iinstanceop vA, vB, [email protected]” 與 “sstaticop vAA, [email protected]”。

普通欄位指令的指令字首為i,如對普通欄位讀操作使用 iget 指令,寫操作使用 iput 指令;靜態欄位的指令字首為s,如對靜態欄位讀操作使用 sget 指令,寫操作使用 sput 指令。

根據訪問的欄位型別不同,欄位操作指令後面會緊跟欄位型別的字尾,如 iget-byte指令表示讀取例項欄位 的值型別為位元組型別,iput-short指令表示設定例項欄位的值型別為短整型。兩類指令操作結果都是一樣,只是指令字首與操作的欄位型別不同。

普通欄位操作指令有: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。

靜態欄位操作指令有: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。

在Android4.0系統中,Dalvik指令集中增加了“iinstanceop/jumbo vAAAA, vBBBB, [email protected]”與”sstaticop/jumbo vAAAA, [email protected]”兩類指令,它們與上面介紹的兩類指令作用相同,只是在指令中增加了jumbo位元組碼字尾,且暫存器值與指令的索引取值範圍更大。

13. 方法呼叫指令

方法呼叫指令負責呼叫類例項的方法。它的基礎指令為 invoke,方法呼叫指令有“invoke-kind {vC, vD, vE, vF, vG},[email protected]”與“invoke-kind/range {vCCCC  .. vNNNN},[email protected]”兩類,兩類指令在作用上並無不同,只是後者在設定引數暫存器時使用了range來指定暫存器的範圍。根據方法型別的不同,共有如下五條方法呼叫指令:

  • “invoke-virtual” 或 “invoke-virtual/range”呼叫例項的虛方法。

  • “invoke-super”或”invoke-super/range”呼叫例項的父類方法。

  • “invoke-direct”或“invoke-direct/range”呼叫例項的直接方法。

  • “invoke-static”或“invoke-static/range”呼叫例項的靜態方法。

  • “invoke-interface”或“invoke-interface/range”呼叫例項的介面方法。

在Android4.0系統中,Dalvik指令集中增加了“invoke-kind/jumbo {vCCCC  .. vNNNN},[email protected]”這類指令,它與上面介紹的兩類指令作用相同,只是在指令中增加了jumbo位元組碼字尾,且暫存器值與指令的索引取值範圍更大。

方法呼叫指令的返回值必須使用move-result*指令來獲取。如下面兩條指令:

invoke-static {}, Landroid/os/Parcel;->obtain() Landroid/os/Parcel;
move-result-object v0

14. 資料轉換指令

資料轉換指令用於將一種型別的數值轉換成另一種型別。它的格式為“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-dobule”:將整型數轉換為雙精度浮點數。

  • “long-to-int”:將長整型數轉換為整型。

  • “long-to-float”:將長整型數轉換為單精度浮點型。

  • “long-to-double”:將長整型數轉換為雙精度浮點型。

  • “float-to-int”:將單精度浮點數轉換為整型。

  • “float-to-long”:將單精度浮點數轉換為長整型數。

  • “float-to-double”:將單精度浮點數轉換為雙精度浮點型數。

  • “double-to-int”:將雙精度浮點數轉換為整型。

  • “double-to-long”:將雙精度浮點數轉換為長整型。

  • “double-to-float”:將雙精度浮點數轉換為單精度浮點型。

  • “int-to-byte”:將整型轉換為位元組型。

  • “int-to-char”:將整型轉換為字元型。

  • “int-to-short”:將整型轉換為短整型。

15. 資料執行指令

資料運算指令包括算術運算指令與邏輯運算指令。算術運算指令主要進行數值間如加,減,乘,除,模,移位等運算。邏輯運算指令主要進行數值間與,或,非,抑或等運算。資料運算指令有如下四類(資料運算時可能是在暫存器或暫存器對間進行,下面的指令作用講解時使用暫存器來描述):

  • “binop vAA, vBB, vCC”:將vBB暫存器與vCC暫存器進行運算,結果儲存到vAA暫存器。

  • “binop/2addr vA, vB”:將vA暫存器與vB暫存器進行運算,結果儲存到vA暫存器。

  • “binop/lit16 vA, vB, #+CCCC”:將vB暫存器與常量 CCCC進行運算,結果儲存到vA暫存器。

  • “binop/lit8 vAA, vBB, #+CC”:將vBB暫存器與常量CC進行運算,結果儲存到vAA暫存器。

後面3類指令比第1類指令分別多出了2addr,lit16,lit8等指令字尾。四類指令中基礎位元組碼相同的指令執行的運算操作是類似的,第1類指令中,根據資料的型別不同會在基礎位元組碼後面加上資料型別字尾,如 -int 或 -long 分別表示操作的資料型別為整型與長整型。第1類指令可歸類如下:

  • “add-type”:vBB暫存器與vCC暫存器值進行加法運算(vBB + vCC)

  • “sub-type”:vBB暫存器與vCC暫存器值進行減法運算(vBB - vCC)

  • “mul-type”:vBB暫存器與vCC暫存器值進行乘法運算(vBB * vCC)

  • “div-type”:vBB暫存器與vCC暫存器值進行除法運算(vBB / vCC)

  • “rem-type”:vBB暫存器與vCC暫存器值進行模運算(vBB % vCC)

  • “and-type”:vBB暫存器與vCC暫存器值進行與運算(vBB & vCC)

  • “or-type”:vBB暫存器與vCC暫存器值進行或運算(vBB | vCC)

  • “xor-type”:vBB暫存器與vCC暫存器值進行異或運算(vBB ^ vCC)

  • “shl-type”:vBB暫存器值(有符號數)左移vCC位(vBB << vCC )

  • “shr-type”:vBB暫存器值(有符號)右移vCC位(vBB >> vCC)

  • “ushr-type”:vBB暫存器值(無符號數)右移vCC位(vBB >>> vCC)

其中基礎位元組碼後面的-type可以是-int,-long, -float,-double。後面3類指令與之類似。

至此,Dalvik虛擬機器支援的所有指令就介紹完了。在android4.0系統以前,每個指令的位元組碼只佔用一個位元組,範圍是0x0~0x0ff。在android4.0系統中,又擴充了一部分指令,這些指令被稱為擴充套件指令,主要是在指令助記符後添加了jumbo字尾,增加了暫存器與常量的取值範圍。