診斷本機崩潰

以下部分包括常見的本機崩潰類型、範例崩潰轉儲的分析、邏輯刪除的討論。每種崩潰類型都包含範例debuggerd輸出,並突出顯示關鍵證據,以幫助您區分特定類型的崩潰。

中止

中止很有趣,因為它們是故意的。有許多不同的方法可以中止(包括呼叫abort(3) 、使assert(3)失敗、使用 Android 特定的致命日誌記錄類型之一),但所有方法都涉及呼叫abort 。對abort呼叫使用 SIGABRT 向呼叫執行緒發出訊號,因此在libc.so中顯示「中止」的幀加上 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 的更多信息,請參閱博客文章Android O 中的 Seccomp 過濾器。呼叫受限系統呼叫的執行緒將收到代碼為 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。如果你mm system/core/debuggerd/你會在你的路徑上同時得到一個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]