【NE现场】

pid: 5252, tid: 5252, name: ndroid.contacts  >>> com.android.contacts <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x1458x0   0000000000000000  x1   0000000090d9892c  x2   0000000000000001  x3   000000000000012cx4   0000000000000000  x5   000000000000012c  x6   0000000000000000  x7   0000000000000003x8   0000000000000000  x9   0000007feddaa618  x10  0000007fedda7f78  x11  0000000000000008x12  0000007f9fdc5088  x13  0000000000200020  x14  0000000000000001  x15  0b9f4a1e359c32dex16  0000000000000000  x17  0000000000200020  x18  0000000000000010  x19  0000007f9ba96a00x20  0000000080000000  x21  0000000013500ca0  x22  00000000ffffffff  x23  0000000000000000x24  000000007144b210  x25  000000001329d880  x26  0000000090d9892c  x27  0000000000000018x28  0000000000000000  x29  0000007fedda85e8  x30  0000000075f56664sp   0000007fedda8210  pc   0000000075f56678  pstate 0000000060000000backtrace:#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, x260x0000000075f5666c:  mov x27, x00x0000000075f56670:  mov w2, #0x1                    // #10x0000000075f56674:    ldr w0, [x1]
=> 0x0000000075f56678:  ldr x0, [x0,#5208]0x0000000075f5667c:    ldr x30, [x0,#48]0x0000000075f56680:    blr x300x0000000075f56684:  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.art00000000'71c00000-00000000'74aeafff r--         0   2eeb000  /data/dalvik-cache/arm64/system@framework@boot.oat00000000'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.oat00000000'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_flags0x04355498: 35001910  cbnz w16, #+0x320 (addr 0x43557b8)0x0435549c: aa0003f8  mov x24, x00x043554a0: aa0203f9  mov x25, x20x043554a4: 52800017  mov w23, #0x00x043554a8: 12800016  mov w22, #0xffffffff0x043554ac: 52800015  mov w21, #0x00x043554b0: 52b00014  mov w20, #0x800000000x043554b4: 1c001960  ldr s0, pc+812 (addr 0x43557e0) (nan)0x043554b8: aa0103fa  mov x26, x10x043554bc: 58001940  ldr x0, pc+808 (addr 0x43557e4) (0x715dd520 / 1901974816)0x043554c0: f940181e  ldr lr, [x0, #48]0x043554c4: d63f03c0  blr lr0x043554c8: b9003355  str w21, [x26, #48]0x043554cc: 39069357  strb w23, [x26, #420]0x043554d0: b900f356  str w22, [x26, #240]...0x04355650: aa0103fb  mov x27, x10x04355654: b9400020  ldr w0, [x1]0x04355658: f940a400  ldr x0, [x0, #328]0x0435565c: f940181e  ldr lr, [x0, #48]0x04355660: d63f03c0  blr lr0x04355664: b9016340  str w0, [x26, #352]0x04355668: aa1a03e1  mov x1, x260x0435566c: aa0003fb  mov x27, x00x04355670: 52800022  mov w2, #0x10x04355674: 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_flags0x0435599c: 3502aef0  cbnz w16, #+0x55dc (addr 0x435af78)0x043559a0: aa0003f4  mov x20, x00x043559a4: aa0303f5  mov x21, x30x043559a8: aa0403f6  mov x22, x40x043559ac: aa0503f7  mov x23, x50x043559b0: aa0103f8  mov x24, x10x043559b4: aa0203f9  mov x25, x20x043559b8: 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, 0x75f569680x0000000075f5695c:   ldr x0, [x0]0x0000000075f56960: ldr x1, [x0]0x0000000075f56964: br  x10x0000000075f56968:   adrp    x0, 0x2ed260000x0000000075f5696c:   .inst   0x0000007f ; undefined0x0000000075f56970:   ldp x0, x1, [sp],#160x0000000075f56974:  add x16, sp, #0x2180x0000000075f56978:  stp x26, x27, [x16]0x0000000075f5697c:  add x16, sp, #0x2280x0000000075f56980:  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, 0x75f5bf780x0000000075f569a0:  mov x20, x00x0000000075f569a4:  mov x21, x30x0000000075f569a8:  mov x22, x40x0000000075f569ac:  mov x23, x50x0000000075f569b0:  mov x24, x10x0000000075f569b4:  mov x25, x20x0000000075f569b8:  ldr x0, 0x75f5c19c0x0000000075f569bc:   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和x10x0000000075f56958:  adr x0, 0x75f56968          # x0 = 0x75f569680x0000000075f5695c:   ldr x0, [x0]                 # x0 = [0x75f56968] = 0x0000007f90dc6e800x0000000075f56960:    ldr x1, [x0]                # x1 = [0x0000007f90dc6e80] = 0x0000007f7f4e31480x0000000075f56964:    br  x1                       # 跳转到0x7f7f4e31480x0000000075f56968:   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.art00000000'71c00000-00000000'74aeafff r--         0   2eeb000  /data/dalvik-cache/arm64/system@framework@boot.oat00000000'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.oat00000000'75397000-00000000'75447fff r-x   3797000     b1000  /data/dalvik-cache/arm64/system@framework@boot.oat00000000'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.oat00000000'76354000-00000000'76810fff r-x   4754000    4bd000  /data/dalvik-cache/arm64/system@framework@boot.oat00000000'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:   0x00000000716193280x0000000071275c18
0x7feddaaa00:   0x000000001356cc00  0xffffffff133ee9d0
0x7feddaaa10:   0x1329d880133ee9d0  0x1329d88000000000
0x7feddaaa20:   0x0000007feddaaa50  0x0000007f9b91e878
0x7feddaaa30:   0x00000000133ee9d0  0x00000000133f0330
0x7feddaaa40:   0x0000007f9b91e858  0x0000007f9ba96a00
0x7feddaaa50:   0x0000000071275b30  0x00000000757395dc
0x7feddaaa60:   0x000000007165ef680x0000007feddae178
0x7feddaaa70:   0x70fabf2000000001  0x0000000600000007
0x7feddaaa80:   0x0000000000000000  0x00000000135279a0
0x7feddaaa90:   0x000000001356cc00  0x00000000133ee9d0
0x7feddaaaa0:   0x000000001329d880  0x00000000133f02e0
0x7feddaaab0:   0x0000000000000001  0x0000000000000002
0x7feddaaac0:   0x0000000071275d70  0x000000007104d2f0
0x7feddaaad0:   0x0000000071275b30  0x0000000075fe113c
0x7feddaaae0:   0x00000000716191300x1356cc00135279a0
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.java751    privateView createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {752        return createViewFromTag(parent, name, context, attrs, false);753}770View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,771            booleanignoreThemeAttr) {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 = newContextThemeWrapper(context, themeResId);782}783ta.recycle();784}785
786        if(name.equals(TAG_1995)) {787            //Let's party like it's 1995!
788            return newBlinkLayout(context, attrs);789}790
791        try{792View 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    0x0000000075fe4f2c0x7feddaa990:    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 #120x0000000075fe4ee8:   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, 0x75fe4f400x0000000075fe4f04:  mov x20, x20x0000000075fe4f08:  mov x2, x30x0000000075fe4f0c:   mov x3, x40x0000000075fe4f10:   mov x21, x10x0000000075fe4f14:  mov x22, x20x0000000075fe4f18:  mov x23, x3
0x0000000075fe4f1c: ldr w0, [x1]          # 获取this的class 0x0000000075fe4f20:  ldr x0, [x0,#328]     # 从class中取某个成员函数的ArtMethod0x0000000075fe4f24: 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 #120x0000000075fe3928:   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, x210x0000000075fe3ce0:   mov x2, x220x0000000075fe3ce4:  mov x4, x270x0000000075fe3ce8:  mov x3, x200x0000000075fe3cec:  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 #120x0000000075fe4ee8:   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 #120x0000000075fe4ee8:   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, 0x75fe4f400x0000000075fe4f04:  mov x20, x20x0000000075fe4f08:  mov x2, x30x0000000075fe4f0c:   mov x3, x40x0000000075fe4f10:   mov x21, x10x0000000075fe4f14:  mov x22, x20x0000000075fe4f18:  mov x23, x30x0000000075fe4f1c:  ldr w0, [x1]          # w0 = [0x135279a0] = 0x70db9428  0x0000000075fe4f20: ldr x0, [x0,#328]     # x0 = [0x70db9428+328] = 0x71636c080x0000000075fe4f24:   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 #120x0000000076360578:   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, 0x763606a40x00000000763605a8:  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 #120x0000000076360558:    .inst   0x00000000 ; undefined0x000000007636055c:   .inst   0x00000000 ; undefined0x0000000076360560:   .inst   0x01a982ff ; undefined0x0000000076360564:   .inst   0x000000b0 ; undefined0x0000000076360568:   .inst   0x4ff00000 ; undefined0x000000007636056c:   .inst   0x00000000 ; undefined0x0000000076360570:   .inst   0x00000178 ; undefined0x0000000076360574:   sub x16, sp, #0x2, lsl #120x0000000076360578:   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, 0x763606a40x00000000763605a8:  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, x210x00000000763605c0: mov x21, x00x00000000763605c4:  mov x4, x30x00000000763605c8:   mov x22, x200x00000000763605cc: mov w20, #0x0                       // #00x00000000763605d0:    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  0x00000000763606040x7feddaa8e0:  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  0x0000000075fe4f2c0x7feddaa990:  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    0x0000000074bb6160java.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    0x0000000074bb70d0java.lang.Object java.lang.reflect.Constructor.newInstance(java.lang.Object[])
0x7feddaa770:    0x00000000715de3a0    0x13536dd8131a5be0
0x7feddaa780:    0x000000007136e0c0    0x70ea322070ddab10
0x7feddaa790:    0x00000000135279a0    0x00000000135279a0
0x7feddaa7a0:    0x0000000013536dd8    0x00000000133f02e0
0x7feddaa7b0:    0x0000000070db3ba0    0x0000000075fe2864android.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    0x0000000076360604android.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    0x0000000075fe4f2candroid.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, #0x500x0000007f9b75055c <+28>:   sub sp, sp, #0xc00x0000007f9b750560 <+32>:    adrp    x25, 0x7f9b9f8000 <_ZTVN3art32BuildNativeCallFrameStateMachineINS_27BuildGenericJniFrameVisitor11FillJniCallEEE>0x0000007f9b750564 <+36>:   mov x19, x20x0000007f9b750568 <+40>:  mov x20, x10x0000007f9b75056c <+44>:  ldr x25, [x25,#1376]0x0000007f9b750570 <+48>: ldr x25, [x25]...0x0000007f9b750a04 <+1220>:  mov x1, x260x0000007f9b750a08 <+1224>:    bl  0x7f9b4a06dc <art::mirror::Class::AllocObject(art::Thread*)>0x0000007f9b750a0c <+1228>: mov x8, x00x0000007f9b750a10 <+1232>: cbz x8, 0x7f9b750a70 <art::Constructor_newInstance0(_JNIEnv*, _jobject*, _jobjectArray*)+1328>0x0000007f9b750a14 <+1236>:   ldr x16, [sp,#128]0x0000007f9b750a18 <+1240>: mov x2, x80x0000007f9b750a1c <+1244>: ldr w1, [x16,#24]0x0000007f9b750a20 <+1248>:  add x0, x16, #0x200x0000007f9b750a24 <+1252>: bl  0x7f9b5f0204 <art::IndirectReferenceTable::Add(unsigned int, art::mirror::Object*)>0x0000007f9b750a28 <+1256>:  mov x21, x00x0000007f9b750a2c <+1260>:    add x0, sp, #0x780x0000007f9b750a30 <+1264>:  orr w4, wzr, #0x20x0000007f9b750a34 <+1268>:  mov x1, x200x0000007f9b750a38 <+1272>:    mov x2, x210x0000007f9b750a3c <+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) {returnnullptr;}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.returnjavaReceiver;
}

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.hinline T JNIEnvExt::AddLocalReference(mirror::Object*obj) {IndirectRefref =locals.Add(local_ref_cookie, obj);return reinterpret_cast<T>(ref);
}

