診斷本機崩潰

以下部分包括本機崩潰的常見類型、對示例崩潰轉儲的分析以及對墓碑的討論。每種崩潰類型都包含示例debuggerd輸出,其中突出顯示了關鍵證據,以幫助您區分特定類型的崩潰。

中止

中止很有趣,因為它們是故意的。有許多不同的中止方法(包括調用abort(3) 、使assert(3)失敗、使用 Android 特定的致命日誌記錄類型之一),但都涉及調用abortabort調用使用 SIGABRT 向調用線程發出信號,因此在libc.so中顯示“abort”的幀加上 SIGABRT 是要在debuggerd輸出中查找以識別這種情況的內容。

可能有一個明確的“中止消息”行。您還應該查看logcat輸出以查看該線程在故意殺死自己之前記錄了什麼,因為與assert(3)或高級致命日誌記錄工具不同, abort(3)不接受消息。

當前版本的 Android 內聯tgkill(2)系統調用,因此它們的堆棧是最容易閱讀的,對 abort(3) 的調用位於最頂部:

pid: 4637, tid: 4637, name: crasher  >>> crasher <<<
signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
Abort message: 'some_file.c:123: some_function: assertion "false" failed'
    r0  00000000  r1  0000121d  r2  00000006  r3  00000008
    r4  0000121d  r5  0000121d  r6  ffb44a1c  r7  0000010c
    r8  00000000  r9  00000000  r10 00000000  r11 00000000
    ip  ffb44c20  sp  ffb44a08  lr  eace2b0b  pc  eace2b16
backtrace:
    #00 pc 0001cb16  /system/lib/libc.so (abort+57)
    #01 pc 0001cd8f  /system/lib/libc.so (__assert2+22)
    #02 pc 00001531  /system/bin/crasher (do_action+764)
    #03 pc 00002301  /system/bin/crasher (main+68)
    #04 pc 0008a809  /system/lib/libc.so (__libc_init+48)
    #05 pc 00001097  /system/bin/crasher (_start_main+38)

舊版本的 Android 在原始中止調用(此處為第 4 幀)和實際發送信號(此處為第 0 幀)之間遵循了一條複雜的路徑。在 32 位 ARM 上尤其如此,它將__libc_android_abort (此處為第 3 幀)添加到其他平台的raise / pthread_kill / tgkill序列中:

pid: 1656, tid: 1656, name: crasher  >>> crasher <<<
signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
Abort message: 'some_file.c:123: some_function: assertion "false" failed'
    r0 00000000  r1 00000678  r2 00000006  r3 f70b6dc8
    r4 f70b6dd0  r5 f70b6d80  r6 00000002  r7 0000010c
    r8 ffffffed  r9 00000000  sl 00000000  fp ff96ae1c
    ip 00000006  sp ff96ad18  lr f700ced5  pc f700dc98  cpsr 400b0010
backtrace:
    #00 pc 00042c98  /system/lib/libc.so (tgkill+12)
    #01 pc 00041ed1  /system/lib/libc.so (pthread_kill+32)
    #02 pc 0001bb87  /system/lib/libc.so (raise+10)
    #03 pc 00018cad  /system/lib/libc.so (__libc_android_abort+34)
    #04 pc 000168e8  /system/lib/libc.so (abort+4)
    #05 pc 0001a78f  /system/lib/libc.so (__libc_fatal+16)
    #06 pc 00018d35  /system/lib/libc.so (__assert2+20)
    #07 pc 00000f21  /system/xbin/crasher
    #08 pc 00016795  /system/lib/libc.so (__libc_init+44)
    #09 pc 00000abc  /system/xbin/crasher

您可以使用crasher abort重現此類崩潰的實例。

純空指針解引用

這是經典的原生崩潰,雖然它只是下一個崩潰類型的特例,但值得單獨提及,因為它通常需要最少的思考。

在下面的示例中,即使崩潰函數位於libc.so中,因為字符串函數只是對它們給出的指針進行操作,所以您可以推斷strlen(3)是使用空指針調用的;這個崩潰應該直接交給調用代碼的作者。在這種情況下,幀#01 是錯誤的調用者。

