為了更好的了解反編譯代碼,有必要了解一下其中的細節(jié)部分,比如v0、v1、p1都是什么等等。
代碼還是之前的Hello.java。體會Java虛擬機和Dalvik虛擬機的架構(gòu)不同之處
public class Hello{
public int foo(int a, int b){
return (a + b) * (a - b);
}
public static void main(String[] argc){
Hello hello = new Hello();
System.out.println(hello.foo(5, 3));
}
}
編譯過程不寫了,直接看dexdump的反編譯代碼:這里我們還是只分析foo()函數(shù)。
Virtual methods -
#0 : (in LHello;)
name : 'foo'
type : '(II)I'
access : 0x0001 (PUBLIC)
code -
registers : 5
ins : 3
outs : 0
insns size : 6 16-bit code units
000198: |[000198] Hello.foo:(II)I
0001a8: 9000 0304 |0000: add-int v0, v3, v4
0001ac: 9101 0304 |0002: sub-int v1, v3, v4
0001b0: b210 |0004: mul-int/2addr v0, v1
0001b2: 0f00 |0005: return v0
v命名方法
從代碼中我們可以看到foo一共使用了5個寄存器,上個章節(jié)中我們也說了,add-int v0, v3, v4中v3和v4分別代表第一個參數(shù)和第二個參數(shù)。那么為什么是這樣呢?
規(guī)則:
根據(jù)Dalvik虛擬機參數(shù)傳遞的規(guī)則,如果一個函數(shù)使用了M個寄存器、擁有N個參數(shù):則參數(shù)使用最后N個寄存器,局部變量從v0開始一直遞增到前M-N個。
那我們來分析下,foo()函數(shù),一共是5個registers寄存器,有兩個int的參數(shù)。由于foo函數(shù)是Hello類的非靜態(tài)方法,因此函數(shù)被調(diào)用時會傳入一個“隱藏”的Hello對象引用(this),所以實際傳參是3個。也就是局部變量使用v0和v1寄存器,參數(shù)使用v2、v3、v4這三個后面的寄存器。其中v2表示被傳入的“隱藏”的對象引用(this)、v3表示第一個參數(shù)、v4表示第二個參數(shù)。
p命名方法
p命名方法對函數(shù)的局部變量寄存器命名沒有影響。他的命名規(guī)則是:函數(shù)引用的參數(shù)從p0開始命名,依次遞增。
使用ddx.jar反匯編Hello.dex文件,這樣會在目錄下生成一個ddxout文件夾,用文件編輯器打開里面的Hello.ddx文件。
$ java -jar /Users/johnhao/Downloads/tools/smali/ddx.jar -d ddxout /Users/johnhao/Downloads/Train/Hello.dex
Processing Hello
來看一下foo()函數(shù),依然是使用5個寄存器:
.method public foo(II)I
.limit registers 5
; this: v2 (LHello;)
; parameter[0] : v3 (I)
; parameter[1] : v4 (I)
.line 3
add-int v0,v3,v4
sub-int v1,v3,v4
mul-int/2addr v0,v1
return v0
.end method
v0和v1表示局部變量的寄存器,p0、p1、p2表示函數(shù)引用參數(shù)的寄存器,其中p0表示被傳入的Hello對象的引用(this)、p1表示第一個int參數(shù)、p2表示第二個int參數(shù)。
比較
v命名相比較p命名,大體結(jié)構(gòu)相同,有一些細微的地方。
1、v命名以.registers開頭,p命名以.limit 開頭
2、v命名以v2作為this的引用,p命名以p0作為this的引用
3、v命名以M-N命名N個參數(shù)的寄存器,p命名則用p命名法。