其中locals是JNIEnvExt的成员:

@art/runtime/jni_env_ext.hstruct JNIEnvExt : publicJNIEnv {...//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);returnresult;
}

接下来需要通过栈来推导这个值:

首先列出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, #0x500x0000007f9b75055c <+28>:   sub sp, sp, #0xc0...0x0000007f9b750a04 <+1220>:   mov x1, x260x0000007f9b750a08 <+1224>:    bl  0x7f9b4a06dc <art::mirror::Class::AllocObject(art::Thread*)>0x0000007f9b750a0c <+1228>: mov x8, x00x0000007f9b750a10 <+1232>: cbz x8, 0x7f9b750a70 <art::Constructor_newInstance0(_JNIEnv*, _jobject*, _jobjectArray*)+1328>0x0000007f9b750a14 <+1236>:   ldr x16, [sp,#128]  ; x16 = [0x7feddaa580+128] = [0x7feddaa600] = 0x7f9ba3e3000x0000007f9b750a18 <+1240>: mov x2, x80x0000007f9b750a1c <+1244>: ldr w1, [x16,#24]0x0000007f9b750a20 <+1248>:  add x0, x16, #0x20  ; x0 = x16 + 0x20 = 0x7f9ba3e3200x0000007f9b750a24 <+1252>:   bl  0x7f9b5f0204 <art::IndirectReferenceTable::Add(unsigned int, art::mirror::Object*)>0x0000007f9b750a28 <+1256>:  mov x21, x00x0000007f9b750a2c <+1260>:    add x0, sp, #0x780x0000007f9b750a30 <+1264>:  orr w4, wzr, #0x20x0000007f9b750a34 <+1268>:  mov x1, x200x0000007f9b750a38 <+1272>:    mov x2, x210x0000007f9b750a3c <+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: 0x70eac5e00x00000000   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) {returnnullptr;}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.returnjavaReceiver;
}

