【NE現場】
pid: 5252, tid: 5252, name: ndroid.contacts >>> com.android.contacts <<< signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x1458 x0 0000000000000000 x1 0000000090d9892c x2 0000000000000001 x3 000000000000012c x4 0000000000000000 x5 000000000000012c x6 0000000000000000 x7 0000000000000003 x8 0000000000000000 x9 0000007feddaa618 x10 0000007fedda7f78 x11 0000000000000008 x12 0000007f9fdc5088 x13 0000000000200020 x14 0000000000000001 x15 0b9f4a1e359c32de x16 0000000000000000 x17 0000000000200020 x18 0000000000000010 x19 0000007f9ba96a00 x20 0000000080000000 x21 0000000013500ca0 x22 00000000ffffffff x23 0000000000000000 x24 000000007144b210 x25 000000001329d880 x26 0000000090d9892c x27 0000000000000018 x28 0000000000000000 x29 0000007fedda85e8 x30 0000000075f56664 sp 0000007fedda8210 pc 0000000075f56678 pstate 0000000060000000 backtrace: #00 pc 0000000000000678 /data/dalvik-cache/arm64/system@framework@boot.oat (offset 0x4356000) #01 pc 0000000000000660 /data/dalvik-cache/arm64/system@framework@boot.oat (offset 0x4356000)
tombstone信息量太少了,這類問題必須要有core才能分析。
【#0層棧】
(gdb) bt #0 0x0000000075f56678 in ?? () #1 0x0000000073d5b178 in ?? () #2 0x0b9f4a1e359c32de in ?? () Backtrace stopped: previous frame identical to this frame (corrupt stack?)
由於boot.oat沒有symbol,所以都得手動分析:
(gdb) disassemble 0x0000000075f56678-0x10,+0x20
Dump of assembler code from 0x75f56668 to 0x75f56688:
0x0000000075f56668: mov x1, x26
0x0000000075f5666c: mov x27, x0
0x0000000075f56670: mov w2, #0x1 // #1
0x0000000075f56674: ldr w0, [x1]
=> 0x0000000075f56678: ldr x0, [x0,#5208]
0x0000000075f5667c: ldr x30, [x0,#48]
0x0000000075f56680: blr x30
0x0000000075f56684: str w20, [x26,#384]
End of assembler dump.
x0為空,所以取x0+5208的值時出現FC,Fatal addr為5208,也就是0x1458。
由於沒有symbol不好分析,所以我們借助dumpoat來解析boot.oat。
我們可以通過虛擬地址,找到代碼在boot.oat中的位置,計算公式如下:
offset = vaddr - base - 0x1000
其中
vaddr就是當前虛擬地址0x75f56678,
base是boot.oat的加載地址,可以從tombstone中獲取
00000000'70dac000-00000000'71bfffff rw- 0 e54000 /data/dalvik-cache/arm64/system@framework@boot.art
00000000'71c00000-00000000'74aeafff r-- 0 2eeb000 /data/dalvik-cache/arm64/system@framework@boot.oat
00000000'74aeb000-00000000'75395fff r-x 2eeb000 8ab000 /data/dalvik-cache/arm64/system@framework@boot.oat (load base 0x74aeb000)
00000000'75396000-00000000'75396fff r-x 3796000 1000 /data/dalvik-cache/arm64/system@framework@boot.oat
00000000'75397000-00000000'75447fff r-x 3797000 b1000 /data/dalvik-cache/arm64/system@framework@boot.oat
base = 0x71c00000
這樣,oatdump中的offset = 0x75f56678 - 0x71c00000 - 0x1000 = 0x4355678
8: void android.view.View.<init>(android.content.Context) (dex_method_idx=12408) ... 0x04355474: d1400bf0 sub x16, sp, #0x2000 (8192) 0x04355478: b940021f ldr wzr, [x16] 0x0435547c: f8180fe0 str x0, [sp, #-128]! 0x04355480: a90357f4 stp x20, x21, [sp, #48] 0x04355484: a9045ff6 stp x22, x23, [sp, #64] 0x04355488: a90567f8 stp x24, x25, [sp, #80] 0x0435548c: a9066ffa stp x26, x27, [sp, #96] 0x04355490: a9077bfc stp x28, lr, [sp, #112] 0x04355494: 79400270 ldrh w16, [tr] ; state_and_flags 0x04355498: 35001910 cbnz w16, #+0x320 (addr 0x43557b8) 0x0435549c: aa0003f8 mov x24, x0 0x043554a0: aa0203f9 mov x25, x2 0x043554a4: 52800017 mov w23, #0x0 0x043554a8: 12800016 mov w22, #0xffffffff 0x043554ac: 52800015 mov w21, #0x0 0x043554b0: 52b00014 mov w20, #0x80000000 0x043554b4: 1c001960 ldr s0, pc+812 (addr 0x43557e0) (nan) 0x043554b8: aa0103fa mov x26, x1 0x043554bc: 58001940 ldr x0, pc+808 (addr 0x43557e4) (0x715dd520 / 1901974816) 0x043554c0: f940181e ldr lr, [x0, #48] 0x043554c4: d63f03c0 blr lr 0x043554c8: b9003355 str w21, [x26, #48] 0x043554cc: 39069357 strb w23, [x26, #420] 0x043554d0: b900f356 str w22, [x26, #240] ... 0x04355650: aa0103fb mov x27, x1 0x04355654: b9400020 ldr w0, [x1] 0x04355658: f940a400 ldr x0, [x0, #328] 0x0435565c: f940181e ldr lr, [x0, #48] 0x04355660: d63f03c0 blr lr 0x04355664: b9016340 str w0, [x26, #352] 0x04355668: aa1a03e1 mov x1, x26 0x0435566c: aa0003fb mov x27, x0 0x04355670: 52800022 mov w2, #0x1 0x04355674: b9400020 ldr w0, [x1] => 0x04355678: f94a2c00 ldr x0, [x0, #5208] 0x0435567c: f940181e ldr lr, [x0, #48] 0x04355680: d63f03c0 blr lr
可知,這個函數就是View的帶一個參數的構造函數:
void android.view.View.<init>(android.content.Context)
其中,x0是x1中取出來的,x1又是x26,而開始的地方x26是從x1賦值過來的。
我們知道,java的native代碼中,x0是ArtMethod,x1是View.this指針,x2是第一個參數...
所以這里就是this是view的Object,它的值是0x90d9892c(tombstone中的x26值),
而Object的第一個word是這個object對應的class,現在這個Class的值是0。
ldr x0, [x0, #5208]其實是試圖從Class中獲取某個方法的ArtMethod。
由於Class的值是0,所以就出現FC。
看起來是View.this有問題,也就是調用void android.view.View.<init>(android.content.Context)時傳入的x1有問題。
接下來得去上一層棧繼續分析
當前sp是0x7fedda8210,而函數入口的寄存器上下文如下:
8: void android.view.View.<init>(android.content.Context) (dex_method_idx=12408)
DEX CODE:
...
0x04355474: d1400bf0 sub x16, sp, #0x2000 (8192)
0x04355478: b940021f ldr wzr, [x16]
0x0435547c: f8180fe0 str x0, [sp, #-128]!
0x04355480: a90357f4 stp x20, x21, [sp, #48]
0x04355484: a9045ff6 stp x22, x23, [sp, #64]
0x04355488: a90567f8 stp x24, x25, [sp, #80]
0x0435548c: a9066ffa stp x26, x27, [sp, #96]
0x04355490: a9077bfc stp x28, lr, [sp, #112]
因此,
lr = [sp + 112 + 8] = [0x7fedda8210 + 112 + 8] = 0x75f569c4
【#1層棧】
#1層的offset = 0x75f569c4 - 0x71c00000 - 0x1000 = 0x43559c4
對應oatdump中:
11: void android.view.View.<init>(android.content.Context, android.util.AttributeSet, int, int) (dex_method_idx=12411)
CODE: (code_offset=0x04355954 size_offset=0x04355950 size=22708)...
0x04355954: d1400bf0 sub x16, sp, #0x2000 (8192)
0x04355958: b940021f ldr wzr, [x16]
0x0435595c: d10903ff sub sp, sp, #0x240 (576)
0x04355960: f90003e0 str x0, [sp]
0x04355964: a91ed7f4 stp x20, x21, [sp, #488]
0x04355968: a91fdff6 stp x22, x23, [sp, #504]
0x0435596c: 910823f0 add x16, sp, #0x208 (520)
0x04355970: a9006618 stp x24, x25, [x16]
0x04355974: 910863f0 add x16, sp, #0x218 (536)
0x04355978: a9006e1a stp x26, x27, [x16]
0x0435597c: 9108a3f0 add x16, sp, #0x228 (552)
0x04355980: a900761c stp x28, x29, [x16]
0x04355984: f9011ffe str lr, [sp, #568]
0x04355988: b9024fe2 str w2, [sp, #588]
0x0435598c: b90253e3 str w3, [sp, #592]
0x04355990: b90257e4 str w4, [sp, #596]
0x04355994: b9025be5 str w5, [sp, #600]
0x04355998: 79400270 ldrh w16, [tr] ; state_and_flags
0x0435599c: 3502aef0 cbnz w16, #+0x55dc (addr 0x435af78)
0x043559a0: aa0003f4 mov x20, x0
0x043559a4: aa0303f5 mov x21, x3
0x043559a8: aa0403f6 mov x22, x4
0x043559ac: aa0503f7 mov x23, x5
0x043559b0: aa0103f8 mov x24, x1
0x043559b4: aa0203f9 mov x25, x2
0x043559b8: 5802bf20 ldr x0, pc+22500 (addr 0x435b19c) (0x7144b210 / 1900327440)
0x043559bc: f940181e ldr lr, [x0, #48]
0x043559c0: d63f03c0 blr lr
=> 0x043559c4: f9401280 ldr x0, [x20, #32]
0x043559c8: b96bdc00 ldr w0, [x0, #11228]
0x043559cc: b9454003 ldr w3, [x0, #1344]
函數的起始位置的虛擬地址為:
vaddr = 0x75f569c4 - 0x043559c4 + 0x04355954 = 0x75f56954
用gdb看這段代碼:
(gdb) disassemble 0x75f56954,0x75f569c4
Dump of assembler code from 0x75f56954 to 0x75f569c4:
0x0000000075f56954: stp x0, x1, [sp,#-16]!
0x0000000075f56958: adr x0, 0x75f56968
0x0000000075f5695c: ldr x0, [x0]
0x0000000075f56960: ldr x1, [x0]
0x0000000075f56964: br x1
0x0000000075f56968: adrp x0, 0x2ed26000
0x0000000075f5696c: .inst 0x0000007f ; undefined
0x0000000075f56970: ldp x0, x1, [sp],#16
0x0000000075f56974: add x16, sp, #0x218
0x0000000075f56978: stp x26, x27, [x16]
0x0000000075f5697c: add x16, sp, #0x228
0x0000000075f56980: stp x28, x29, [x16]
0x0000000075f56984: str x30, [sp,#568]
0x0000000075f56988: str w2, [sp,#588]
0x0000000075f5698c: str w3, [sp,#592]
0x0000000075f56990: str w4, [sp,#596]
0x0000000075f56994: str w5, [sp,#600]
0x0000000075f56998: ldrh w16, [x19]
0x0000000075f5699c: cbnz w16, 0x75f5bf78
0x0000000075f569a0: mov x20, x0
0x0000000075f569a4: mov x21, x3
0x0000000075f569a8: mov x22, x4
0x0000000075f569ac: mov x23, x5
0x0000000075f569b0: mov x24, x1
0x0000000075f569b4: mov x25, x2
0x0000000075f569b8: ldr x0, 0x75f5c19c
0x0000000075f569bc: ldr x30, [x0,#48]
0x0000000075f569c0: blr x30
End of assembler dump.
發現一個很有趣的現象:
函數開頭0x75f56954~0x75f56990的代碼和oatdump數據不一樣!
從代碼的內容來看,是一段hook code,代碼在運行時被篡改。
先理解hook的邏輯:
(gdb) disassemble 0x75f56954,0x75f569c4 Dump of assembler code from 0x75f56954 to 0x75f569c4: 0x0000000075f56954: stp x0, x1, [sp,#-16]! # 保存x0和x1 0x0000000075f56958: adr x0, 0x75f56968 # x0 = 0x75f56968 0x0000000075f5695c: ldr x0, [x0] # x0 = [0x75f56968] = 0x0000007f90dc6e80 0x0000000075f56960: ldr x1, [x0] # x1 = [0x0000007f90dc6e80] = 0x0000007f7f4e3148 0x0000000075f56964: br x1 # 跳轉到0x7f7f4e3148 0x0000000075f56968: 0x90dc6e80 # 存放跳轉地址指針的低32位 0x0000000075f5696c: 0x0000007f # 存放跳轉地址指針的高32位
0x0000000075f56970: ldp x0, x1, [sp],#16 # 恢復x0和x1,hook結束后就要跳轉到這邊了
看起來時hook過程中恢復x1值有問題。
接下來需要證明傳入的x1值時正確的即能證明是hook的問題。
這需要進一步推導上一級棧,但這里不能再用通常的棧推到方法,因為hook邏輯里也會改棧,在這里無法判斷上一級棧的位置。
既然反向無法推到棧,那我們就嘗試正向推導。
【正向推導棧】
我們需要推導出調用View構造函數的深層的調用棧,首先得列出棧數據,然后在棧數據中找出返回地址x30。
這里有一個技巧:一般的java函數入口的壓棧動作有如下幾個特點:
1、棧幀的起始位置都是16字節對齊的
2、棧幀的最低位置保存x0,也就是該函數對應的ArtMethod。
3、棧幀的最高為保存X30,也就是該函數的返回地址,也就是caller的地址。
如:
0x04355254: d1400bf0 sub x16, sp, #0x2000 (8192) 0x04355258: b940021f ldr wzr, [x16] 0x0435525c: f8190fe0 str x0, [sp, #-112]! 0x04355260: a90357f4 stp x20, x21, [sp, #48] 0x04355264: a9045ff6 stp x22, x23, [sp, #64] 0x04355268: a90567f8 stp x24, x25, [sp, #80] 0x0435526c: a9067bfa stp x26, lr, [sp, #96]
而相鄰兩個函數的棧幀是相連的,也就是說caller的ArtMethod和callee的返回地址也就是caller的函數地址時連着的。
而ArtMethod是放在boot.art的,而NativeCode是放在boot.oat的可執行段里。
通過map表可以看到它們的地址范圍為:
00000000'70dac000-00000000'71bfffff rw- 0 e54000 /data/dalvik-cache/arm64/system@framework@boot.art 00000000'71c00000-00000000'74aeafff r-- 0 2eeb000 /data/dalvik-cache/arm64/system@framework@boot.oat 00000000'74aeb000-00000000'75395fff r-x 2eeb000 8ab000 /data/dalvik-cache/arm64/system@framework@boot.oat (load base 0x74aeb000) 00000000'75396000-00000000'75396fff r-x 3796000 1000 /data/dalvik-cache/arm64/system@framework@boot.oat 00000000'75397000-00000000'75447fff r-x 3797000 b1000 /data/dalvik-cache/arm64/system@framework@boot.oat 00000000'75448000-00000000'75448fff r-x 3848000 1000 /data/dalvik-cache/arm64/system@framework@boot.oat ... 00000000'76353000-00000000'76353fff r-x 4753000 1000 /data/dalvik-cache/arm64/system@framework@boot.oat 00000000'76354000-00000000'76810fff r-x 4754000 4bd000 /data/dalvik-cache/arm64/system@framework@boot.oat 00000000'76811000-00000000'76811fff r-- 4c11000 1000 /data/dalvik-cache/arm64/system@framework@boot.oat
(注:這里boot.oat被切割成多個段,主要是因為hook的時候要給目標內存賦予可寫權限,這樣虛擬內存就被切割成多個小段了)
從上面的map表可知,
boot.art的范圍是0x0000000070dac000~0x0000000071c00000
boot.oat的范圍是0x0000000074aeb000~0x0000000076812000
因此,我們需要在棧里面找到一對地址:
16字節位對齊位置是boot.art范圍內的地址,它的前一個地址是在boot.oat范圍內的很可能是一個棧幀的分界點
...
0x7feddaa9c0: 0x000000001356cc00 0x00000000133ee9d0 0x7feddaa9d0: 0x00000000135279a0 0x000000001356cc00 0x7feddaa9e0: 0x0000000000000000 0x0000000075fe3cfc 0x7feddaa9f0: 0x0000000071619328 0x0000000071275c18 0x7feddaaa00: 0x000000001356cc00 0xffffffff133ee9d0 0x7feddaaa10: 0x1329d880133ee9d0 0x1329d88000000000 0x7feddaaa20: 0x0000007feddaaa50 0x0000007f9b91e878 0x7feddaaa30: 0x00000000133ee9d0 0x00000000133f0330 0x7feddaaa40: 0x0000007f9b91e858 0x0000007f9ba96a00 0x7feddaaa50: 0x0000000071275b30 0x00000000757395dc 0x7feddaaa60: 0x000000007165ef68 0x0000007feddae178 0x7feddaaa70: 0x70fabf2000000001 0x0000000600000007 0x7feddaaa80: 0x0000000000000000 0x00000000135279a0 0x7feddaaa90: 0x000000001356cc00 0x00000000133ee9d0 0x7feddaaaa0: 0x000000001329d880 0x00000000133f02e0 0x7feddaaab0: 0x0000000000000001 0x0000000000000002 0x7feddaaac0: 0x0000000071275d70 0x000000007104d2f0 0x7feddaaad0: 0x0000000071275b30 0x0000000075fe113c 0x7feddaaae0: 0x0000000071619130 0x1356cc00135279a0 0x7feddaaaf0: 0x0000000000000001 0x00000000133f02e0 0x7feddaab00: 0x0000000071275d70 0x000000007104d2f0 0x7feddaab10: 0x0000000071275b30 0x00000000757361a4
按GDB打印ART基礎類中的方法,通過腳本獲取ArtMehod對應的方法的定義:
(gdb) art_get_method_name_by_method_id 0x0000000071619130 android.view.LayoutInflater.createViewFromTag "(Landroid/view/View;Ljava/lang/String;Landroid/content/Context;Landroid/util/AttributeSet;)Landroid/view/View;"
(gdb) art_get_method_name_by_method_id 0x0000000071619328 android.view.LayoutInflater.createViewFromTag "(Landroid/view/View;Ljava/lang/String;Landroid/content/Context;Landroid/util/AttributeSet;Z)Landroid/view/View;"
(gdb) art_get_method_name_by_method_id 0x0000000071619520 android.view.LayoutInflater.onCreateView "(Landroid/view/View;Ljava/lang/String;Landroid/util/AttributeSet;)Landroid/view/View;"
對應的源碼如下:
@frameworks/base/core/java/android/view/LayoutInflater.java 751 private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) { 752 return createViewFromTag(parent, name, context, attrs, false); 753 } 770 View createViewFromTag(View parent, String name, Context context, AttributeSet attrs, 771 boolean ignoreThemeAttr) { 772 if (name.equals("view")) { 773 name = attrs.getAttributeValue(null, "class"); 774 } 775 776 // Apply a theme wrapper, if allowed and one is specified. 777 if (!ignoreThemeAttr) { 778 final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME); 779 final int themeResId = ta.getResourceId(0, 0); 780 if (themeResId != 0) { 781 context = new ContextThemeWrapper(context, themeResId); 782 } 783 ta.recycle(); 784 } 785 786 if (name.equals(TAG_1995)) { 787 // Let's party like it's 1995! 788 return new BlinkLayout(context, attrs); 789 } 790 791 try { 792 View view; 793 if (mFactory2 != null) { 794 view = mFactory2.onCreateView(parent, name, context, attrs); 795 } else if (mFactory != null) { 796 view = mFactory.onCreateView(name, context, attrs); 797 } else { 798 view = null; 799 } 800
當然,也用oatdump文件推導也是可以的,不過如果中間有native代碼,oatdump就無法推下去了,所以還是用gdb推導更好一些。
NE時是在View的構造函數中掛掉的,調用函數前,得先alloc堆空間,然后再把申請到的堆地址當做this指針傳給構造函數。
現在是this指針有問題,所以我們得先找到alloc的地方。
為此,從onCreateView()這個函數入手看起來是非常合理的。
【android.view.View android.view.LayoutInflater.onCreateView】
它的棧幀上面已經貼過:
0x7feddaa980: 0x00000000133f02e0 0x0000000075fe4f2c
0x7feddaa990: 0x0000000071619520 0x133ee9d0135279a0 0x7feddaa9a0: 0x00000000133f02e0 0x13500f4013508808 0x7feddaa9b0: 0x00000000133ee9d0 0x00000000135279a0 0x7feddaa9c0: 0x000000001356cc00 0x00000000133ee9d0 0x7feddaa9d0: 0x00000000135279a0 0x000000001356cc00 0x7feddaa9e0: 0x0000000000000000 0x0000000075fe3cfc
先通過ArtMethod找到它的代碼:
(gdb) p *('art::ArtMethod ' *)0x0000000071619520
$53 = {
declaring_class_ = {
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 1893415840
}, <No data fields>}
},
access_flags_ = 524292,
dex_code_item_offset_ = 2108352,
dex_method_index_ = 11030,
method_index_ = 22,
hotness_count_ = 0,
ptr_sized_fields_ = {
dex_cache_resolved_methods_ = 0x719c8b7800000000,
dex_cache_resolved_types_ = 0x719c2e8800000000,
entry_point_from_jni_ = 0x0,
entry_point_from_quick_compiled_code_ = 0x75fe4ee400000000
}
}
它的入口地址時0x75fe4ee4(注意,這里gdb有個bug,它的偏移看起來是有問題的),
它的下一級函數的返回地址又是0x75fe4f2c,所以相關代碼為:
(gdb) disassemble 0x75fe4ee4, 0x75fe4f2c
Dump of assembler code from 0x75fe4ee4 to 0x75fe4f2c:
0x0000000075fe4ee4: sub x16, sp, #0x2, lsl #12
0x0000000075fe4ee8: ldr wzr, [x16]
0x0000000075fe4eec: str x0, [sp,#-96]!
0x0000000075fe4ef0: stp x20, x21, [sp,#56]
0x0000000075fe4ef4: stp x22, x23, [sp,#72]
0x0000000075fe4ef8: str x30, [sp,#88]
0x0000000075fe4efc: ldrh w16, [x19]
0x0000000075fe4f00: cbnz w16, 0x75fe4f40
0x0000000075fe4f04: mov x20, x2
0x0000000075fe4f08: mov x2, x3
0x0000000075fe4f0c: mov x3, x4
0x0000000075fe4f10: mov x21, x1
0x0000000075fe4f14: mov x22, x2
0x0000000075fe4f18: mov x23, x3
0x0000000075fe4f1c: ldr w0, [x1] # 獲取this的class
0x0000000075fe4f20: ldr x0, [x0,#328] # 從class中取某個成員函數的ArtMethod
0x0000000075fe4f24: ldr x30, [x0,#48] # 從ArtMehod中取NativeCode的地址
=> 0x0000000075fe4f28: blr x30 # 跳轉到子函數
End of assembler dump.
關鍵是部分是紅色代碼,我們需要知道x1的值,才能推導下去,而x1是上一級函數中傳入的,
每一次函數調用前,都會把參數保存在x20以上的寄存器里,而進入下一集函數時,又會把x20保存在棧中。
所以我們得先找到上一級函數,看看在調用當前函數前,x1值時保存在哪個寄存器里的。
上一級函數的ArtMehod是0x0000000071619328,從之前的方法一樣,我們可以得到它的NativeCode:
android.view.View android.view.LayoutInflater.createViewFromTag:
(gdb) disassemble 0x0000000075fe3924,0x0000000075fe3cfc
Dump of assembler code from 0x75fe3924 to 0x75fe3cfc:
0x0000000075fe3924: sub x16, sp, #0x2, lsl #12
0x0000000075fe3928: ldr wzr, [x16]
0x0000000075fe392c: str x0, [sp,#-240]!
0x0000000075fe3930: stp x20, x21, [sp,#152]
0x0000000075fe3934: stp x22, x23, [sp,#168]
0x0000000075fe3938: stp x24, x25, [sp,#184]
0x0000000075fe393c: stp x26, x27, [sp,#200]
0x0000000075fe3940: stp x28, x29, [sp,#21
...
0x0000000075fe3cdc: mov x1, x21
0x0000000075fe3ce0: mov x2, x22
0x0000000075fe3ce4: mov x4, x27
0x0000000075fe3ce8: mov x3, x20
0x0000000075fe3cec: ldr w0, [x1]
0x0000000075fe3cf0: ldr x0, [x0,#320]
0x0000000075fe3cf4: ldr x30, [x0,#48]
0x0000000075fe3cf8: blr x30
可知,LayoutInflater.createViewFromTag()在調用LayoutInflater.onCreateView()前把x1保存在x21。
我們再看看LayoutInflater.onCreateView()中x21是保存在哪的:
(gdb) disassemble 0x75fe4ee4,0x75fe4f2c
Dump of assembler code from 0x75fe4ee4 to 0x75fe4f2c:
0x0000000075fe4ee4: sub x16, sp, #0x2, lsl #12
0x0000000075fe4ee8: ldr wzr, [x16]
0x0000000075fe4eec: str x0, [sp,#-96]!
0x0000000075fe4ef0: stp x20, x21, [sp,#56]
0x0000000075fe4ef4: stp x22, x23, [sp,#72]
0x0000000075fe4ef8: str x30, [sp,#88]
...
各個寄存器在棧中的保存位置如下:
0x7feddaa980: 0x00000000133f02e0 0x0000000075fe4f2c 0x7feddaa990: 0x0000000071619520 0x133ee9d0135279a0
x0 0x7feddaa9a0: 0x00000000133f02e0 0x13500f4013508808 0x7feddaa9b0: 0x00000000133ee9d0 0x00000000135279a0 0x7feddaa9c0: 0x000000001356cc00 0x00000000133ee9d0
x20 0x7feddaa9d0: 0x00000000135279a0 0x000000001356cc00
x21 x22 0x7feddaa9e0: 0x0000000000000000 0x0000000075fe3cfc
x23 x30
x21的值是0x135279a0,這個在map表上示java堆的main space,這個區域一般用於存放小的java對象。
00000000'12c00000-00000000'12e07fff rw- 0 208000 /dev/ashmem/dalvik-main space (deleted) 00000000'12e08000-00000000'22bfffff rw- 208000 fdf8000 /dev/ashmem/dalvik-main space (deleted)
用腳本查看這個對象的類名是什么:
(gdb) art_print_object 0x00000000135279a0 com.android.internal.policy.PhoneLayoutInflater
再回過頭繼續分析LayoutInflater.onCreateView()的代碼:
(gdb) disassemble 0x75fe4ee4, 0x75fe4f2c Dump of assembler code from 0x75fe4ee4 to 0x75fe4f2c: 0x0000000075fe4ee4: sub x16, sp, #0x2, lsl #12 0x0000000075fe4ee8: ldr wzr, [x16] 0x0000000075fe4eec: str x0, [sp,#-96]! 0x0000000075fe4ef0: stp x20, x21, [sp,#56] 0x0000000075fe4ef4: stp x22, x23, [sp,#72] 0x0000000075fe4ef8: str x30, [sp,#88] 0x0000000075fe4efc: ldrh w16, [x19] 0x0000000075fe4f00: cbnz w16, 0x75fe4f40 0x0000000075fe4f04: mov x20, x2 0x0000000075fe4f08: mov x2, x3 0x0000000075fe4f0c: mov x3, x4 0x0000000075fe4f10: mov x21, x1 0x0000000075fe4f14: mov x22, x2 0x0000000075fe4f18: mov x23, x3 0x0000000075fe4f1c: ldr w0, [x1] # w0 = [0x135279a0] = 0x70db9428 0x0000000075fe4f20: ldr x0, [x0,#328] # x0 = [0x70db9428+328] = 0x71636c08 0x0000000075fe4f24: ldr x30, [x0,#48] # x30 = [0x71636c08+48] = 0x76360574 => 0x0000000075fe4f28: blr x30 End of assembler dump.
下一級函數的ArtMehod是0x71636c08,對應NativeCode的地址是0x76360574。
同樣,可以通過腳本查看這個函數的定義:
(gdb) art_get_method_name_by_method_id 0x0000000071636c08 com.android.internal.policy.PhoneLayoutInflater.onCreateView "(Ljava/lang/String;Landroid/util/AttributeSet;)Landroid/view/View;"
再看看它的NativeCode:
(gdb) disassemble 0x0000000076360574,+0x80
Dump of assembler code from 0x76360574 to 0x763605f4:
0x0000000076360574: sub x16, sp, #0x2, lsl #12
0x0000000076360578: ldr wzr, [x16]
0x000000007636057c: str x0, [sp,#-176]!
0x0000000076360580: stp x20, x21, [sp,#104]
0x0000000076360584: stp x22, x23, [sp,#120]
0x0000000076360588: stp x24, x25, [sp,#136]
0x000000007636058c: stp x26, x27, [sp,#152]
0x0000000076360590: str x30, [sp,#168]
0x0000000076360594: str w1, [sp,#184]
0x0000000076360598: str w2, [sp,#188]
0x000000007636059c: str w3, [sp,#192]
0x00000000763605a0: ldrh w16, [x19]
0x00000000763605a4: cbnz w16, 0x763606a4
0x00000000763605a8: ldr w4, [x0]
0x00000000763605ac: ldr w20, [x4,#384]
0x00000000763605b0: str w20, [sp,#28]
...
從NativeCode中的sp的變化,可以知道這個函數的棧幀大小。有時候sp頻繁變化,通過計算sp值來算棧幀大小比較麻煩。
這里還有個技巧,java的NativeCode的起始位置的頭部會有一段描述性數據結構,可以從這個數據接口中獲取棧幀大小,如:
(gdb) disassemble 0x0000000076360574-0x20,+0x80
Dump of assembler code from 0x76360554 to 0x763605d4:
0x0000000076360554: subs w24, w28, #0x8da, lsl #12
0x0000000076360558: .inst 0x00000000 ; undefined
0x000000007636055c: .inst 0x00000000 ; undefined
0x0000000076360560: .inst 0x01a982ff ; undefined
0x0000000076360564: .inst 0x000000b0 ; undefined
0x0000000076360568: .inst 0x4ff00000 ; undefined
0x000000007636056c: .inst 0x00000000 ; undefined
0x0000000076360570: .inst 0x00000178 ; undefined
0x0000000076360574: sub x16, sp, #0x2, lsl #12
0x0000000076360578: ldr wzr, [x16]
0x000000007636057c: str x0, [sp,#-176]!
0x0000000076360580: stp x20, x21, [sp,#104]
0x0000000076360584: stp x22, x23, [sp,#120]
0x0000000076360588: stp x24, x25, [sp,#136]
0x000000007636058c: stp x26, x27, [sp,#152]
0x0000000076360590: str x30, [sp,#168]
0x0000000076360594: str w1, [sp,#184]
0x0000000076360598: str w2, [sp,#188]
0x000000007636059c: str w3, [sp,#192]
0x00000000763605a0: ldrh w16, [x19]
0x00000000763605a4: cbnz w16, 0x763606a4
0x00000000763605a8: ldr w4, [x0]
0x00000000763605ac: ldr w20, [x4,#384]
0x00000000763605b0: str w20, [sp,#28]
0x00000000763605b4: ldr w21, [x20,#8]
0x00000000763605b8: str w21, [sp,#32]
0x00000000763605bc: mov x23, x21
0x00000000763605c0: mov x21, x0
0x00000000763605c4: mov x4, x3
0x00000000763605c8: mov x22, x20
0x00000000763605cc: mov w20, #0x0 // #0
0x00000000763605d0: str w20, [sp,#24]
End of assembler dump.
也就是說NativeCode的地址減去0x10的地址上就存放有該函數棧幀的大小:
(gdb) x /wx 0x76360574-0x10 0x76360564: 0x000000b0
當然,如果不是java的NativeCode,我們只能通過sp值的變化來推導棧幀大小。
我們已經知道com.android.internal.policy.PhoneLayoutInflater.onCreateView()的棧幀大小,接下來就可以完整的分析這個函數了。
0x7feddaa8d0: 0x000000001329d880 0x0000000076360604
0x7feddaa8e0: 0x0000000071636c08 0x133ee9d0135279a0 0x7feddaa8f0: 0x133f02e0768594f0 0x76873e6000000000 0x7feddaa900: 0x0000000000000003 0x0000000013451a60 0x7feddaa910: 0x0000000013536df0 0x000000001356cc00 0x7feddaa920: 0x00000000133ee9d0 0x0000000000000000 0x7feddaa930: 0x0000000013536df0 0x000000001356cc00 0x7feddaa940: 0x00000000133ee9d0 0x000000001356cc00 0x7feddaa950: 0x00000000135279a0 0x00000000133ee9d0 0x7feddaa960: 0x00000000133f02e0 0x000000001329d880 0x7feddaa970: 0x0000000071619328 0x0000000000000000 0x7feddaa980: 0x00000000133f02e0 0x0000000075fe4f2c 0x7feddaa990: 0x0000000071619520 0x133ee9d0135279a0 0x7feddaa9a0: 0x00000000133f02e0 0x13500f4013508808 0x7feddaa9b0: 0x00000000133ee9d0 0x00000000135279a0 0x7feddaa9c0: 0x000000001356cc00 0x00000000133ee9d0 0x7feddaa9d0: 0x00000000135279a0 0x000000001356cc00 0x7feddaa9e0: 0x0000000000000000 0x0000000075fe3cfc
用這種方式可以一級一級的推導下去,過程都是很機械的,就不再說了,我們只把棧幀和對應方法名給貼出來:
... art::Constructor_newInstance0 0x7feddaa580: 0x0000007f9ba3e300 0x0000007feddaa624 0x7feddaa590: 0x0000000000000000 0x0000000000000000 0x7feddaa5a0: 0x00000000009c3200 0x0000000000000000 0x7feddaa5b0: 0x0000000000000000 0x000000001352603c 0x7feddaa5c0: 0x0000000000000002 0x0000007f9b704880 0x7feddaa5d0: 0x0000007f9b9f0838 0x0000000000000002 0x7feddaa5e0: 0x0000007feddaa818 0x0000007feddaa728 0x7feddaa5f0: 0x00000000eddaa800 0x0000007f9ba96a00 0x7feddaa600: 0x0000007f9ba3e300 0x0000007f9bae8000 0x7feddaa610: 0x0000007f00000002 0x0000007feddaa6a8 0x7feddaa620: 0x70eac5e000000001 0x0000007f9ba96a00 0x7feddaa630: 0x0000000000000001 0x0b9f4a1e359c32de 0x7feddaa640: 0x0000000000000000 0x00000000131a5be0 0x7feddaa650: 0x0000000000000000 0x00000000133ee9d0 0x7feddaa660: 0x00000000716192f0 0x0000000070db3ba0 0x7feddaa670: 0x0000000013536dd8 0x00000000131a5be0 0x7feddaa680: 0x0000007f9b750540 0x0000007f9ba96a00 0x7feddaa690: 0x000000001329d880 0x0000000074bb6160 java.lang.Object java.lang.reflect.Constructor.newInstance0(java.lang.Object[]) 0x7feddaa6a0: 0x00000000715ddf78 0x0000007feddae178 0x7feddaa6b0: 0x131a5be000000002 0x0000000713536dd8 0x7feddaa6c0: 0x000000007137a7c8 0x00000000134c6f00 0x7feddaa6d0: 0x0000000000000000 0x0000000000000000 0x7feddaa6e0: 0x0000000000000000 0x0000000000000000 0x7feddaa6f0: 0x0000000000000000 0x0000000000000000 0x7feddaa700: 0x0000000000000000 0x0000000000000000 0x7feddaa710: 0x0000007f9ba96a00 0x0000000000000000 0x7feddaa720: 0x00000000131a5be0 0x0000000013536dd8 0x7feddaa730: 0x0000000070db3ba0 0x00000000716192f0 0x7feddaa740: 0x00000000133ee9d0 0x0000000000000000 0x7feddaa750: 0x00000000131a5be0 0x0000000000000000 0x7feddaa760: 0x000000001329d880 0x0000000074bb70d0 java.lang.Object java.lang.reflect.Constructor.newInstance(java.lang.Object[]) 0x7feddaa770: 0x00000000715de3a0 0x13536dd8131a5be0 0x7feddaa780: 0x000000007136e0c0 0x70ea322070ddab10 0x7feddaa790: 0x00000000135279a0 0x00000000135279a0 0x7feddaa7a0: 0x0000000013536dd8 0x00000000133f02e0 0x7feddaa7b0: 0x0000000070db3ba0 0x0000000075fe2864 android.view.View android.view.LayoutInflater.createView(java.lang.String, java.lang.String, android.util.AttributeSet) 0x7feddaa7c0: 0x00000000716192f0 0x0000000074b0af88 0x7feddaa7d0: 0x000000007133c3e8 0x70db3ba01350eaa0 0x7feddaa7e0: 0x0000000013508940 0x0000000000000001 0x7feddaa7f0: 0x0000000012c2bc40 0x0000000013500f40 0x7feddaa800: 0x0000007f95720cb8 0x0000007f822b46e4 0x7feddaa810: 0x0000007f95720cb8 0x0000007f797f4940 0x7feddaa820: 0x1352600076813ab0 0x1352600013523400 0x7feddaa830: 0x1350894013523400 0x0000007f797f4940 0x7feddaa840: 0x0000007f9ba3e300 0x0b9f4a1e359c32de 0x7feddaa850: 0x0000007f76bf2450 0x00000000133f0330 0x7feddaa860: 0x0000007f9ba3e300 0x0000000012c2bc40 0x7feddaa870: 0x0000000013500ca0 0x0000000013500f40 0x7feddaa880: 0x00000000133f02e0 0x0000000000000000 0x7feddaa890: 0x0000000071636c08 0x0000000076873e60 0x7feddaa8a0: 0x0000000000000003 0x00000000135279a0 0x7feddaa8b0: 0x00000000133ee9d0 0x00000000133f02e0 0x7feddaa8c0: 0x00000000768594f0 0x0000000000000000 0x7feddaa8d0: 0x000000001329d880 0x0000000076360604 android.view.View com.android.internal.policy.PhoneLayoutInflater.onCreateView(java.lang.String, android.util.AttributeSet) 0x7feddaa8e0: 0x0000000071636c08 0x133ee9d0135279a0 0x7feddaa8f0: 0x133f02e0768594f0 0x76873e6000000000 0x7feddaa900: 0x0000000000000003 0x0000000013451a60 0x7feddaa910: 0x0000000013536df0 0x000000001356cc00 0x7feddaa920: 0x00000000133ee9d0 0x0000000000000000 0x7feddaa930: 0x0000000013536df0 0x000000001356cc00 0x7feddaa940: 0x00000000133ee9d0 0x000000001356cc00 0x7feddaa950: 0x00000000135279a0 0x00000000133ee9d0 0x7feddaa960: 0x00000000133f02e0 0x000000001329d880 0x7feddaa970: 0x0000000071619328 0x0000000000000000 0x7feddaa980: 0x00000000133f02e0 0x0000000075fe4f2c android.view.View android.view.LayoutInflater.onCreateView(android.view.View, java.lang.String, android.util.AttributeSet) 0x7feddaa990: 0x0000000071619520 0x133ee9d0135279a0 0x7feddaa9a0: 0x00000000133f02e0 0x13500f4013508808 0x7feddaa9b0: 0x00000000133ee9d0 0x00000000135279a0 0x7feddaa9c0: 0x000000001356cc00 0x00000000133ee9d0 0x7feddaa9d0: 0x00000000135279a0 0x000000001356cc00 0x7feddaa9e0: 0x0000000000000000 0x0000000075fe3cfc
在java.lang.Object java.lang.reflect.Constructor.newInstance0(java.lang.Object[])的代碼中發現了有趣的代碼:
(gdb) disassemble art::Constructor_newInstance0 Dump of assembler code for function art::Constructor_newInstance0(_JNIEnv*, _jobject*, _jobjectArray*): 0x0000007f9b750540 <+0>: stp x28, x27, [sp,#-96]! 0x0000007f9b750544 <+4>: stp x26, x25, [sp,#16] 0x0000007f9b750548 <+8>: stp x24, x23, [sp,#32] 0x0000007f9b75054c <+12>: stp x22, x21, [sp,#48] 0x0000007f9b750550 <+16>: stp x20, x19, [sp,#64] 0x0000007f9b750554 <+20>: stp x29, x30, [sp,#80] 0x0000007f9b750558 <+24>: add x29, sp, #0x50 0x0000007f9b75055c <+28>: sub sp, sp, #0xc0 0x0000007f9b750560 <+32>: adrp x25, 0x7f9b9f8000 <_ZTVN3art32BuildNativeCallFrameStateMachineINS_27BuildGenericJniFrameVisitor11FillJniCallEEE> 0x0000007f9b750564 <+36>: mov x19, x2 0x0000007f9b750568 <+40>: mov x20, x1 0x0000007f9b75056c <+44>: ldr x25, [x25,#1376] 0x0000007f9b750570 <+48>: ldr x25, [x25] ... 0x0000007f9b750a04 <+1220>: mov x1, x26 0x0000007f9b750a08 <+1224>: bl 0x7f9b4a06dc <art::mirror::Class::AllocObject(art::Thread*)> 0x0000007f9b750a0c <+1228>: mov x8, x0 0x0000007f9b750a10 <+1232>: cbz x8, 0x7f9b750a70 <art::Constructor_newInstance0(_JNIEnv*, _jobject*, _jobjectArray*)+1328> 0x0000007f9b750a14 <+1236>: ldr x16, [sp,#128] 0x0000007f9b750a18 <+1240>: mov x2, x8 0x0000007f9b750a1c <+1244>: ldr w1, [x16,#24] 0x0000007f9b750a20 <+1248>: add x0, x16, #0x20 0x0000007f9b750a24 <+1252>: bl 0x7f9b5f0204 <art::IndirectReferenceTable::Add(unsigned int, art::mirror::Object*)> 0x0000007f9b750a28 <+1256>: mov x21, x0 0x0000007f9b750a2c <+1260>: add x0, sp, #0x78 0x0000007f9b750a30 <+1264>: orr w4, wzr, #0x2 0x0000007f9b750a34 <+1268>: mov x1, x20 0x0000007f9b750a38 <+1272>: mov x2, x21 0x0000007f9b750a3c <+1276>: mov x3, x19 => 0x0000007f9b750a40 <+1280>: bl 0x7f9b7f5474 <art::InvokeMethod(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned long)> 0x0000007f9b750a44 <+1284>: b 0x7f9b750a84 <art::Constructor_newInstance0(_JNIEnv*, _jobject*, _jobjectArray*)+1348> 0x0000007f9b750a48 <+1288>: mov x21, xzr
終於找到了申請堆內存的地方!
這里先申請內存,然后再把這個內存保存在ReferenceTable中,再通過InvokeMethod調用類的構造函數。
查看它的源碼:
static jobject Constructor_newInstance0(JNIEnv* env, jobject javaMethod, jobjectArray javaArgs) { ScopedFastNativeObjectAccess soa(env); mirror::Constructor* m = soa.Decode<mirror::Constructor*>(javaMethod); StackHandleScope<1> hs(soa.Self()); Handle<mirror::Class> c(hs.NewHandle(m->GetDeclaringClass())); ... mirror::Object* receiver = movable ? c->AllocObject(soa.Self()) : c->AllocNonMovableObject(soa.Self()); if (receiver == nullptr) { return nullptr; } jobject javaReceiver = soa.AddLocalReference<jobject>(receiver); InvokeMethod(soa, javaMethod, javaReceiver, javaArgs, 2); // Constructors are ()V methods, so we shouldn't touch the result of InvokeMethod. return javaReceiver; }
1、先通過AllocObject()申請JAVA堆內存
2、再通過AddLocalReference()將申請到的Object指針添加到Local Reference Table中,並返回Table中的Index
3、再將Index傳給下一級函數InvokeMethod()
這里我們要看Object值是否正確,這需要查看Local Reference Table,先看一下AddLocalReference()的邏輯:
@art/runtime/jni_env_ext-inl.h inline T JNIEnvExt::AddLocalReference(mirror::Object* obj) { IndirectRef ref = locals.Add(local_ref_cookie, obj); return reinterpret_cast<T>(ref); }
其中locals是JNIEnvExt的成員:
@art/runtime/jni_env_ext.h struct JNIEnvExt : public JNIEnv { ... // JNI local references. IndirectReferenceTable locals GUARDED_BY(Locks::mutator_lock_);
因此:
IndirectRef IndirectReferenceTable::Add(uint32_t cookie, mirror::Object* obj) { IRTSegmentState prevState; prevState.all = cookie; size_t topIndex = segment_state_.parts.topIndex; ... IndirectRef result; int numHoles = segment_state_.parts.numHoles - prevState.parts.numHoles; size_t index; if (numHoles > 0) { ... } else { // Add to the end. index = topIndex++; segment_state_.parts.topIndex = topIndex; } table_[index].Add(obj); result = ToIndirectRef(index); return result; }
接下來需要通過棧來推導這個值:
首先列出art::Constructor_newInstance0()的棧幀:
0x7feddaa580: 0x0000007f9ba3e300 0x0000007feddaa624
0x7feddaa590: 0x0000000000000000 0x0000000000000000
0x7feddaa5a0: 0x00000000009c3200 0x0000000000000000
0x7feddaa5b0: 0x0000000000000000 0x000000001352603c
0x7feddaa5c0: 0x0000000000000002 0x0000007f9b704880
0x7feddaa5d0: 0x0000007f9b9f0838 0x0000000000000002
0x7feddaa5e0: 0x0000007feddaa818 0x0000007feddaa728
0x7feddaa5f0: 0x00000000eddaa800 0x0000007f9ba96a00
0x7feddaa600: 0x0000007f9ba3e300 0x0000007f9bae8000
0x7feddaa610: 0x0000007f00000002 0x0000007feddaa6a8
0x7feddaa620: 0x70eac5e000000001 0x0000007f9ba96a00
0x7feddaa630: 0x0000000000000001 0x0b9f4a1e359c32de
0x7feddaa640: 0x0000000000000000 0x00000000131a5be0
0x7feddaa650: 0x0000000000000000 0x00000000133ee9d0
0x7feddaa660: 0x00000000716192f0 0x0000000070db3ba0
0x7feddaa670: 0x0000000013536dd8 0x00000000131a5be0
0x7feddaa680: 0x0000007f9b750540 0x0000007f9ba96a00
0x7feddaa690: 0x000000001329d880 0x0000000074bb6160
匯編代碼:
(gdb) disassemble art::Constructor_newInstance0 Dump of assembler code for function art::Constructor_newInstance0(_JNIEnv*, _jobject*, _jobjectArray*): 0x0000007f9b750540 <+0>: stp x28, x27, [sp,#-96]! 0x0000007f9b750544 <+4>: stp x26, x25, [sp,#16] 0x0000007f9b750548 <+8>: stp x24, x23, [sp,#32] 0x0000007f9b75054c <+12>: stp x22, x21, [sp,#48] 0x0000007f9b750550 <+16>: stp x20, x19, [sp,#64] 0x0000007f9b750554 <+20>: stp x29, x30, [sp,#80] 0x0000007f9b750558 <+24>: add x29, sp, #0x50 0x0000007f9b75055c <+28>: sub sp, sp, #0xc0 ... 0x0000007f9b750a04 <+1220>: mov x1, x26 0x0000007f9b750a08 <+1224>: bl 0x7f9b4a06dc <art::mirror::Class::AllocObject(art::Thread*)> 0x0000007f9b750a0c <+1228>: mov x8, x0 0x0000007f9b750a10 <+1232>: cbz x8, 0x7f9b750a70 <art::Constructor_newInstance0(_JNIEnv*, _jobject*, _jobjectArray*)+1328> 0x0000007f9b750a14 <+1236>: ldr x16, [sp,#128] ; x16 = [0x7feddaa580+128] = [0x7feddaa600] = 0x7f9ba3e300 0x0000007f9b750a18 <+1240>: mov x2, x8 0x0000007f9b750a1c <+1244>: ldr w1, [x16,#24] 0x0000007f9b750a20 <+1248>: add x0, x16, #0x20 ; x0 = x16 + 0x20 = 0x7f9ba3e320 0x0000007f9b750a24 <+1252>: bl 0x7f9b5f0204 <art::IndirectReferenceTable::Add(unsigned int, art::mirror::Object*)> 0x0000007f9b750a28 <+1256>: mov x21, x0 0x0000007f9b750a2c <+1260>: add x0, sp, #0x78 0x0000007f9b750a30 <+1264>: orr w4, wzr, #0x2 0x0000007f9b750a34 <+1268>: mov x1, x20 0x0000007f9b750a38 <+1272>: mov x2, x21 0x0000007f9b750a3c <+1276>: mov x3, x19 => 0x0000007f9b750a40 <+1280>: bl 0x7f9b7f5474 <art::InvokeMethod(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned long)>
通過棧可以推導出art::IndirectReferenceTable對象的地址為:0x0000007f9ba3e320
(gdb) p *('art::IndirectReferenceTable' *) 0x0000007f9ba3e320 $8 = { segment_state_ = { all = 8, parts = { topIndex = 8, numHoles = 0 } }, table_mem_map_ = { __ptr_ = { <std::__1::__libcpp_compressed_pair_imp<art::MemMap*, std::__1::default_delete<art::MemMap>, 2>> = { <std::__1::default_delete<art::MemMap>> = {<No data fields>}, members of std::__1::__libcpp_compressed_pair_imp<art::MemMap*, std::__1::default_delete<art::MemMap>, 2>: __first_ = 0x7f9ba39400 }, <No data fields>} }, table_ = 0x7f9fdc5000, kind_ = art::kLocal, max_entries_ = 512 }
我們重點關注table_和topIndex。其中IrtEntry* table_,且這個table的長度是topIndex+1 = 9個,
因此我們可以列出所有table中的數據:
(gdb) p /x *('art::IrtEntry' *)0x7f9fdc5000@9
$66 = {{
serial_ = 0x0,
references_ = {{
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x70ec7fa8
}, <No data fields>}
}, {
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x0
}, <No data fields>}
}, {
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x12c0b000
}, <No data fields>}
}}
}, {
serial_ = 0x2,
references_ = {{
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x0
}, <No data fields>}
}, {
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x70fdb3a8
}, <No data fields>}
}, {
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x768135e0
}, <No data fields>}
}}
}, {
serial_ = 0x2,
references_ = {{
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x0
}, <No data fields>}
}, {
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x70f8d6e0
}, <No data fields>}
}, {
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x76813fa0
}, <No data fields>}
}}
}, {
serial_ = 0x2,
references_ = {{
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x0
}, <No data fields>}
}, {
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x70f82380
}, <No data fields>}
}, {
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x768141b8
}, <No data fields>}
}}
}, {
serial_ = 0x1,
references_ = {{
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x70f30268
}, <No data fields>}
}, {
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x768141f0
}, <No data fields>}
}, {
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x12c050d8
}, <No data fields>}
}}
}, {
serial_ = 0x1,
references_ = {{
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x70ec7fa8
}, <No data fields>}
}, {
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x76813088
}, <No data fields>}
}, {
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x12c100d0
}, <No data fields>}
}}
}, {
serial_ = 0x0,
references_ = {{
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x70eb0360
}, <No data fields>}
}, {
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x12c0e0e0
}, <No data fields>}
}, {
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x71006a88
}, <No data fields>}
}}
}, {
serial_ = 0x0,
references_ = {{
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x1354a5e0
}, <No data fields>}
}, {
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x1356cc00
}, <No data fields>}
}, {
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x133ee9d0
}, <No data fields>}
}}
}, {
serial_ = 0x2,
references_ = {{
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x1356cc00
}, <No data fields>}
}, {
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x1356cc00
}, <No data fields>}
}, {
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0x1329d880
}, <No data fields>}
}}
}}
這里我們只關注最后一個加到table里的index為7的項:
serial_ = 0x0, references_ = {{ root_ = { <art::mirror::ObjectReference<false, art::mirror::Object>> = { reference_ = 0x1354a5e0 }, <No data fields>} }, { root_ = { <art::mirror::ObjectReference<false, art::mirror::Object>> = { reference_ = 0x1356cc00 }, <No data fields>} }, { root_ = { <art::mirror::ObjectReference<false, art::mirror::Object>> = { reference_ = 0x133ee9d0 }, <No data fields>} }}
由於該項的serial_為0,所以對應的Object為0x1354a5e0:
(gdb) art_print_object 0x1354a5e0 android.view.View
果然,Alloc后返回的Object的所屬類是View,可以確定Alloc過程本身沒有問題。
我們看一下這個Object的內容:
(gdb) x /32wx 0x1354a5e0
0x1354a5e0: 0x70eac5e0 0x00000000 0x00000000 0x00000000
0x1354a5f0: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a600: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a610: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a620: 0x00000000 0x00000000 0x00000000 0x00000000
...
可以看到,除了Class*以外,其他值都都還沒賦上(其他的值都是在構造函數中賦值)。
(gdb) p *('art::mirror::Class' *)0x70eac5e0
$67 = {
<art::mirror::Object> = {
static kVTableLength = 11,
static hash_code_seed = {
<std::__1::atomic<unsigned int>> = {
<std::__1::__atomic_base<unsigned int, true>> = {
<std::__1::__atomic_base<unsigned int, false>> = {
__a_ = 4074838449
}, <No data fields>}, <No data fields>}, <No data fields>},
klass_ = {
<art::mirror::ObjectReference<false, art::mirror::Class>> = {
reference_ = 1894950384
}, <No data fields>},
monitor_ = 0
},
members of art::mirror::Class:
static kClassWalkSuper = 3221225472,
annotation_type_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0
}, <No data fields>},
class_loader_ = {
<art::mirror::ObjectReference<false, art::mirror::ClassLoader>> = {
reference_ = 0
}, <No data fields>},
component_type_ = {
<art::mirror::ObjectReference<false, art::mirror::Class>> = {
reference_ = 0
}, <No data fields>},
dex_cache_ = {
<art::mirror::ObjectReference<false, art::mirror::DexCache>> = {
reference_ = 1893384976
}, <No data fields>},
iftable_ = {
<art::mirror::ObjectReference<false, art::mirror::IfTable>> = {
reference_ = 1893881384
}, <No data fields>},
name_ = {
<art::mirror::ObjectReference<false, art::mirror::String>> = {
reference_ = 1898151168
}, <No data fields>},
super_class_ = {
<art::mirror::ObjectReference<false, art::mirror::Class>> = {
reference_ = 1894543104
}, <No data fields>},
verify_error_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 0
}, <No data fields>},
vtable_ = {
<art::mirror::ObjectReference<false, art::mirror::PointerArray>> = {
reference_ = 0
}, <No data fields>},
access_flags_ = 524289,
dex_cache_strings_ = 1906522624,
ifields_ = 1898725992,
methods_ = 1900326984,
sfields_ = 1898720820,
class_flags_ = 0,
class_size_ = 7099,
clinit_thread_id_ = 0,
dex_class_def_idx_ = 759,
dex_type_idx_ = 1567,
num_reference_instance_fields_ = 51,
num_reference_static_fields_ = 54,
object_size_ = 423,
primitive_type_ = 131072,
reference_instance_offsets_ = 3221225472,
status_ = art::mirror::Class::kStatusInitialized,
copied_methods_offset_ = 793,
virtual_methods_offset_ = 91,
static java_lang_Class_ = {
root_ = {
<art::mirror::ObjectReference<false, art::mirror::Object>> = {
reference_ = 1894950384
}, <No data fields>}
}
}
這個Object的大小是423,因此它的實際范圍為:
(gdb) x /32wx 0x1354a5e0
0x1354a5e0: 0x70eac5e0 0x00000000 0x00000000 0x00000000
0x1354a5f0: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a600: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a610: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a620: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a630: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a640: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a650: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a660: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a670: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a680: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a690: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a6a0: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a6b0: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a6c0: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a6d0: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a6e0: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a6f0: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a700: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a710: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a720: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a730: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a740: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a750: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a760: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a770: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a780: 0x00000000 0x00000000 0x00000000 0x00000000
0x1354a790: 0x1354a940 0x00000000 0x00000000 0x00000000
0x1354a7a0: 0x00000000 0x00000000 0x00000000 0x00000000
確定只有Class賦值對了,其他的都沒賦值。因此可以確定,進入構造函數前,Object的指針可定已經是錯的了。
接下來就得看這個Object*指針是如何傳到構造函數就可以了。
art::IndirectReferenceTable::Add()返回的是一個索引,它的計算方式是:
class IndirectReferenceTable {
IndirectRef ToIndirectRef(uint32_t tableIndex) const {
uint32_t serialChunk = table_[tableIndex].GetSerial();
uintptr_t uref = (serialChunk << 20) | (tableIndex << 2) | kind_;
return reinterpret_cast<IndirectRef>(uref);
}
這里serialChunk是0,tableIndex是7,kind_是1。因此這里返回的索引值是0x1d(29)。
這個索引值就是代碼中的javaReceiver,它被傳入到InvokeMethod()函數:
static jobject Constructor_newInstance0(JNIEnv* env, jobject javaMethod, jobjectArray javaArgs) { ScopedFastNativeObjectAccess soa(env); mirror::Constructor* m = soa.Decode<mirror::Constructor*>(javaMethod); StackHandleScope<1> hs(soa.Self()); Handle<mirror::Class> c(hs.NewHandle(m->GetDeclaringClass())); ... mirror::Object* receiver = movable ? c->AllocObject(soa.Self()) : c->AllocNonMovableObject(soa.Self()); if (receiver == nullptr) { return nullptr; } jobject javaReceiver = soa.AddLocalReference<jobject>(receiver); InvokeMethod(soa, javaMethod, javaReceiver, javaArgs, 2); // Constructors are ()V methods, so we shouldn't touch the result of InvokeMethod. return javaReceiver; }
再看看其他參數:
0x0000007f9b750a2c <+1260>: add x0, sp, #0x78 0x0000007f9b750a30 <+1264>: orr w4, wzr, #0x2 0x0000007f9b750a34 <+1268>: mov x1, x20 0x0000007f9b750a38 <+1272>: mov x2, x21 0x0000007f9b750a3c <+1276>: mov x3, x19 => 0x0000007f9b750a40 <+1280>: bl 0x7f9b7f5474 <art::InvokeMethod(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned long)>
這些參數保存在x19~x21寄存器中,而下一級函數art::InvokeMethod()函數開頭,會保存X19~x21。
(gdb) disassemble art::InvokeMethod Dump of assembler code for function art::InvokeMethod(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned long): 0x0000007f9b7f5474 <+0>: stp x28, x27, [sp,#-96]! 0x0000007f9b7f5478 <+4>: stp x26, x25, [sp,#16] 0x0000007f9b7f547c <+8>: stp x24, x23, [sp,#32] 0x0000007f9b7f5480 <+12>: stp x22, x21, [sp,#48] 0x0000007f9b7f5484 <+16>: stp x20, x19, [sp,#64] 0x0000007f9b7f5488 <+20>: stp x29, x30, [sp,#80] 0x0000007f9b7f548c <+24>: add x29, sp, #0x50 0x0000007f9b7f5490 <+28>: sub sp, sp, #0x140 ...
因此通過art::InvokeMethod()的棧幀就可以推導出這些參數的值:
art::InvokeMethod()的棧幀:
0x7feddaa3e0: 0x0000007feddaa508 0x0000007f9ba96a00 0x7feddaa3f0: 0x0000007feddaa460 0x0000007f9b5470d4 0x7feddaa400: 0x0000007f9ba96a00 0x0000005900000043 0x7feddaa410: 0x0000000000080001 0x0000007f9b9fe170 0x7feddaa420: 0x0b9f4a1e359c32de 0x0b9f4a1e359c32de 0x7feddaa430: 0x0000007f9baa92e0 0x00000000131a5be0 0x7feddaa440: 0x0000000000000002 0x00000000000001a7 0x7feddaa450: 0x0000007f9ba4c700 0x0000007f9ba96a00 0x7feddaa460: 0x0000007feddaa570 0x0000007f9b4a08f4 0x7feddaa470: 0x0000007feddaa480 0x0000007f9f55db80 0x7feddaa480: 0x0000007f77e65200 0x000000007570771c 0x7feddaa490: 0x0000007f9ba96a00 0x0000000000000000 0x7feddaa4a0: 0x00000000735a6477 0x0000000c00000003 0x7feddaa4b0: 0x0000007feddaa4b8 0x1329d8801354a5e0 0x7feddaa4c0: 0x00000000133f02e0 0x0000000070eac5e0 0x7feddaa4d0: 0x0000000013508940 0x0000000000000001 0x7feddaa4e0: 0x0000007f9b94d333 0x0000007f9ba3e300 0x7feddaa4f0: 0x0000007f9f629000 0x0000000000000000 0x7feddaa500: 0x00000000000001b0 0x00000000000001b0 0x7feddaa510: 0x00000000000001b0 0x0b9f4a1e359c32de 0x7feddaa520: 0x0000000000000000 0x0000007feddaa624 x28 x27 0x7feddaa530: 0x0000007f9ba96a00 0x0b9f4a1e359c32de x26 x25 0x7feddaa540: 0x00000000716192f0 0x00000000131a5be0 x24 x23 0x7feddaa550: 0x0000007f9ba96a00 0x000000000000001d x22 x21 0x7feddaa560: 0x0000007feddaa6b4 0x0000007feddaa6b8 x20 x19 0x7feddaa570: 0x0000007feddaa690 0x0000007f9b750a44 x29 x30 lr art::Constructor_newInstance0
果然參數值確實是0x1d。
再看看art::InvokeMethod()函數的實現:
jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaMethod, jobject javaReceiver, jobject javaArgs, size_t num_frames) { ...
auto* abstract_method = soa.Decode<mirror::AbstractMethod*>(javaMethod);
ArtMethod* m = abstract_method->GetArtMethod(); mirror::Object* receiver = nullptr;
if (!m->IsStatic()) { if (declaring_class->IsStringClass() && m->IsConstructor()) { ... } else { // Check that the receiver is non-null and an instance of the field's declaring class. receiver = soa.Decode<mirror::Object*>(javaReceiver); ...
m = receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(m, sizeof(void*)); } } // Invoke the method. JValue result; uint32_t shorty_len = 0; const char* shorty = np_method->GetShorty(&shorty_len); ArgArray arg_array(shorty, shorty_len); if (!arg_array.BuildArgArrayFromObjectArray(receiver, objects, np_method)) { ... } InvokeWithArgArray(soa, m, &arg_array, &result, shorty); ... return soa.AddLocalReference<jobject>(BoxPrimitive(Primitive::GetType(shorty[0]), result)); }
這里參數javaReceiver被Decode后加入到arg_array里,m是ArtMethod。
同樣可以通過棧推導來確定這些值:
先看看匯編代碼:
0x0000007f9b7f5914 <+1184>: bl 0x7f9b7f642c <art::ArgArray::BuildArgArrayFromObjectArray(art::mirror::Object*, art::mirror::ObjectArray<art::mirror::Object>*, art::ArtMethod*)> 0x0000007f9b7f5918 <+1188>: tbz w0, #0, 0x7f9b7f59e0 <art::InvokeMethod(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned long)+1388> 0x0000007f9b7f591c <+1192>: add x2, sp, #0xc0 0x0000007f9b7f5920 <+1196>: add x3, sp, #0xb8 0x0000007f9b7f5924 <+1200>: mov x0, x19 0x0000007f9b7f5928 <+1204>: mov x1, x20 0x0000007f9b7f592c <+1208>: mov x4, x25 0x0000007f9b7f5930 <+1212>: bl 0x7f9b7f3728 <art::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::ArgArray*, art::JValue*, char const*)> => 0x0000007f9b7f5934 <+1216>: ldr x16, [x19]
art::ArtMethod*對應x1,和x20,這個值可以在art:InvokeWithArgArray()函數的棧幀中找到,具體方法和參考前面的推導過程。這里直接給出它的值:0x000000007144b248
art::ArgArray*對應的值時x2,也就是sp + 0xc0 = 0x7feddaa3e0 + 0xc0 = 0x7feddaa4a0
先看看ArtMehod:
(gdb) art_get_method_name_by_method_id 0x000000007144b248 android.view.View.<init> "(Landroid/content/Context;Landroid/util/AttributeSet;)V"
再看看ArgArray:
(gdb) p /x *('art::ArgArray' *)0x7feddaa4a0
$26 = {
shorty_ = 0x735a6477,
shorty_len_ = 0x3,
num_bytes_ = 0xc,
arg_array_ = 0x7feddaa4b8,
small_arg_array_ = {0x1354a5e0, 0x1329d880, 0x133f02e0, 0x0, 0x70eac5e0, 0x0, 0x13508940, 0x0, 0x1, 0x0, 0x9b94d333, 0x7f, 0x9ba3e300, 0x7f, 0x9f629000, 0x7f},
large_arg_array_ = {
__ptr_ = {
<std::__1::__libcpp_compressed_pair_imp<unsigned int*, std::__1::default_delete<unsigned int []>, 2>> = {
<std::__1::default_delete<unsigned int []>> = {<No data fields>},
members of std::__1::__libcpp_compressed_pair_imp<unsigned int*, std::__1::default_delete<unsigned int []>, 2>:
__first_ = 0x0
}, <No data fields>}
}
}
0x1354a5e0就是前面講的View對象,看來這一層也是對的。
接下來繼續分析下一級函數:
static void InvokeWithArgArray(const ScopedObjectAccessAlreadyRunnable& soa, ArtMethod* method, ArgArray* arg_array, JValue* result, const char* shorty) SHARED_REQUIRES(Locks::mutator_lock_) { uint32_t* args = arg_array->GetArray(); if (UNLIKELY(soa.Env()->check_jni)) { CheckMethodArguments(soa.Vm(), method->GetInterfaceMethodIfProxy(sizeof(void*)), args); } method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, shorty); }
這一級邏輯較少直接跳過,接着看下一級函數:
void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result, const char* shorty) { ManagedStack fragment; self->PushManagedStackFragment(&fragment); Runtime* runtime = Runtime::Current(); ... if (UNLIKELY(!runtime->IsStarted() || Dbg::IsForcedInterpreterNeededForCalling(self, this))) { ... } else { ... if (LIKELY(have_quick_code)) { ... if (!IsStatic()) { (*art_quick_invoke_stub)(this, args, args_size, self, result, shorty); } ... } ... } ... self->PopManagedStackFragment(fragment); }
這一層雖然內容比較多,但是主要的參數都沒變化,直接看下一級函數,並字節列出棧推導的結果:
Dump of assembler code for function art_quick_invoke_stub: 0x0000007f9b44e9f0 <+0>: mov x9, sp ; x9 = sp = 0x7feddaa100 0x0000007f9b44e9f4 <+4>: add x10, x2, #0x80 ; x10 = x2 + 0x80 = 0xc + 0x80 = 0x8c 0x0000007f9b44e9f8 <+8>: sub x10, sp, x10 ; x10 = 0x7feddaa100 - 0x8c = 0x7feddaa074 0x0000007f9b44e9fc <+12>: and x10, x10, #0xfffffffffffffff0 ; x10 = 0x7feddaa074 & 0xfffffffffffffff0 = 0x7feddaa070 0x0000007f9b44ea00 <+16>: mov sp, x10 ; sp = x10 = 0x7feddaa070 0x0000007f9b44ea04 <+20>: sub x10, x9, #0x78 ; x10 = 0x7feddaa100 - 0x78 0x0000007f9b44ea08 <+24>: str x28, [x10,#112] 0x0000007f9b44ea0c <+28>: stp x26, x27, [x10,#96] 0x0000007f9b44ea10 <+32>: stp x24, x25, [x10,#80] 0x0000007f9b44ea14 <+36>: stp x22, x23, [x10,#64] 0x0000007f9b44ea18 <+40>: stp x20, x21, [x10,#48]0x0000000013536dd8 0x0000007f9b44ea1c <+44>: stp x9, x19, [x10,#32] 0x0000007f9b44ea20 <+48>: stp x4, x5, [x10,#16] 0x0000007f9b44ea24 <+52>: stp x29, x30, [x10] 0x0000007f9b44ea28 <+56>: mov x29, x10 0x0000007f9b44ea2c <+60>: mov x19, x3 0x0000007f9b44ea30 <+64>: add x9, sp, #0x8 ; x9 = 0x7feddaa070 + 0x8 = 0x7feddaa078 0x0000007f9b44ea34 <+68>: cmp w2, #0x0 0x0000007f9b44ea38 <+72>: b.eq 0x7f9b44ea4c <art_quick_invoke_stub+92> 0x0000007f9b44ea3c <+76>: sub w2, w2, #0x4 0x0000007f9b44ea40 <+80>: ldr w10, [x1,x2] 0x0000007f9b44ea44 <+84>: str w10, [x9,x2] 0x0000007f9b44ea48 <+88>: b 0x7f9b44ea34 <art_quick_invoke_stub+68> 0x0000007f9b44ea4c <+92>: str xzr, [sp] 0x0000007f9b44ea50 <+96>: adr x11, 0x7f9b44eae0 <art_quick_invoke_stub+240> 0x0000007f9b44ea54 <+100>: adr x12, 0x7f9b44eb28 <art_quick_invoke_stub+312> 0x0000007f9b44ea58 <+104>: adr x13, 0x7f9b44eb70 <art_quick_invoke_stub+384> 0x0000007f9b44ea5c <+108>: adr x14, 0x7f9b44ebd0 <art_quick_invoke_stub+480> 0x0000007f9b44ea60 <+112>: mov x8, #0x0 // #0 0x0000007f9b44ea64 <+116>: mov x15, #0x0 // #0 0x0000007f9b44ea68 <+120>: add x10, x5, #0x1 0x0000007f9b44ea6c <+124>: ldr w1, [x9],#4 ; w1 = [0x7feddaa078] = 0x1354a5e0 android.view.View 0x0000007f9b44ea70 <+128>: ldrb w17, [x10],#1 ... 0x0000007f9b44ec30 <+576>: ldr x9, [x0,#48] ; x9 = [0x000000007144b248+48] = 0x0000000075f56824 0x0000007f9b44ec34 <+580>: blr x9 ; => 0x0000007f9b44ec38 <+584>: ldp x4, x5, [x29,#16]
這個函數中x0就是前面的是android.view.View.<init> "(Landroid/content/Context;Landroid/util/AttributeSet;)V"的ArtMethod指針,
而跳轉語句就是跳到它的native code中,且傳入的this指針是w1,這里能確定是0x1354a5e0,就是android.view.View的Object。
現在可以確定,轉入android.view.View.<init> "(Landroid/content/Context;Landroid/util/AttributeSet;)V"的this指針是正確的。
再看下一級:
(gdb) disassemble 0x0000000075f56824,+0x80 Dump of assembler code from 0x75f56824 to 0x75f568a4: 0x0000000075f56824: stp x0, x1, [sp,#-16]! 0x0000000075f56828: adr x0, 0x75f56838 0x0000000075f5682c: ldr x0, [x0] 0x0000000075f56830: ldr x1, [x0] 0x0000000075f56834: br x1 0x0000000075f56838: adrp x0, 0x2ed46000 0x0000000075f5683c: .inst 0x0000007f ; undefined 0x0000000075f56840: ldp x0, x1, [sp],#16 0x0000000075f56844: mov x21, x2 0x0000000075f56848: mov x22, x3 0x0000000075f5684c: mov w4, #0x0 // #0 0x0000000075f56850: ldr x0, 0x75f56890 0x0000000075f56854: ldr x30, [x0,#48] 0x0000000075f56858: blr x30 0x0000000075f5685c: dmb ishst 0x0000000075f56860: ldp x20, x21, [sp,#48] 0x0000000075f56864: ldp x22, x30, [sp,#64] 0x0000000075f56868: add sp, sp, #0x50 0x0000000075f5686c: ret 0x0000000075f56870: stp x1, x2, [sp,#24] 0x0000000075f56874: str x3, [sp,#40] 0x0000000075f56878: ldr x30, [x19,#1232] 0x0000000075f5687c: blr x30 0x0000000075f56880: ldp x1, x2, [sp,#24] 0x0000000075f56884: ldr x3, [sp,#40] 0x0000000075f56888: b 0x75f56840 0x0000000075f5688c: ldr xzr, 0x75f56898
現在能確定是Hook框架的問題了,Hook前傳入的Object是正確值,但Hook后卻變成了一個異常值。
另外,在本地復現過程中發現每次FC時,都有線程在做GC。
結合這兩點反饋給HooK模塊負責人,很快就定位到了問題點。
原來,Hook框架在返回到原函數時,如果有checkpoint請求(GC是其中一種)就會有問題。
相關代碼如下:
android.view.View.<init> "(Landroid/content/Context;Landroid/util/AttributeSet;)V"對應的MethodItem:
(gdb) p *('miui::art::MethodItem'*)0x0000007f90dc6f80 $38 = { handleHookerFunc = 0x7f7f4e3148 <call_hooker_func>, handleOriginalFunc = 0x7f7f4e3398 <call_original_func>, original_entry = 0x7f9a5574c0, ... }
Hook的入口函數是call_hooker_func,而退出函數是original_entry。
先看看Hook是怎么跳轉到call_hooker_func的:
(gdb) disassemble 0x0000000075f56824,+0x80 Dump of assembler code from 0x75f56824 to 0x75f568a4: 0x0000000075f56824: stp x0, x1, [sp,#-16]! ; 保存x0,x1到棧里,這里面x0就是ArtMethod,x1就是View的Object 0x0000000075f56828: adr x0, 0x75f56838 ; x0 = 0x75f56838 0x0000000075f5682c: ldr x0, [x0] ; x0 = [0x75f56838] = 0x7f90dc6f80,這里保存的是入口函數的地址 0x0000000075f56830: ldr x1, [x0] ; x1 = [0x7f90dc6f80] = 0x7f7f4e3148,這個就是hook的入口函數call_hooker_func 0x0000000075f56834: br x1 ; 跳轉到call_hooker_func 0x0000000075f56838: 0x90dc6f80 ; 前面跳轉用的數據低8字節 0x0000000075f5683c: 0x0000007f ; 前面跳轉用的數據高8字節 0x0000000075f56840: ldp x0, x1, [sp],#16 ; 這里是最終要跳回來的地方,從棧里還原x0,x1 0x0000000075f56844: mov x21, x2 0x0000000075f56848: mov x22, x3 0x0000000075f5684c: mov w4, #0x0
Hook只是在截獲入口,處理自己的一些邏輯,最后還是要執行原先的代碼。
但Hook的跳轉代碼已經把原有的邏輯修改掉了,如下面紅色部分:
9 void android.view.View.<init>(android.content.Context, android.util.AttributeSet) (dex_method_idx=12409)
0x0435582c: f81b0fe0 str x0, [sp, #-80]!
0x04355830: a90357f4 stp x20, x21, [sp, #48]
0x04355834: a9047bf6 stp x22, lr, [sp, #64]
0x04355838: 79400270 ldrh w16, [tr] ; state_and_flags
0x0435583c: 350001b0 cbnz w16, #+0x34 (addr 0x4355870)
0x04355840: aa0103f4 mov x20, x1
0x04355844: aa0203f5 mov x21, x2
0x04355848: aa0303f6 mov x22, x3
0x0435584c: 52800004 mov w4, #0x0
那么在跳轉回0x75f56840前必須先執行被覆蓋的內容,這個是就是在original_entry里做的(gdb) disassemble 0x7f9a5574c0,+0xDump of assembler code from 0x7f9a5574c0 to 0x7f9a5576c0:
0x0000007f9a5574c0: sub x16, sp, #0x2, lsl #12 0x0000007f9a5574c4: ldr wzr, [x16] 0x0000007f9a5574c8: str x0, [sp,#-80]! 0x0000007f9a5574cc: stp x20, x21, [sp,#48] 0x0000007f9a5574d0: stp x22, x30, [sp,#64] 0x0000007f9a5574d4: ldrh w16, [x19] 0x0000007f9a5574d8: cbnz w16, 0x7f9a5574f8 0x0000007f9a5574dc: mov x20, x1 0x0000007f9a5574e0: stp x0, x1, [sp,#-16]! 0x0000007f9a5574e4: adr x0, 0x7f9a5574f0 0x0000007f9a5574e8: ldr x0, [x0] 0x0000007f9a5574ec: br x0
0x0000007f9a5574f0: .inst 0x75f56840 ; undefined
0x0000007f9a5574f4: .inst 0x00000000 ; undefined
0x0000007f9a5574f8: adr x30, 0x7f9a557504
0x0000007f9a5574fc: ldr x30, [x30]
0x0000007f9a557500: br x30
執行完原先的邏輯后,將x0,x1壓入棧中,然后再跳轉到0x7f9a5574f0,
並且在0x7f9a5574f0又從棧里取出x0,x1后繼續走原先的代碼,這樣Hook就完成了。
如果代碼都是位置無關的,這么處理是沒問題的。
但是如果代碼是位置相關的,比如cbnz這種相對跳轉指令,如果還是調用原先的相對跳轉指令,那可能程序就會跑到original_entry相關代碼里。
而程序原本是跳轉到View.<init>相對地址的,這樣程序就會出錯。
所以這里做了針對這種位置相關的指令做了特殊操作:
一旦原先的指令是相對跳轉指令如cbnz,就跳轉到一段特殊的code中,如上面的藍色部分。
這段code的前8個字節是原來代碼里要跳轉過去的目標地址的絕對地址。
后面三個指令就是跳轉指令了。
這種處理確實解決了位置相關代碼的跳轉問題,
但有一個問題,就是如果cbnz條件滿足了,那將會直接跳過stp x0, x1, [sp,#-16]!這個壓寄存器的指令,
而我們這里cbnz跳轉到pTestSuspend代碼塊,走完相關邏輯還要跳轉回來的。
這樣,在有suspend請求時,沒執行stp x0, x1, [sp,#-16]!的情況下,處理完suspend請求后跳回0x7f9a5574e0。
0x0000007f9a5574c0: sub x16, sp, #0x2, lsl #12
0x0000007f9a5574c4: ldr wzr, [x16]
0x0000007f9a5574c8: str x0, [sp,#-80]!
0x0000007f9a5574cc: stp x20, x21, [sp,#48]
0x0000007f9a5574d0: stp x22, x30, [sp,#64]
0x0000007f9a5574d4: ldrh w16, [x19]
0x0000007f9a5574d8: cbnz w16, 0x7f9a5574f8
0x0000007f9a5574dc: mov x20, x1
0x0000007f9a5574e0: stp x0, x1, [sp,#-16]!
0x0000007f9a5574e4: adr x0, 0x7f9a5574f0
由於此時棧里的數據是不確定的數據,所以x0,x1的值也是異常數據。
但下一條指令中x0又會重新賦值,所以只有x1值是異常值。
到此問題也清楚了,由於original_entry也是動態生成的,所以這里不太好改。
最終解決方案是修改Hook方案,這里就不多講了。