pid: 25326, tid: 25326, name: crasher  >>> crasher <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
    r0 00000000  r1 00000000  r2 00004c00  r3 00000000
    r4 ab088071  r5 fff92b34  r6 00000002  r7 fff92b40
    r8 00000000  r9 00000000  sl 00000000  fp fff92b2c
    ip ab08cfc4  sp fff92a08  lr ab087a93  pc efb78988  cpsr 600d0030

backtrace:
    #00 pc 00019988  /system/lib/libc.so (strlen+71)
    #01 pc 00001a8f  /system/xbin/crasher (strlen_null+22)
    #02 pc 000017cd  /system/xbin/crasher (do_action+948)
    #03 pc 000020d5  /system/xbin/crasher (main+100)
    #04 pc 000177a1  /system/lib/libc.so (__libc_init+48)
    #05 pc 000010e4  /system/xbin/crasher (_start+96)

您可以使用crasher strlen-NULL重現此類崩潰的實例。

低地址空指針解引用

在許多情況下,故障地址不會是 0,而是其他一些低數字。特別是兩位或三位地址非常常見,而六位地址幾乎可以肯定不是空指針取消引用——這需要 1MiB 的偏移量。當您的代碼取消引用空指針時,通常會發生這種情況,就好像它是一個有效的結構一樣。常用函數是fprintf(3) (或任何其他採用 FILE* 的函數)和readdir(3) ,因為代碼通常無法檢查fopen(3)opendir(3)調用是否首先成功。

這是readdir的示例:

pid: 25405, tid: 25405, name: crasher  >>> crasher <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xc
    r0 0000000c  r1 00000000  r2 00000000  r3 3d5f0000
    r4 00000000  r5 0000000c  r6 00000002  r7 ff8618f0
    r8 00000000  r9 00000000  sl 00000000  fp ff8618dc
    ip edaa6834  sp ff8617a8  lr eda34a1f  pc eda618f6  cpsr 600d0030

backtrace:
    #00 pc 000478f6  /system/lib/libc.so (pthread_mutex_lock+1)
    #01 pc 0001aa1b  /system/lib/libc.so (readdir+10)
    #02 pc 00001b35  /system/xbin/crasher (readdir_null+20)
    #03 pc 00001815  /system/xbin/crasher (do_action+976)
    #04 pc 000021e5  /system/xbin/crasher (main+100)
    #05 pc 000177a1  /system/lib/libc.so (__libc_init+48)
    #06 pc 00001110  /system/xbin/crasher (_start+96)

這裡崩潰的直接原因是pthread_mutex_lock(3)試圖訪問地址 0xc(幀 0)。但是pthread_mutex_lock所做的第一件事是取消引用它所給定的pthread_mutex_t*state元素。如果查看源代碼,您可以看到該元素位於結構中的偏移量 0 處,這告訴您pthread_mutex_lock被賦予了無效指針 0xc。從第 1 幀中,您可以看到它是由readdir給定的,它從給定的DIR*中提取mutex_字段。查看該結構,您可以看到mutex_位於struct DIR的偏移量sizeof(int) + sizeof(size_t) + sizeof(dirent*)處,在 32 位設備上為 4 + 4 + 4 = 12 = 0xc,所以你發現了錯誤:調用者向readdir傳遞了一個空指針。此時您可以將堆棧粘貼到堆棧工具中,以找出在 logcat 中發生這種情況的位置。

  struct DIR {
    int fd_;
    size_t available_bytes_;
    dirent* next_;
    pthread_mutex_t mutex_;
    dirent buff_[15];
    long current_pos_;
  };

在大多數情況下,您實際上可以跳過此分析。足夠低的故障地址通常意味著您可以跳過堆棧中的任何libc.so幀並直接指責調用代碼。但並非總是如此,這就是您提出令人信服的案例的方式。

您可以使用crasher fprintf-NULLcrasher readdir-NULL重現這種崩潰的實例。

強化失敗

