boot.oat FC问题分析报告

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

原文地址:https://www.cnblogs.com/YYPapa/p/6847583.html