再看看其他参数:

   0x0000007f9b750a2c <+1260>:   add x0, sp, #0x780x0000007f9b750a30 <+1264>:  orr w4, wzr, #0x20x0000007f9b750a34 <+1268>:  mov x1, x200x0000007f9b750a38 <+1272>:    mov x2, x210x0000007f9b750a3c <+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, #0x500x0000007f9b7f5490 <+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  0x0000007feddaa624x28           x27
0x7feddaa530:   0x0000007f9ba96a00  0x0b9f4a1e359c32dex26           x25
0x7feddaa540:   0x00000000716192f0  0x00000000131a5be0x24           x23
0x7feddaa550:   0x0000007f9ba96a00  0x000000000000001dx22           x21
0x7feddaa560:   0x0000007feddaa6b4 0x0000007feddaa6b8x20            x19
0x7feddaa570:   0x0000007feddaa690  0x0000007f9b750a44x29           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, #0xc00x0000007f9b7f5920 <+1196>:  add x3, sp, #0xb80x0000007f9b7f5924 <+1200>:  mov x0, x190x0000007f9b7f5928 <+1204>:    mov x1, x200x0000007f9b7f592c <+1208>:    mov x4, x250x0000007f9b7f5930 <+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 = 0x7feddaa1000x0000007f9b44e9f4 <+4>:    add    x10, x2, #0x80                   ; x10 = x2 + 0x80 = 0xc + 0x80 = 0x8c0x0000007f9b44e9f8 <+8>:    sub    x10, sp, x10                      ; x10 = 0x7feddaa100 - 0x8c = 0x7feddaa0740x0000007f9b44e9fc <+12>:    and    x10, x10, #0xfffffffffffffff0    ; x10 = 0x7feddaa074 & 0xfffffffffffffff0 = 0x7feddaa0700x0000007f9b44ea00 <+16>:    mov    sp, x10                          ; sp = x10 = 0x7feddaa0700x0000007f9b44ea04 <+20>:    sub    x10, x9, #0x78                   ; x10 = 0x7feddaa100 - 0x780x0000007f9b44ea08 <+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]0x0000000013536dd80x0000007f9b44ea1c <+44>:    stp    x9, x19, [x10,#32]0x0000007f9b44ea20 <+48>:    stp    x4, x5, [x10,#16]0x0000007f9b44ea24 <+52>:    stp    x29, x30, [x10]0x0000007f9b44ea28 <+56>:    mov    x29, x100x0000007f9b44ea2c <+60>:    mov    x19, x30x0000007f9b44ea30 <+64>:    add    x9, sp, #0x8                    ; x9 = 0x7feddaa070 + 0x8 = 0x7feddaa0780x0000007f9b44ea34 <+68>:    cmp    w2, #0x00x0000007f9b44ea38 <+72>:    b.eq    0x7f9b44ea4c <art_quick_invoke_stub+92>0x0000007f9b44ea3c <+76>:    sub    w2, w2, #0x40x0000007f9b44ea40 <+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                       // #00x0000007f9b44ea64 <+116>:    mov    x15, #0x0                       // #00x0000007f9b44ea68 <+120>:    add    x10, x5, #0x10x0000007f9b44ea6c <+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] = 0x0000000075f568240x0000007f9b44ec34 <+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, 0x75f568380x0000000075f5682c:   ldr x0, [x0]0x0000000075f56830: ldr x1, [x0]0x0000000075f56834: br  x10x0000000075f56838:   adrp    x0, 0x2ed460000x0000000075f5683c:   .inst   0x0000007f ; undefined
0x0000000075f56840: ldp x0, x1, [sp],#160x0000000075f56844:  mov x21, x20x0000000075f56848:  mov x22, x30x0000000075f5684c:  mov w4, #0x0                    // #00x0000000075f56850:    ldr x0, 0x75f568900x0000000075f56854:   ldr x30, [x0,#48]0x0000000075f56858:    blr x300x0000000075f5685c:  dmb ishst0x0000000075f56860:    ldp x20, x21, [sp,#48]0x0000000075f56864:   ldp x22, x30, [sp,#64]0x0000000075f56868:   add sp, sp, #0x500x0000000075f5686c:    ret0x0000000075f56870:  stp x1, x2, [sp,#24]0x0000000075f56874: str x3, [sp,#40]0x0000000075f56878: ldr x30, [x19,#1232]0x0000000075f5687c: blr x300x0000000075f56880:  ldp x1, x2, [sp,#24]0x0000000075f56884: ldr x3, [sp,#40]0x0000000075f56888: b   0x75f568400x0000000075f5688c:   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的Object0x0000000075f56828:  adr x0, 0x75f56838          ; x0 = 0x75f568380x0000000075f5682c:    ldr x0, [x0]               ; x0 = [0x75f56838] = 0x7f90dc6f80,这里保存的是入口函数的地址0x0000000075f56830:  ldr x1, [x0]               ; x1 = [0x7f90dc6f80] = 0x7f7f4e3148,这个就是hook的入口函数call_hooker_func0x0000000075f56834:   br  x1                     ; 跳转到call_hooker_func0x0000000075f56838:    0x90dc6f80                      ; 前面跳转用的数据低8字节   0x0000000075f5683c:   0x0000007f                    ; 前面跳转用的数据高8字节0x0000000075f56840:   ldp x0, x1, [sp],#16        ; 这里是最终要跳回来的地方,从栈里还原x0,x10x0000000075f56844: mov x21, x20x0000000075f56848:  mov x22, x30x0000000075f5684c:  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_flags0x0435583c: 350001b0  cbnz w16, #+0x34 (addr 0x4355870)0x04355840: aa0103f4  mov x20, x10x04355844: aa0203f5  mov x21, x20x04355848: aa0303f6  mov x22, x30x0435584c: 52800004  mov w4, #0x0