FORTIFY 失敗是當 C 庫檢測到可能導致安全漏洞的問題時發生的中止的一種特殊情況。許多 C 庫函數得到了強化;他們需要一個額外的參數來告訴他們緩衝區實際上有多大,並在運行時檢查您嘗試執行的操作是否真正適合。這是一個示例,其中代碼嘗試將read(fd, buf, 32)入實際上只有 10 個字節長的緩衝區...

pid: 25579, tid: 25579, name: crasher  >>> crasher <<<
signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
Abort message: 'FORTIFY: read: prevented 32-byte write into 10-byte buffer'
    r0 00000000  r1 000063eb  r2 00000006  r3 00000008
    r4 ff96f350  r5 000063eb  r6 000063eb  r7 0000010c
    r8 00000000  r9 00000000  sl 00000000  fp ff96f49c
    ip 00000000  sp ff96f340  lr ee83ece3  pc ee86ef0c  cpsr 000d0010

backtrace:
    #00 pc 00049f0c  /system/lib/libc.so (tgkill+12)
    #01 pc 00019cdf  /system/lib/libc.so (abort+50)
    #02 pc 0001e197  /system/lib/libc.so (__fortify_fatal+30)
    #03 pc 0001baf9  /system/lib/libc.so (__read_chk+48)
    #04 pc 0000165b  /system/xbin/crasher (do_action+534)
    #05 pc 000021e5  /system/xbin/crasher (main+100)
    #06 pc 000177a1  /system/lib/libc.so (__libc_init+48)
    #07 pc 00001110  /system/xbin/crasher (_start+96)

您可以使用crasher fortify重現此類崩潰的實例。

-fstack-protector 檢測到堆棧損壞

編譯器的-fstack-protector選項將檢查插入到具有堆棧緩衝區的函數中,以防止緩衝區溢出。默認情況下,此選項對平台代碼啟用,但不適用於應用程序。啟用此選項後,編譯器將指令添加到函數序言以將隨機值寫入堆棧上的最後一個局部變量,並將指令添加到函數尾聲以將其讀回並檢查它是否未更改。如果該值發生更改,它會被緩衝區溢出覆蓋,因此結尾調用__stack_chk_fail來記錄消息併中止。

pid: 26717, tid: 26717, name: crasher  >>> crasher <<<
signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
Abort message: 'stack corruption detected'
    r0 00000000  r1 0000685d  r2 00000006  r3 00000008
    r4 ffd516d8  r5 0000685d  r6 0000685d  r7 0000010c
    r8 00000000  r9 00000000  sl 00000000  fp ffd518bc
    ip 00000000  sp ffd516c8  lr ee63ece3  pc ee66ef0c  cpsr 000e0010

backtrace:
    #00 pc 00049f0c  /system/lib/libc.so (tgkill+12)
    #01 pc 00019cdf  /system/lib/libc.so (abort+50)
    #02 pc 0001e07d  /system/lib/libc.so (__libc_fatal+24)
    #03 pc 0004863f  /system/lib/libc.so (__stack_chk_fail+6)
    #04 pc 000013ed  /system/xbin/crasher (smash_stack+76)
    #05 pc 00001591  /system/xbin/crasher (do_action+280)
    #06 pc 00002219  /system/xbin/crasher (main+100)
    #07 pc 000177a1  /system/lib/libc.so (__libc_init+48)
    #08 pc 00001144  /system/xbin/crasher (_start+96)

您可以通過回溯中存在__stack_chk_fail和特定的中止消息來將此與其他類型的中止區分開來。

您可以使用crasher smash-stack重現此類崩潰的實例。

來自不允許的系統調用的 Seccomp SIGSYS

seccomp系統(特別是 seccomp-bpf)限制對系統調用的訪問。有關面向平台開發人員的 seccomp 的更多信息,請參閱博文 Seccomp filter in Android O 。調用受限系統調用的線程將收到帶有代碼 SYS_SECCOMP 的 SIGSYS 信號。系統調用號將與架構一起顯示在原因行中。重要的是要注意系統調用號因體系結構而異。例如, readlinkat(2)系統調用在 x86 上是 305,但在 x86-64 上是 267。 arm 和 arm64 上的索書號再次不同。因為系統調用號因體系結構而異,所以使用堆棧跟踪來找出不允許的系統調用通常比在標頭中查找系統調用號更容易。

pid: 11046, tid: 11046, name: crasher  >>> crasher <<<
signal 31 (SIGSYS), code 1 (SYS_SECCOMP), fault addr --------
Cause: seccomp prevented call to disallowed arm system call 99999
    r0 cfda0444  r1 00000014  r2 40000000  r3 00000000
    r4 00000000  r5 00000000  r6 00000000  r7 0001869f
    r8 00000000  r9 00000000  sl 00000000  fp fffefa58
    ip fffef898  sp fffef888  lr 00401997  pc f74f3658  cpsr 600f0010

backtrace:
    #00 pc 00019658  /system/lib/libc.so (syscall+32)
    #01 pc 00001993  /system/bin/crasher (do_action+1474)
    #02 pc 00002699  /system/bin/crasher (main+68)
    #03 pc 0007c60d  /system/lib/libc.so (__libc_init+48)
    #04 pc 000011b0  /system/bin/crasher (_start_main+72)

您可以通過信號線上的SYS_SECCOMP和原因線上的描述來區分不允許的系統調用和其他崩潰。

您可以使用crasher seccomp重現此類崩潰的實例。

僅執行內存違規(僅限 Android 10)

僅對於 Android 10 中的 arm64,二進製文件和庫的可執行段被映射到內存中僅執行(不可讀),作為針對代碼重用攻擊的強化技術。此緩解措施與其他緩解措施的相互作用很差,後來被刪除。

使代碼不可讀會導致有意和無意讀取標記為僅執行的內存段以拋出帶有代碼SEGV_ACCERRSIGSEGV 。這可能是由於錯誤、漏洞、與代碼混合的數據(例如文字池)或有意的內存自省而發生的。

編譯器假定代碼和數據沒有混合,但手寫彙編可能會出現問題。在許多情況下,這些可以通過簡單地將常量移動到.data部分來解決。如果對可執行代碼段絕對需要代碼自省,則應首先調用mprotect(2)將代碼標記為可讀,然後在操作完成後再次將其標記為不可讀。

pid: 2938, tid: 2940, name: crasher64  >>> crasher64 <<<
signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x5f2ced24a8
Cause: execute-only (no-read) memory access error; likely due to data in .text.
    x0  0000000000000000  x1  0000005f2cecf21f  x2  0000000000000078  x3  0000000000000053
    x4  0000000000000074  x5  8000000000000000  x6  ff71646772607162  x7  00000020dcf0d16c
    x8  0000005f2ced24a8  x9  000000781251c55e  x10 0000000000000000  x11 0000000000000000
    x12 0000000000000014  x13 ffffffffffffffff  x14 0000000000000002  x15 ffffffffffffffff
    x16 0000005f2ced52f0  x17 00000078125c0ed8  x18 0000007810e8e000  x19 00000078119fbd50
    x20 00000078125d6020  x21 00000078119fbd50  x22 00000b7a00000b7a  x23 00000078119fbdd8
    x24 00000078119fbd50  x25 00000078119fbd50  x26 00000078119fc018  x27 00000078128ea020
    x28 00000078119fc020  x29 00000078119fbcb0
    sp  00000078119fba40  lr  0000005f2ced1b94  pc  0000005f2ced1ba4

backtrace:
      #00 pc 0000000000003ba4  /system/bin/crasher64 (do_action+2348)
      #01 pc 0000000000003234  /system/bin/crasher64 (thread_callback+44)
      #02 pc 00000000000e2044  /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+36)
      #03 pc 0000000000083de0  /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64)

您可以通過原因行將僅執行內存違規與其他崩潰區分開來。

您可以使用crasher xom重現此類崩潰的實例。

fdsan 檢測到錯誤

Android 的 fdsan 文件描述符清理程序有助於捕捉文件描述符的常見錯誤,例如使用後關閉和雙重關閉。有關調試(和避免)此類錯誤的更多詳細信息,請參閱fdsan 文檔

pid: 32315, tid: 32315, name: crasher64  >>> crasher64 <<<
signal 35 (), code -1 (SI_QUEUE), fault addr --------
Abort message: 'attempted to close file descriptor 3, expected to be unowned, actually owned by FILE* 0x7d8e413018'
    x0  0000000000000000  x1  0000000000007e3b  x2  0000000000000023  x3  0000007fe7300bb0
    x4  3033313465386437  x5  3033313465386437  x6  3033313465386437  x7  3831303331346538
    x8  00000000000000f0  x9  0000000000000000  x10 0000000000000059  x11 0000000000000034
    x12 0000007d8ebc3a49  x13 0000007fe730077a  x14 0000007fe730077a  x15 0000000000000000
    x16 0000007d8ec9a7b8  x17 0000007d8ec779f0  x18 0000007d8f29c000  x19 0000000000007e3b
    x20 0000000000007e3b  x21 0000007d8f023020  x22 0000007d8f3b58dc  x23 0000000000000001
    x24 0000007fe73009a0  x25 0000007fe73008e0  x26 0000007fe7300ca0  x27 0000000000000000
    x28 0000000000000000  x29 0000007fe7300c90
    sp  0000007fe7300860  lr  0000007d8ec2f22c  pc  0000007d8ec2f250

backtrace:
      #00 pc 0000000000088250  /bionic/lib64/libc.so (fdsan_error(char const*, ...)+384)
      #01 pc 0000000000088060  /bionic/lib64/libc.so (android_fdsan_close_with_tag+632)
      #02 pc 00000000000887e8  /bionic/lib64/libc.so (close+16)
      #03 pc 000000000000379c  /system/bin/crasher64 (do_action+1316)
      #04 pc 00000000000049c8  /system/bin/crasher64 (main+96)
      #05 pc 000000000008021c  /bionic/lib64/libc.so (_start_main)

您可以通過回溯中fdsan_error的存在和特定的中止消息來將此與其他類型的中止區分開來。

您可以使用crasher fdsan_filecrasher fdsan_dir此類崩潰的實例。

調查崩潰轉儲

如果您現在沒有正在調查的特定崩潰,則平台源包含一個用於測試debuggerd的工具,稱為 crasher。如果您在system/core/debuggerd/中使用mm ,您將在您的路徑上同時獲得一個crasher和一個crasher64 (後者允許您測試 64 位崩潰)。根據您提供的命令行參數,Crasher 可能會以多種有趣的方式崩潰。使用crasher --help查看當前支持的選擇。

為了在故障轉儲中介紹不同的部分,讓我們通過這個示例故障轉儲:

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'Android/aosp_flounder/flounder:5.1.51/AOSP/enh08201009:eng/test-keys'
Revision: '0'
ABI: 'arm'
pid: 1656, tid: 1656, name: crasher  >>> crasher <<<
signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
Abort message: 'some_file.c:123: some_function: assertion "false" failed'
    r0 00000000  r1 00000678  r2 00000006  r3 f70b6dc8
    r4 f70b6dd0  r5 f70b6d80  r6 00000002  r7 0000010c
    r8 ffffffed  r9 00000000  sl 00000000  fp ff96ae1c
    ip 00000006  sp ff96ad18  lr f700ced5  pc f700dc98  cpsr 400b0010
backtrace:
    #00 pc 00042c98  /system/lib/libc.so (tgkill+12)
    #01 pc 00041ed1  /system/lib/libc.so (pthread_kill+32)
    #02 pc 0001bb87  /system/lib/libc.so (raise+10)
    #03 pc 00018cad  /system/lib/libc.so (__libc_android_abort+34)
    #04 pc 000168e8  /system/lib/libc.so (abort+4)
    #05 pc 0001a78f  /system/lib/libc.so (__libc_fatal+16)
    #06 pc 00018d35  /system/lib/libc.so (__assert2+20)
    #07 pc 00000f21  /system/xbin/crasher
    #08 pc 00016795  /system/lib/libc.so (__libc_init+44)
    #09 pc 00000abc  /system/xbin/crasher
