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方案,這里就不多講了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM