編輯:關於Android編程
0x00
這一節我們要討論對象的繼承和虛函數的匯編實現。
0x01
我們先直接看匯編代碼:
#include "com_example_ndkreverse6_Lesson6.h" #include #define LOG_TAG "lesson6" #define ALOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) class Base { public: virtual void display() { //虛函數,virtual聲明的函數,向上轉型後的對象才能調用到子類同名的方法 ALOGD("Base:%d, BaseChar:%d", base_, baseChar_); } Base(int base) { base_ = base; baseChar_ = 8; ALOGD("Base ..."); } virtual ~Base() { //虛析構函數,只有聲明成virtual,向上轉型的對象先調用子類的析構函數,再調用父類的析構函數 ALOGD("~Base ..."); } private: int base_; char baseChar_; }; class Derived: public Base { public: virtual void display() { //覆蓋父類的方法 ALOGD("Derived:%d, DerivedChar:%d", derived_, derivedChar_); Base::display(); //使用父類的方法,由於是覆蓋,所以同名,要用這種方式來引用 } Derived(int derived) : Base(derived) { derived_ = derived; derivedChar_ = 10; ALOGD("Derived ..."); } ~Derived() { ALOGD("~Derived ..."); } private: int derived_; char derivedChar_; }; JNIEXPORT void JNICALL Java_com_example_ndkreverse6_Lesson6_main (JNIEnv * env, jobject jobject) { Base* d = new Derived(18); d->display(); delete d; }那麼,運行後執行的結果如下:
D/lesson6 (28959): Base ... D/lesson6 (28959): Derived ... D/lesson6 (28959): Derived:18, DerivedChar:10 D/lesson6 (28959): Base:18, BaseChar:8 D/lesson6 (28959): ~Derived ... D/lesson6 (28959): ~Base ...
0x02
下面我們使用ida來打開so,對匯編代碼做出解釋。該匯編代碼使用的是調試狀態下的匯編代碼。
.text:00003088 EXPORT Java_com_example_ndkreverse6_Lesson6_main .text:00003088 Java_com_example_ndkreverse6_Lesson6_main .text:00003088 PUSH {R4-R6,LR} .text:0000308A MOVS R0, #0x14 ; unsigned int R0被分配為20,表示要分配20個字節的內存單元 .text:0000308C BL _Znwj ; operator new(uint) 分配20個字節大小的內存單元 .text:00003090 LDR R3, =(_ZTV4Base_ptr - 0x3098) ;直接調用Base的構造函數 .text:00003092 MOVS R6, #0x12 ;R6被初始化為18 .text:00003094 ADD R3, PC ; _ZTV4Base_ptr .text:00003096 LDR R3, [R3] ; `vtable for'Base 取出Base的虛表指針,指向.data.rel.ro,即0x70428960 .text:00003098 MOVS R4, R0 ;R0為剛分配的20個字節大小的內存單元的首地址,現在賦值給R4 .text:0000309A ADDS R3, #8 ;R3+8再賦值給R3,R3指向了0x70428968 .text:0000309C STR R3, [R0] ;將0x70428968賦值給剛分配的內存單元的首地址(所指向的內存單元) .text:0000309E MOVS R3, #8 ;R3被賦值為8 .text:000030A0 LDR R5, =(aLesson6 - 0x30AA) .text:000030A2 LDR R2, =(aBase____0 - 0x30B0) .text:000030A4 STR R6, [R0,#4] ;把18賦值給剛分配的內存單元的首地址+4 .text:000030A6 ADD R5, PC ; "lesson6" R5指向了位於.rodata段中lesson6 .text:000030A8 MOVS R1, R5 ;把R5賦值給R1做為參數 .text:000030AA STRB R3, [R0,#8] ;把8賦值給剛分配的內存單元的首地址+8 .text:000030AC ADD R2, PC ; "Base ..." R2指向了位於.rodata段中Base ... .text:000030AE MOVS R0, #3 ;R0被初始化為3 .text:000030B0 BL j_j___android_log_print ;輸出Base ... .text:000030B4 LDR R3, =(_ZTV7Derived_ptr - 0x30BE) ;開始調用Derived的構造函數 .text:000030B6 LDR R2, =(aDerived____0 - 0x30C2) .text:000030B8 MOVS R1, R5 ;R1被賦值為R5,指向位於.rodata段中的lesson6 .text:000030BA ADD R3, PC ; _ZTV7Derived_ptr .text:000030BC LDR R3, [R3] ; `vtable for'Derived 取出Derived的虛表指針,指向.data.rel.ro,即0x70428978 .text:000030BE ADD R2, PC ; "Derived ..." R2指向位於.rodata段中Derived .text:000030C0 STR R6, [R4,#0xC] ;把18賦值給剛分配的內存單元的首地址+12 .text:000030C2 ADDS R3, #8 ;R3賦值為Derived的虛表指針+8,即0x70428980 .text:000030C4 STR R3, [R4] ;把0x70428980賦值給剛分配的內存單元的首地址 .text:000030C6 MOVS R3, #0xA ;把R3賦值給10 .text:000030C8 MOVS R0, #3 ;把R0賦值給3 .text:000030CA STRB R3, [R4,#0x10] ;把10賦值給剛分配的內存單元的首地址+16 .text:000030CC BL j_j___android_log_print ;目前R0,R1,R2都被初始化為正確的值了,開始調用j_j___android_log_print .text:000030D0 LDR R3, [R4] ;取出虛表指針,即位於.data.rel.ro段的地址0x70428980 .text:000030D2 MOVS R0, R4 ;把R0賦值為R4,作為第一個參數,R4為剛分配的內存單元的首地址,即this指針 .text:000030D4 LDR R3, [R3] ;取出虛表指針所指向的內容,即Derived的Display方法的地址 .text:000030D6 BLX R3 ;跳轉到Derived的Display方法 .text:000030D8 LDR R3, [R4] ;取出虛表指針,即位於.data.rel.ro段的地址0x70428980 .text:000030DA MOVS R0, R4 ;把R0賦值為R4,作為第一個參數,R4為剛分配的內存單元的首地址,即this指針 .text:000030DC LDR R3, [R3,#8] ;取出虛表指針+8所指向的內容,即Derived的~Derived方法的地址 .text:000030DE BLX R3 ;跳轉到Derived的~Derived方法 .text:000030E0 POP {R4-R6,PC}
.text:704220E4 off_704220E4 DCD _ZTV4Base_ptr - 0x70422098 .text:704220E4 ; DATA XREF: Java_com_example_ndkreverse6_Lesson6_main+8r .text:704220E8 off_704220E8 DCD aLesson6 - 0x704220AA .text:704220E8 ; DATA XREF: Java_com_example_ndkreverse6_Lesson6_main+18r .text:704220E8 ; "lesson6" .text:704220EC off_704220EC DCD aBase____0 - 0x704220B0 .text:704220EC ; DATA XREF: Java_com_example_ndkreverse6_Lesson6_main+1Ar .text:704220EC ; "Base ..." .text:704220F0 off_704220F0 DCD _ZTV7Derived_ptr - 0x704220BE .text:704220F0 ; DATA XREF: Java_com_example_ndkreverse6_Lesson6_main+2Cr .text:704220F4 off_704220F4 DCD aDerived____0 - 0x704220C2 .text:704220F4 ; DATA XREF: Java_com_example_ndkreverse6_Lesson6_main+2Er .text:704220F4 ; "Derived ..."
.got:70428F24 AREA .got, DATA .got:70428F24 ; ORG 0x70428F24 .got:70428F24 _ZTV4Base_ptr DCD _ZTV4Base ; DATA XREF: Base::~Base()+Ao .got:70428F24 ; Base::~Base()+Cr ... .got:70428F24 ; `vtable for'Base .got:70428F28 _ZTV7Derived_ptr DCD _ZTV7Derived ; DATA XREF: Derived::~Derived()+Ao .got:70428F28 ; Derived::~Derived()+Cr ... .got:70428F28 ; `vtable for'Derived
.data.rel.ro:70428960 ; `vtable for'Base .data.rel.ro:70428960 _ZTV4Base DCB 0 ; DATA XREF: Base::~Base()+Co .data.rel.ro:70428960 ; Java_com_example_ndkreverse6_Lesson6_main+Eo ... .data.rel.ro:70428961 DCB 0 .data.rel.ro:70428962 DCB 0 .data.rel.ro:70428963 DCB 0 .data.rel.ro:70428964 DCD _ZTI4Base ; `typeinfo for'Base .data.rel.ro:70428968 DCD _ZN4Base7displayEv+1 .data.rel.ro:7042896C DCD _ZN4BaseD2Ev+1 .data.rel.ro:70428970 DCD _ZN4BaseD0Ev+1 .data.rel.ro:70428974 ALIGN 8 .data.rel.ro:70428978 WEAK _ZTV7Derived .data.rel.ro:70428978 ; `vtable for'Derived .data.rel.ro:70428978 _ZTV7Derived DCB 0 ; DATA XREF: Derived::~Derived()+Co .data.rel.ro:70428978 ; Java_com_example_ndkreverse6_Lesson6_main+34o ... .data.rel.ro:70428979 DCB 0 .data.rel.ro:7042897A DCB 0 .data.rel.ro:7042897B DCB 0 .data.rel.ro:7042897C DCD _ZTI7Derived ; `typeinfo for'Derived .data.rel.ro:70428980 DCD _ZN7Derived7displayEv+1 .data.rel.ro:70428984 DCD _ZN7DerivedD2Ev+1 .data.rel.ro:70428988 DCD _ZN7DerivedD0Ev+1
.rodata:704267B0 ; `typeinfo name for'Base .rodata:704267B0 _ZTS4Base DCB "4Base",0 ; DATA XREF: .data.rel.ro:_ZTI4Base+4o .rodata:704267B6 ALIGN 4 .rodata:704267B8 WEAK _ZTS7Derived .rodata:704267B8 ; `typeinfo name for'Derived .rodata:704267B8 _ZTS7Derived DCB "7Derived",0 ; DATA XREF: .data.rel.ro:_ZTI7Derived+4o .rodata:704267C1 ALIGN 4 .rodata:704267C4 aLesson6 DCB "lesson6",0 ; DATA XREF: Base::display(void)+Eo .rodata:704267C4 ; .text:off_70421FD4o ... .rodata:704267CC aBaseDBasecharD DCB "Base:%d, BaseChar:%d",0 .rodata:704267CC ; DATA XREF: Base::display(void)+10o .rodata:704267CC ; .text:off_70421FD8o .rodata:704267E1 aDerivedDDerive DCB "Derived:%d, DerivedChar:%d",0 .rodata:704267E1 ; DATA XREF: Derived::display(void)+Eo .rodata:704267E1 ; .text:off_70422000o .rodata:704267FC aBase___ DCB "~Base ...",0 ; DATA XREF: Base::~Base()+10o .rodata:704267FC ; .text:off_7042202Co .rodata:70426806 aDerived___ DCB "~Derived ...",0 ; DATA XREF: Derived::~Derived()+10o .rodata:70426806 ; .text:off_70422060o .rodata:70426813 aBase____0 DCB "Base ...",0 ; DATA XREF: Java_com_example_ndkreverse6_Lesson6_main+24o .rodata:70426813 ; .text:off_704220ECo .rodata:7042681C aDerived____0 DCB "Derived ...",0 ; DATA XREF: Java_com_example_ndkreverse6_Lesson6_main+36o .rodata:7042681C ; .text:off_704220F4o
我們看到執行Display方法時,首先從對象的首地址取出虛表指針,然後再從虛表指針所指向的內存單元中取出具體的Display指令的地址,然後跳轉到對應的指令執行。
.text:70421FDC ; _DWORD Derived::display(Derived *__hidden this) .text:70421FDC WEAK _ZN7Derived7displayEv .text:70421FDC _ZN7Derived7displayEv ; DATA XREF: .data.rel.ro:_ZTV7Derived+8o .text:70421FDC .text:70421FDC var_10 = -0x10 .text:70421FDC .text:70421FDC PUSH {R0,R1,R4,LR} .text:70421FDE MOVS R4, R0 ;R4被賦值給R0,即剛分配的內存單元的首地址 .text:70421FE0 LDRB R3, [R0,#0x10] ;從內存單元中取出子類的第二個變量,也就是DerivedChar的值 .text:70421FE2 LDR R1, =(aLesson6 - 0x70421FEC) .text:70421FE4 LDR R2, =(aDerivedDDerive - 0x70421FEE) .text:70421FE6 STR R3, [SP,#0x10+var_10] ;作為第五個參數存放在堆棧中傳遞 .text:70421FE8 ADD R1, PC ; "lesson6" .text:70421FEA ADD R2, PC ; "Derived:%d, DerivedChar:%d" .text:70421FEC LDR R3, [R4,#0xC] ; 第四個參數是子類的第一個變量,也就是Derived的值 .text:70421FEE MOVS R0, #3 .text:70421FF0 BL j_j___android_log_print .text:70421FF4 MOVS R0, R4 ; this ;返回this指針 .text:70421FF6 BL _ZN4Base7displayEv ; Base::display(void) .text:70421FFA POP {R0,R1,R4,PC}
接著,我們繼續析構函數,思路和Display方法是一樣的,首先從對象的首地址取出虛表指針,然後再從虛表指針+8所指向的內存單元中取出具體的Derived類析構函數的地址,然後跳轉到對應的指令執行。
.text:70422064 ; _DWORD __fastcall Derived::~Derived(Derived *__hidden this) .text:70422064 WEAK _ZN7DerivedD0Ev .text:70422064 _ZN7DerivedD0Ev ; DATA XREF: .data.rel.ro:_ZTV7Derived+10o .text:70422064 PUSH {R4,LR} .text:70422066 MOVS R4, R0 ;R4被賦值為R0,即對象的首地址 .text:70422068 BL _ZN7DerivedD2Ev ; Derived::~Derived() .text:7042206C MOVS R0, R4 ; void * R0被賦值為對象的首地址 .text:7042206E BL _ZdlPv ; operator delete(void *) 真正的釋放堆空間 .text:70422072 MOVS R0, R4
.text:70422030 ; _DWORD __fastcall Derived::~Derived(Derived *__hidden this) .text:70422030 WEAK _ZN7DerivedD2Ev .text:70422030 _ZN7DerivedD2Ev ; CODE XREF: Derived::~Derived()+4p .text:70422030 ; DATA XREF: .data.rel.ro:_ZTV7Derived+Co .text:70422030 PUSH {R4,LR} ; Alternative name is 'Derived::~Derived()' .text:70422032 MOVS R4, R0 ;R4被賦值為R0,即對象的首地址 .text:70422034 LDR R3, =(_ZTV7Derived_ptr - 0x7042203E) .text:70422036 LDR R1, =(aLesson6 - 0x70422042) .text:70422038 LDR R2, =(aDerived___ - 0x70422044) .text:7042203A ADD R3, PC ; _ZTV7Derived_ptr .text:7042203C LDR R3, [R3] ; `vtable for'Derived R3 取出Derived的虛表指針,指向.data.rel.ro,即0x70428978 .text:7042203E ADD R1, PC ; "lesson6" .text:70422040 ADD R2, PC ; "~Derived ..." .text:70422042 ADDS R3, #8 ;R3賦值為Derived的虛表指針+8,即0x70428980 .text:70422044 STR R3, [R0] ;把0x70428980賦值給剛分配的內存單元的首地址 .text:70422046 MOVS R0, #3 .text:70422048 BL j_j___android_log_print .text:7042204C MOVS R0, R4 ; this R0被賦值為對象的首地址 .text:7042204E BL _ZN4BaseD2Ev ; Base::~Base() 調用父類的析構函數 .text:70422052 MOVS R0, R4 ;返回對象的首地址 .text:70422054 POP {R4,PC}
.text:70422004 ; _DWORD __fastcall Base::~Base(Base *__hidden this) .text:70422004 WEAK _ZN4BaseD2Ev .text:70422004 _ZN4BaseD2Ev ; CODE XREF: Derived::~Derived()+1Ep .text:70422004 ; Base::~Base()+4p .text:70422004 ; DATA XREF: ... .text:70422004 PUSH {R4,LR} ; Alternative name is 'Base::~Base()' .text:70422006 MOVS R4, R0 ;R4被賦值為對象的首地址 .text:70422008 LDR R3, =(_ZTV4Base_ptr - 0x70422012) .text:7042200A LDR R1, =(aLesson6 - 0x70422016) .text:7042200C LDR R2, =(aBase___ - 0x70422018) .text:7042200E ADD R3, PC ; _ZTV4Base_ptr .text:70422010 LDR R3, [R3] ; `vtable for'Base 取出Base的虛表指針,指向.data.rel.ro,即0x70428960 .text:70422012 ADD R1, PC ; "lesson6" .text:70422014 ADD R2, PC ; "~Base ..." .text:70422016 ADDS R3, #8 ;R3+8再賦值給R3,R3指向了0x70428968 .text:70422018 STR R3, [R0] ;將0x70428968賦值給剛分配的內存單元的首地址(所指向的內存單元) .text:7042201A MOVS R0, #3 ;R0是第一個參數,賦值為3 .text:7042201C BL j_j___android_log_print .text:70422020 MOVS R0, R4 ;返回對象的首地址 .text:70422022 POP {R4,PC}
總結:父類的成員位於低地址,子類的成員位於高地址,虛表指針位於對象的首地址,構造對象時,先構造父類,虛表指針指向父類的虛表,再構造子類,虛表指針指向子類的虛表。析構對象時,先析構子類,虛表指針指向子類的虛表,再析構父類,虛表指針指向父類的虛表。
ViewPager作為Android最常用的的組件之一,相信大家在項目中會頻繁的使用到的,例如利用ViewPager制作引導頁、輪播圖,甚至做整個app的表
通過SpannableStringBuilder來實現,它就像html裡邊的元素改變指定文字的文字顏色或背景色public class MainActivity exte
在程序開發過程中,LOG是廣泛使用的用來記錄程序執行過程的機制,它既可以用於程序調試,也可以用於
前言Android Studio對於快捷鍵的設置比較的靈活,開發者在從不同的平台轉移到Android Studio進行Android開發的時候,應該都能找到合適的KeyM