Tombstone written to: /data/tombstones/tombstone_06
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

如果您正在搜索日誌以查找本機崩潰,則帶有空格的星號行會很有幫助。除了在原生崩潰開始時,字符串“*** ***”很少出現在日誌中。

Build fingerprint:
'Android/aosp_flounder/flounder:5.1.51/AOSP/enh08201009:eng/test-keys'

指紋可讓您準確識別崩潰發生在哪個構建上。這與ro.build.fingerprint系統屬性完全相同。

Revision: '0'

修訂是指硬件而不是軟件。這通常是不用的,但可以幫助您自動忽略已知由壞硬件引起的錯誤。這與ro.revision系統屬性完全相同。

ABI: 'arm'

ABI 是 arm、arm64、x86 或 x86-64 之一。這對於上面提到的stack腳本最有用,因此它知道要使用什麼工具鏈。

pid: 1656, tid: 1656, name: crasher >>> crasher <<<

此行標識進程中崩潰的特定線程。在這種情況下,它是進程的主線程,因此進程 ID 和線程 ID 匹配。第一個名字是線程名,>>>和<<<包圍的名字是進程名。對於應用程序,進程名稱通常是完全限定的包名稱(例如 com.facebook.katana),這在提交錯誤或嘗試在 Google Play 中查找應用程序時很有用。 pid 和 tid 在查找崩潰前的相關日誌行時也很有用。

signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------

這一行告訴您接收到了哪個信號 (SIGABRT),以及更多關於它是如何接收的 (SI_TKILL)。 debuggerd報告的信號有 SIGABRT、SIGBUS、SIGFPE、SIGILL、SIGSEGV 和 SIGTRAP。信號特定代碼根據特定信號而變化。

Abort message: 'some_file.c:123: some_function: assertion "false" failed'

並非所有崩潰都會有中止消息行,但中止會有。這是從該 pid/tid 的致命 logcat 輸出的最後一行自動收集的,在故意中止的情況下,可能會解釋程序為什麼會自行終止。

r0 00000000 r1 00000678 r2 00000006 r3 f70b6dc8
r4 f70b6dd0 r5 f70b6d80 r6 00000002 r7 0000010c
r8 ffffffed r9 00000000 sl 00000000 fp ff96ae1c
ip 00000006 sp ff96ad18 lr f700ced5 pc f700dc98 cpsr 400b0010

寄存器轉儲顯示接收信號時 CPU 寄存器的內容。 (這部分在 ABI 之間差異很大。)它們的用處將取決於確切的崩潰。

backtrace:
    #00 pc 00042c98 /system/lib/libc.so (tgkill+12)
    #01 pc 00041ed1 /system/lib/libc.so (pthread_kill+32)
    #02 pc 0001bb87 /system/lib/libc.so (raise+10)
    #03 pc 00018cad /system/lib/libc.so (__libc_android_abort+34)
    #04 pc 000168e8 /system/lib/libc.so (abort+4)
    #05 pc 0001a78f /system/lib/libc.so (__libc_fatal+16)
    #06 pc 00018d35 /system/lib/libc.so (__assert2+20)
    #07 pc 00000f21 /system/xbin/crasher
    #08 pc 00016795 /system/lib/libc.so (__libc_init+44)
    #09 pc 00000abc /system/xbin/crasher

回溯向您顯示崩潰時我們在代碼中的位置。第一列是幀​​號(匹配 gdb 的樣式,其中最深的幀為 0)。 PC 值是相對於共享庫的位置而不是絕對地址。下一列是映射區域的名稱(通常是共享庫或可執行文件,但可能不適用於 JIT 編譯代碼)。最後,如果符號可用,則顯示 PC 值對應的符號,以及該符號的偏移量(以字節為單位)。您可以將它與objdump(1)結合使用來查找相應的彙編指令。

讀墓碑

