1. 程式人生 > >用GDB推導DVM的Java棧

用GDB推導DVM的Java棧

get 通過 size 表示 reg b2c avi 函數 ssi

用GDB的bt命令很容易就能打印native的調用棧,如:

(gdb) bt
#0  tgkill () at bionic/libc/arch-arm/bionic/tgkill.S:46
#1  0x40061030 in pthread_kill (t=<optimized out>, sig=6) at bionic/libc/bionic/pthread_kill.cpp:49
#2  0x40061244 in raise (sig=6) at bionic/libc/bionic/raise.cpp:32
#3  0x4005ff9e in __libc_android_abort () at bionic/libc/bionic/abort.cpp:65
#4  0x4006f850 in abort () at bionic/libc/arch-arm/bionic/abort_arm.S:41
#5  0x7217b50c in DebugBreak () at external/chromium_org/base/debug/debugger_posix.cc:233
#6  base::debug::BreakDebugger () at external/chromium_org/base/debug/debugger_posix.cc:257
#7  0x7217910e in base::android::CheckException ([email protected]
/* */=0x414cefa8) at external/chromium_org/base/android/jni_android.cc:204 #8 0x72b2d0dc in Java_ContentViewCore_setTitle (title=0xbe500021, obj=0x240001d, env=0x414cefa8) at out/target/product/pisces/obj/GYP/shared_intermediates/content/jni/ContentViewCore_jni.h:1282 #9 content::ContentViewCoreImpl::SetTitle (this=<optimized out>, title=...) at external/chromium_org/content/browser/android/content_view_core_impl.cc:437 #10 0x72b89dfc in content::WebContentsImpl::UpdateTitleForEntry ([email protected]
/* */=0x76b22280, [email protected]=0x7a0cf7b0, title=...) at external/chromium_org/content/browser/web_contents/web_contents_impl.cc:2717 ...

有時候我們想知道Native Crash時的java調用棧,這時候我們可以用gDvm中的數據來推導java棧。

我們知道gDvm中有一個threadList,它是一個線程鏈表,可以通過這個鏈表遍歷當前進程中的所有線程。

(gdb) p gDvm->threadList
$1 = (Thread *) 0x414d0558

(gdb) p * (Thread *) 0x414d0558
$3 = {
  ...
  threadId = 1,
  ...
  status = THREAD_NATIVE,
  systemTid = 23405,
  interpStackStart = 0x6d557000 "",
  threadObj = 0x416b2ca8,
  jniEnv = 0x414cefa8,
  prev = 0x0,
  next = 0x7b88a3a8,
... } (gdb) p * (Thread *) 0x7b88a3a8 $4 = { ... threadId = 26, ... status = THREAD_NATIVE, systemTid = 25905, interpStackStart = 0x77ead000 <Address 0x77ead000 out of bounds>, threadObj = 0x42dacd70, jniEnv = 0x7a0cff28, prev = 0x414d0558, next = 0x7a095de0, ... }

...

用info thread命令可以看到,出問題的線程是23405線程,也就是主線程。

(gdb) info thread
  Id   Target Id         Frame
  54   LWP 23412         recvmsg () at bionic/libc/arch-arm/syscalls/recvmsg.S:9
  53   LWP 23451         __futex_syscall3 () at bionic/libc/arch-arm/bionic/futex_arm.S:39
  ...
  5    LWP 23460         __futex_syscall3 () at bionic/libc/arch-arm/bionic/futex_arm.S:39
  4    LWP 23418         __ioctl () at bionic/libc/arch-arm/syscalls/__ioctl.S:9
  3    LWP 25905         __ioctl () at bionic/libc/arch-arm/syscalls/__ioctl.S:9
  2    LWP 23417         __ioctl () at bionic/libc/arch-arm/syscalls/__ioctl.S:9
* 1    LWP 23405         tgkill () at bionic/libc/arch-arm/bionic/tgkill.S:46

接下來就開始推導主線程的調用棧:

(gdb) p *(Thread*)0x414d0558
$3 = {
  interpSave = {
    pc = 0x6e601544,
    curFrame = 0x6d556e20,
    ...
  },
  threadId = 1,
  ...
  status = THREAD_NATIVE,
  systemTid = 23405,
  interpStackStart = 0x6d557000 "",
  threadObj = 0x416b2ca8,
  jniEnv = 0x414cefa8,
  ...
  prev = 0x0,
  next = 0x7b88a3a8,
  ...
}

從interpSave的curFrame可以推導最頂層的StackSaveArea,由於:

#define SAVEAREA_FROM_FP(_fp)   ((StackSaveArea*)(_fp) -1)

所以最頂層的StackSaveArea為:

(gdb) p *(StackSaveArea*)(0x6d556e20-sizeof(StackSaveArea))
$5 = {
  prevFrame = 0x6d556e40,
  savedPc = 0x7015e73c,
  method = 0x6d7d6068,
  ...
}

取Method:

(gdb) p *(Method*) 0x6d7d6068
$6 = {
  clazz = 0x4187f7b8,
  accessFlags = 258,
  methodIndex = 0,
  registersSize = 3,
  outsSize = 0,
  insSize = 3,
  name = 0x701b6c59 <Address 0x701b6c59 out of bounds>,
  ...
}

取Method對應的ClassObject:

(gdb) p *(ClassObject*) 0x4187f7b8
$7 = {
  ...
  descriptor = 0x7019bc6d <Address 0x7019bc6d out of bounds>,
  ...
}

對照map表,發現是這個descriptor的地址是webviewchromium的dex文件:

7012e000-701ef000 r--p 00000000 b3:1b 40987      [email protected]@[email protected]

descriptor的地址減去dex的起始地址,就得到類名稱字符串在dex中的偏移地址:

(gdb) p /x 0x7019bc6d-0x7012e000
$8 = 0x6dc6d

從手機中pull出來這個dex文件後,用hexdump查看:

$ hexdump -C -n64 -s0x6dc6d [email protected]@[email protected]
0006dc6d  4c 63 6f 6d 2f 61 6e 64  72 6f 69 64 2f 6f 72 67  |Lcom/android/org|
0006dc7d  2f 63 68 72 6f 6d 69 75  6d 2f 62 61 73 65 2f 53  |/chromium/base/S|
0006dc8d  79 73 74 65 6d 4d 65 73  73 61 67 65 48 61 6e 64  |ystemMessageHand|
0006dc9d  6c 65 72 3b 00 2b 4c 63  6f 6d 2f 61 6e 64 72 6f  |ler;.+Lcom/andro|

可知,這個類的名稱是com/android/org/chromium/base/SystemMessageHandler

Method裏有name成員,表示函數名,它的偏移地址為:

(gdb) p *(Method*) 0x6d7d6068
$6 = {
  clazz = 0x4187f7b8,
  accessFlags = 258,
  methodIndex = 0,
  registersSize = 3,
  outsSize = 0,
  insSize = 3,
  name = 0x701b6c59 <Address 0x701b6c59 out of bounds>,
  ...
}

(gdb) p /x 0x701b6c59-0x7012e000
$9 = 0x88c59

同樣用hexdump就能得到這個函數名:

$ hexdump -C -n32 -s0x88c59 [email protected]@[email protected]
00088c59  6e 61 74 69 76 65 44 6f  52 75 6e 4c 6f 6f 70 4f  |nativeDoRunLoopO|
00088c69  6e 63 65 00 17 6e 61 74  69 76 65 44 6f 63 75 6d  |nce..nativeDocum|

最頂層的函數名為nativeDoRunLoopOnce()

再看看次頂層,次頂層的frame保存在頂層StackSaveArea的prevFrame成員裏:

(gdb) p *(StackSaveArea*)(0x6d556e20-sizeof(StackSaveArea))
$5 = {
  prevFrame = 0x6d556e40,
  ...
}

(gdb) p *(StackSaveArea*)(0x6d556e20-sizeof(StackSaveArea))
$5 = {
  prevFrame = 0x6d556e40,
  savedPc = 0x7015e73c,
  method = 0x6d7d6068,
  ...
}

(gdb) p *(StackSaveArea*)(0x6d556e40-sizeof(StackSaveArea))
$12 = {
  prevFrame = 0x6d556e64,
  savedPc = 0x6edd27f0,
  method = 0x6d7d6150,
  ...
}

(gdb) p *(Method*) 0x6d7d6150
$13 = {
  clazz = 0x4187f7b8,
  accessFlags = 1,
  methodIndex = 16,
  registersSize = 4,
  outsSize = 3,
  insSize = 2,
  name = 0x701b091d <Address 0x701b091d out of bounds>,
  ...
}

clazz和頂層的一樣是com/android/org/chromium/base/SystemMessageHandler

method name的偏移地址:

(gdb) p /x 0x701b091d-0x7012e000
$17 = 0x8291d

函數名為handleMessage():

$ hexdump -C -n32 -s0x8291d [email protected]@[email protected]
0008291d  68 61 6e 64 6c 65 4d 65  73 73 61 67 65 00 0e 68  |handleMessage..h|
0008292d  61 6e 64 6c 65 4e 61 76  69 67 61 74 65 00 10 68  |andleNavigate..h|

再推導下一個棧:

(gdb) p *(StackSaveArea*)(0x6d556e64-sizeof(StackSaveArea))
$34 = {
  prevFrame = 0x6d556e84,
  savedPc = 0x6efdd434,
  method = 0x6d5f75a0,
  ...
}

(gdb) p *(Method*)0x6d5f75a0
$35 = {
  clazz = 0x416e0ad8,
  accessFlags = 1,
  methodIndex = 11,
  registersSize = 3,
  outsSize = 2,
  insSize = 2,
  name = 0x6f28d6c6 <Address 0x6f28d6c6 out of bounds>,
  ...
}

(gdb) p *(ClassObject*)0x416e0ad8
$36 = {
  ...
  descriptor =  <Address 0x6f1e621b out of bounds>,
  ...
}

6ec32000-6edaa000 r--p 00000000 b3:1b 40972      [email protected]@[email protected]
...
6f127000-6f586000 r--p 004f5000 b3:1b 40972      [email protected]@[email protected]


(gdb) p /x 0x6f1e621b-0x6ec32000
$40 = 0x5b421b

$ hexdump -C -n32 -s0x5b421b [email protected]@[email protected]
005b421b  4c 61 6e 64 72 6f 69 64  2f 6f 73 2f 48 61 6e 64  |Landroid/os/Hand|
005b422b  6c 65 72 3b 00 1a 4c 61  6e 64 72 6f 69 64 2f 6f  |ler;..Landroid/o|

(gdb) p /x 0x6f28d6c6-0x6ec32000
$41 = 0x65b6c6

$ hexdump -C -n32 -s0x65b6c6 [email protected]@[email protected]
0065b6c6  64 69 73 70 61 74 63 68  4d 65 73 73 61 67 65 00  |dispatchMessage.|
0065b6d6  0d 64 69 73 70 61 74 63  68 4d 6f 76 65 64 00 11  |.dispatchMoved..|

得到的調用棧大概就是:

com.android.org.chromium.base.SystemMessageHandler.nativeDoRunLoopOnce
com.android.org.chromium.base.SystemMessageHandler.handleMessage
android.os.Handler.dispatchMessage
...

用GDB推導DVM的Java棧