那么在跳转回0x75f56840前必须先执行被覆盖的内容,这个是就是在original_entry里做的(gdb) disassemble 0x7f9a5574c0,+0xDump of assembler code from 0x7f9a5574c0 to 0x7f9a5576c0:

   0x0000007f9a5574c0:   sub x16, sp, #0x2, lsl #120x0000007f9a5574c4:   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, 0x7f9a5574f80x0000007f9a5574dc:    mov x20, x10x0000007f9a5574e0:   stp x0, x1, [sp,#-16]!0x0000007f9a5574e4:   adr x0, 0x7f9a5574f00x0000007f9a5574e8: ldr x0, [x0]0x0000007f9a5574ec: br  x00x0000007f9a5574f0: .inst 0x75f56840 ; undefined0x0000007f9a5574f4: .inst 0x00000000 ; undefined0x0000007f9a5574f8:  adr     x30, 0x7f9a5575040x0000007f9a5574fc:    ldr x30, [x30]0x0000007f9a557500:  br  x30

执行完原先的逻辑后,将x0,x1压入栈中,然后再跳转到0x7f9a5574f0,

并且在0x7f9a5574f0又从栈里取出x0,x1后继续走原先的代码,这样Hook就完成了。

如果代码都是位置无关的,这么处理是没问题的。

但是如果代码是位置相关的,比如cbnz这种相对跳转指令,如果还是调用原先的相对跳转指令,那可能程序就会跑到original_entry相关代码里。

而程序原本是跳转到View.<init>相对地址的,这样程序就会出错。

所以这里做了针对这种位置相关的指令做了特殊操作:

一旦原先的指令是相对跳转指令如cbnz,就跳转到一段特殊的code中,如上面的蓝色部分。

这段code的前8个字节是原来代码里要跳转过去的目标地址的绝对地址。

后面三个指令就是跳转指令了。

这种处理确实解决了位置相关代码的跳转问题,

但有一个问题,就是如果cbnz条件满足了,那将会直接跳过stpx0, x1, [sp,#-16]!这个压寄存器的指令,

而我们这里cbnz跳转到pTestSuspend代码块,走完相关逻辑还要跳转回来的。

这样,在有suspend请求时,没执行stpx0, x1, [sp,#-16]!的情况下,处理完suspend请求后跳回0x7f9a5574e0。

   0x0000007f9a5574c0:   sub x16, sp, #0x2, lsl #120x0000007f9a5574c4:   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, 0x7f9a5574f80x0000007f9a5574dc:    mov x20, x1
0x0000007f9a5574e0: stp x0, x1, [sp,#-16]!0x0000007f9a5574e4:    adr x0, 0x7f9a5574f0

由于此时栈里的数据是不确定的数据,所以x0,x1的值也是异常数据。

但下一条指令中x0又会重新赋值,所以只有x1值是异常值。

到此问题也清楚了,由于original_entry也是动态生成的,所以这里不太好改。

最终解决方案是修改Hook方案,这里就不多讲了。

转载于:https://www.cnblogs.com/YYPapa/p/6847583.html

boot.oat FC问题分析报告相关推荐

  1. boot客户管理系统实训报告_客户太多,客户转化困难?分析报告迟迟出不来,CRM客户管理系统助你一臂之力...

    原标题:客户太多,客户转化困难?分析报告迟迟出不来,CRM客户管理系统助你一臂之力 作者:Teamface 企业中,每天接触的客户太多,时间太长根本记不住谁是谁,难以分清楚是否还是企业的意向客户,很容 ...

  2. 2021-2027全球及中国攀冰专用装备行业研究及十四五规划分析报告

    2021-2027全球及中国攀冰专用装备行业研究及十四五规划分析报告 本报告研究"十三五"期间全球及中国市场攀冰专用装备的供给和需求情况,以及"十四五"期间行业 ...

  3. ART加载OAT文件的分析

    本文对老罗博客http://blog.csdn.net/luoshengyang/article/details/39307813 进行学习理解,针对android6.0系统源码,连个人理解带复制粘贴 ...

  4. Networknt Light 4J分析报告

    Networknt Light 4J分析报告 一.整体介绍 Light 4J是networknt.com的核心产品.一个基于Java SE快速.轻量级并且高效的微服务框架.其包含十多个子模块,用于不同 ...

  5. 眼科疾病可视化分析报告/Pycharm爬虫/数据处理/csv/plt画图

    眼科疾病可视化分析报告 蒋永真 2020.6 一.背景描述 6月6日是全国爱眼日,我国现有约500万盲人,占总人口的0.4%,是世界上盲和视力损伤严重的国家之一,随着经济社会的发展和人口结构的老龄化, ...

  6. 中国环境服务行业十四五规划目标与建设现状分析报告2022-2028年

    中国环境服务行业十四五规划目标与建设现状分析报告2022-2028年 <修订日期>:2022年2月 <出版单位>:鸿晟信合研究院 <对接人员>:周文文 [内容分析有 ...

  7. 最新的全球编程语言,操作系统,web服务器等使用率分析报告

    由www.w3techs.com 根据alexa排名前100万的网站数据给出的分析报告,并每天持续更新. 1.服务器端编程语言排名 http://w3techs.com/technologies/ov ...

  8. oracle如何自动分析报告,ORACLE 性能分析报告的获取

    ORACLE 性能分析报告的获取: 我们都知道,在oracle数据库当中有性能分析报告的设计,如ASH,AWR,ADDM,AWRDD,AWRSQRPT,这些报告就像我们去医院的检查报告一样,我们在这个 ...

  9. 用哪种语言写的应用漏洞最严重?六大主流语言代码漏洞分析报告出炉

    来源:机器之心 本文约1600字,建议阅读5分钟 静态代码分析安全公司 Veracode 近日发布了一份应用程序分析报告,结果发现比起 JavaScript 和 Python 等语言,C++ 和 PH ...

最新文章

  1. 3月编程语言排行已出!Java第二,那霸主是谁?
  2. 操作系统:小和尚打水+老和尚喝水经典同步问题实现 菜鸟的解题全过程(附具体代码)
  3. Map.putAll()用法
  4. 认识实时动态测量技术
  5. MySQL—表的完整性约束(外键约束)(二)
  6. 怎么查到mysql的账号密码是什么_怎么查到mysql的账号密码是什么?
  7. Sring3MVC页面无刷新上传文件
  8. swift4.1 系统学习十 函数
  9. java多线程——CAS
  10. ActiveMQ 下载和安装
  11. xlsx的python处理
  12. EA量化交易是什么?外汇EA量化交易可靠吗?
  13. PHP读取word文档 导入word文档
  14. 《关于长沙.NET技术社区未来发展规划》问卷调查结果公布
  15. java无响应_Java HttpClient请求无响应解决方案
  16. 多平台epub阅读器分享
  17. 微信二维码图片长按没有出现“识别图中的二维码”
  18. cocos2d-x 禁用触摸
  19. Unknown column 'xxx' in 'field list'
  20. 测试用例颗粒度实例列举

热门文章

  1. 团队行为心理学读书笔记(3)领导力背后的行为心理学
  2. 深度 | 一篇文章带你进入无监督学习:从基本概念到四种实现模型(附论文)
  3. 关于RGBDSLAMV2学习、安装、调试过程
  4. DDG全家桶之3022
  5. 验证Oracle收集统计信息参数granularity数据分析的力度
  6. Linux下git的使用——将已有项目放到github上
  7. 2016年11月13日周工作知识点总结
  8. 巧用VC工程下的rc文件
  9. vue3中websocket用法
  10. 如何解决浏览器缩小出现横向滚动条时网页背景图出现空白的问题