Tombstone written to: /data/tombstones/tombstone_06

這告訴你debuggerd在哪裡寫了額外的信息。 debuggerd將保留最多 10 個墓碑,循環通過數字 00 到 09 並根據需要覆蓋現有的墓碑。

墓碑包含與故障轉儲相同的信息,以及一些額外信息。例如,它包括所有線程(不僅僅是崩潰線程)、浮點寄存器、原始堆棧轉儲和圍繞寄存器地址的內存轉儲的回溯。最有用的是它還包括一個完整的內存映射(類似於/proc/ pid /maps )。下面是一個來自 32 位 ARM 進程崩潰的帶註釋示例:

memory map: (fault address prefixed with --->)
--->ab15f000-ab162fff r-x 0 4000 /system/xbin/crasher (BuildId:
b9527db01b5cf8f5402f899f64b9b121)

這裡有兩點需要注意。首先是這一行以“--->”為前綴。當您的崩潰不僅僅是空指針取消引用時,這些映射最有用。如果故障地址很小,則可能是空指針取消引用的某種變體。否則,查看故障地址周圍的地圖通常可以為您提供有關發生了什麼的線索。通過查看地圖可以識別的一些可能的問題包括:

  • 讀/寫超過內存塊的末尾。
  • 在內存塊開始之前讀/寫。
  • 嘗試執行非代碼。
  • 跑出堆棧的末尾。
  • 嘗試寫入代碼(如上例所示)。

第二點要注意的是,可執行文件和共享庫文件將在 Android 6.0 及更高版本中顯示 BuildId(如果存在),因此您可以準確地看到哪個版本的代碼崩潰了。自 Android 6.0 起,平台二進製文件默認包含 BuildId; NDK r12 及更高版本也會自動將-Wl,--build-id傳遞給鏈接器。

ab163000-ab163fff r--      3000      1000  /system/xbin/crasher
ab164000-ab164fff rw-         0      1000
f6c80000-f6d7ffff rw-         0    100000  [anon:libc_malloc]

在 Android 上,堆不一定是單個區域。堆區域將被標記為[anon:libc_malloc]

f6d82000-f6da1fff r--         0     20000  /dev/__properties__/u:object_r:logd_prop:s0
f6da2000-f6dc1fff r--         0     20000  /dev/__properties__/u:object_r:default_prop:s0
f6dc2000-f6de1fff r--         0     20000  /dev/__properties__/u:object_r:logd_prop:s0
f6de2000-f6de5fff r-x         0      4000  /system/lib/libnetd_client.so (BuildId: 08020aa06ed48cf9f6971861abf06c9d)
f6de6000-f6de6fff r--      3000      1000  /system/lib/libnetd_client.so
f6de7000-f6de7fff rw-      4000      1000  /system/lib/libnetd_client.so
f6dec000-f6e74fff r-x         0     89000  /system/lib/libc++.so (BuildId: 8f1f2be4b37d7067d366543fafececa2) (load base 0x2000)
f6e75000-f6e75fff ---         0      1000
f6e76000-f6e79fff r--     89000      4000  /system/lib/libc++.so
f6e7a000-f6e7afff rw-     8d000      1000  /system/lib/libc++.so
f6e7b000-f6e7bfff rw-         0      1000  [anon:.bss]
f6e7c000-f6efdfff r-x         0     82000  /system/lib/libc.so (BuildId: d189b369d1aafe11feb7014d411bb9c3)
f6efe000-f6f01fff r--     81000      4000  /system/lib/libc.so
f6f02000-f6f03fff rw-     85000      2000  /system/lib/libc.so
f6f04000-f6f04fff rw-         0      1000  [anon:.bss]
f6f05000-f6f05fff r--         0      1000  [anon:.bss]
f6f06000-f6f0bfff rw-         0      6000  [anon:.bss]
f6f0c000-f6f21fff r-x         0     16000  /system/lib/libcutils.so (BuildId: d6d68a419dadd645ca852cd339f89741)
f6f22000-f6f22fff r--     15000      1000  /system/lib/libcutils.so
f6f23000-f6f23fff rw-     16000      1000  /system/lib/libcutils.so
f6f24000-f6f31fff r-x         0      e000  /system/lib/liblog.so (BuildId: e4d30918d1b1028a1ba23d2ab72536fc)
f6f32000-f6f32fff r--      d000      1000  /system/lib/liblog.so
f6f33000-f6f33fff rw-      e000      1000  /system/lib/liblog.so

