接口定義:
給接口做一個實作:
我們先做一個建立對象,并調用這個對象從接口。
java位元組碼:
0:首先new一個對象。
3:對象的引用值在棧裡,dup一個,給下面調用類時用。
4:調用sampleclass的構造函數。雖然是生動生成的,但是new完了之後是一定要調的。
9:invokevirtual是調用虛方法。
dalvik代碼:
dalvik代碼還是要更好讀一些:
new-instance結果存到v0中。
然後間接尋址,調用v0中的類的構造方法
接着v0繼續用,調interfacemethod方法。
最後傳回。
下面我們分析一下與面向對象相關的oat指令:
new-instance, invoke-direct和invoke-virtual都是兩個參數的指令,用到兩個虛拟寄存器:
v0:位址為sp+16
v1: 位址為sp+40
前面還是儲存參數,檢查suspend狀态。
下面是調用pallocobject過程去建立新對象。w0中是類的類型。
傳回值就是新對象的引用,将其存到sp+16(v0)。再重新讀回來。
将對象引用存到sp+12,再讀入到w1。
然後計算sampleclass類的構造方法的位址,然後跳轉到該函數。
傳回值存到sp+16(v0)。
w0再存到sp+12,再讀到w1。
然後計算interfacemethod方法的位址,最後直接跳轉到那個方法。
将進入時備份的lr恢複,并傳回。
最後還是檢查suspend的調用。
接口類:
接口的實作類:
正如以前我們看到的一樣,這次再強調一下。如果沒有預設的構造函數,java會自動給生成一個,然後會調用object類的構造函數。
sampleinterface就沒生成什麼代碼。
oat代碼:
前面還是例行備份和檢查。
下面先load v0(sp+40)的值,然後清0初始化。
最後計算object的構造方法位址,并跳轉過去。
然後傳回。
這個空的interfacemethod函數可真是夠空的,除了查一下suspend狀态,可以打個斷點啥的。其餘真的啥也沒幹。
lr存到sp+24
w1存到sp+40
然後從sp+24中把lr讀回來
傳回
在看oat生成的代碼的時候,我們經常看到lr和tr這樣的别名。下面我們先掃清一下寄存器的障礙,學習一下art使用寄存器的土話。
複習一下,arm 64下的64位通用寄存器有33個:
x0~x30共31個
sp棧寄存器
xzr零寄存器,從它讀就讀出個0,向它寫等于空操作
這33個64位寄存器也可以用低32位,當作32位寄存器來使用:
w0~w30
wsp
wzr
另外,還有32個64位的neon浮點運算寄存器:
64位雙精度是d0~d31
32位單精度是s0~s31
這其中,有下列寄存器被art定義了自己含義:
fp是frame pointer,lr是link register。
在art代碼中,這些寄存器還有别名:
在art代碼中,用得最多的就是xself,舉個例子,判斷線程是不是被挂起來了,生成的代碼是這樣的:
上面的代碼是不是看起來有點似曾相識啊?
下面是儲存剛才我們介紹的特殊寄存器的代碼:
下面是恢複用的:
這一章的最後,我們說說jni.
針對jni,沒有什麼java代碼值得生成的。
我們看下反彙編的java代碼,對應test1并沒有java位元組碼生成。
但是oat還是會生成對應的查找jni函數的指令:
sp用掉了192的空間,先減掉。
然後備份x19~x30.d8~d15和tr(x18)的值。
下面将産生三次過程調用,分别是對應artquickgenericjnitrampoline,native函數本身和artquickgenericjniendtrampoline。
再調用了artquickgenericjniendtrampoline之後,再去檢查是否有異常産生。
下面兩句是處理異常的,#136是異常狀态的位址,如果出現異常,就跳轉到ret後面的異常處理部分去執行。
為什麼異常狀态的值是136呢,因為它在art代碼中的定義是:thread_exception_offset。
在64位系統裡,指針的size是8,是以這個offset的值為128+8,為136.
thread_top_quick_frame_offset為128+3*8 = 152。
thread_self_offset為128+72=200.
thread_card_table_offset = 128 + 147 * 8 = 1304.
把一開始減掉的192加回來,然後傳回。
後面是異常處理的部分
這段代碼用了大量的寄存器啊,那他們是幹嘛用的呢?
不用擔心,雖然沒有文檔,但是art代碼中對此有說明:
如果有參數傳進來,如果是整型的,最多7個參數,位于x1~x7中。如果是浮點數,就在d0~d7中。
傳回位址在x30/lr中。
對照着下面的代碼去看上面生成的test1調用jni的代碼,是不是覺得有點能看懂了的意思了?
這些代碼位于/art/runtime/arch/arm64/quick_entrypoints_arm64.s中。
通過看這裡面的彙編過程的代碼,再去對照編譯出來的oat代碼,為我們打開一條繼續深入的路徑。