【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方案,这里就不多讲了。