通常,共享庫具有三個相鄰的條目。一種是可讀可執行的(代碼),一種是只讀的(只讀數據),一種是可讀寫的(可變數據)。第一列顯示映射的地址範圍,第二列顯示權限(在通常的 Unix ls(1)樣式中),第三列顯示文件的偏移量(十六進制),第四列顯示區域的大小(十六進制),第五列是文件(或其他區域名稱)。

f6f34000-f6f53fff r-x         0     20000  /system/lib/libm.so (BuildId: 76ba45dcd9247e60227200976a02c69b)
f6f54000-f6f54fff ---         0      1000
f6f55000-f6f55fff r--     20000      1000  /system/lib/libm.so
f6f56000-f6f56fff rw-     21000      1000  /system/lib/libm.so
f6f58000-f6f58fff rw-         0      1000
f6f59000-f6f78fff r--         0     20000  /dev/__properties__/u:object_r:default_prop:s0
f6f79000-f6f98fff r--         0     20000  /dev/__properties__/properties_serial
f6f99000-f6f99fff rw-         0      1000  [anon:linker_alloc_vector]
f6f9a000-f6f9afff r--         0      1000  [anon:atexit handlers]
f6f9b000-f6fbafff r--         0     20000  /dev/__properties__/properties_serial
f6fbb000-f6fbbfff rw-         0      1000  [anon:linker_alloc_vector]
f6fbc000-f6fbcfff rw-         0      1000  [anon:linker_alloc_small_objects]
f6fbd000-f6fbdfff rw-         0      1000  [anon:linker_alloc_vector]
f6fbe000-f6fbffff rw-         0      2000  [anon:linker_alloc]
f6fc0000-f6fc0fff r--         0      1000  [anon:linker_alloc]
f6fc1000-f6fc1fff rw-         0      1000  [anon:linker_alloc_lob]
f6fc2000-f6fc2fff r--         0      1000  [anon:linker_alloc]
f6fc3000-f6fc3fff rw-         0      1000  [anon:linker_alloc_vector]
f6fc4000-f6fc4fff rw-         0      1000  [anon:linker_alloc_small_objects]
f6fc5000-f6fc5fff rw-         0      1000  [anon:linker_alloc_vector]
f6fc6000-f6fc6fff rw-         0      1000  [anon:linker_alloc_small_objects]
f6fc7000-f6fc7fff rw-         0      1000  [anon:arc4random _rsx structure]
f6fc8000-f6fc8fff rw-         0      1000  [anon:arc4random _rs structure]
f6fc9000-f6fc9fff r--         0      1000  [anon:atexit handlers]
f6fca000-f6fcafff ---         0      1000  [anon:thread signal stack guard page]

從 Android 5.0 開始,C 庫命名了大部分匿名映射區域,因此神秘區域減少了。

f6fcb000-f6fccfff rw- 0 2000 [stack:5081]

名為[stack: tid ]的區域是給定線程的堆棧。

f6fcd000-f702afff r-x         0     5e000  /system/bin/linker (BuildId: 84f1316198deee0591c8ac7f158f28b7)
f702b000-f702cfff r--     5d000      2000  /system/bin/linker
f702d000-f702dfff rw-     5f000      1000  /system/bin/linker
f702e000-f702ffff rw-         0      2000
f7030000-f7030fff r--         0      1000
f7031000-f7032fff rw-         0      2000
ffcd7000-ffcf7fff rw-         0     21000
ffff0000-ffff0fff r-x         0      1000  [vectors]

您看到的是[vector]還是[vdso]取決於架構。 ARM 使用[vector] ,而所有其他架構使用[vdso]