诊断原生崩溃

以下部分包括常见的原生崩溃类型、崩溃转储样本分析以及对 tombstone 的讨论。每种崩溃类型都包含示例 debuggerd 输出,其中突出显示了关键证据,以帮助您区分特定类型的崩溃。

Abort(中止)

Abort(中止)很有趣,因为它们是故意的。有很多不同的中止方式(包括调用 abort(3)、断言 assert(3) 失败、使用 Android 特定的致命日志记录类型之一),但所有这些都涉及调用 abortabort 调用会向调用线程发送 SIGABRT 信号,因此在 libc.so 中显示 “abort” 的帧加上 SIGABRT 是在 debuggerd 输出中识别这种情况需要查找的内容。

可能有一行明确的 “abort message”(中止消息)。您还应该查看 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 在原始 abort 调用(此处为帧 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,因此您找到了 bug:调用者向 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 失败

FORTIFY 失败是 abort 的一种特殊情况,当 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 以及特定的中止消息来区分它与其他类型的 abort。

您可以使用 crasher smash-stack 重现此类崩溃的实例。

来自被禁止的系统调用的 Seccomp SIGSYS

seccomp 系统(特别是 seccomp-bpf)限制对系统调用的访问。有关平台开发人员的 seccomp 的更多信息,请参阅博客文章Android O 中的 Seccomp 过滤器。调用受限系统调用的线程将收到带有代码 SYS_SECCOMP 的 SIGSYS 信号。系统调用号将显示在 cause 行中,以及架构。重要的是要注意,系统调用号在不同架构之间有所不同。例如,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 以及 cause 行上的描述来区分被禁止的系统调用与其他崩溃。

您可以使用 crasher seccomp 重现此类崩溃的实例。

仅执行内存违规(仅限 Android 10)

对于仅限 Android 10 中的 arm64,二进制文件和库的可执行段被映射到仅执行(不可读)内存中,作为针对代码重用攻击的强化技术。此缓解措施与其他缓解措施相互作用不良,后来被移除。

使代码不可读会导致有意和无意的读取到标记为仅执行的内存段中,从而引发代码为 SEGV_ACCERRSIGSEGV。这可能是由于 bug、漏洞、与代码混合的数据(例如文字池)或有意的内存自省而发生的。

编译器假定代码和数据未混合,但手写汇编可能会出现问题。在许多情况下,只需将常量移动到 .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)

您可以通过 cause 行将仅执行内存违规与其他崩溃区分开来。

您可以使用 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 以及特定的中止消息来区分它与其他类型的 abort。

您可以使用 crasher fdsan_filecrasher fdsan_dir 重现此类崩溃的实例。

调查崩溃转储

如果您现在没有要调查的特定崩溃,平台源代码包含一个用于测试 debuggerd 的工具,称为 crasher。如果您在 system/core/debuggerd/mm,您将在您的路径中获得 crashercrasher64(后者允许您测试 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'

修订版本指的是硬件而不是软件。这通常未使用,但可以帮助您自动忽略已知由硬件不良引起的 bug。这与 ro.revision 系统属性完全相同。

ABI: 'arm'

ABI 是 arm、arm64、x86 或 x86-64 之一。这主要对上面提到的 stack 脚本有用,以便它知道要使用哪个工具链。

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

此行标识进程中崩溃的特定线程。在本例中,它是进程的主线程,因此进程 ID 和线程 ID 匹配。第一个名称是线程名称,而由 >>> 和 <<< 包围的名称是进程名称。对于应用,进程名称通常是完全限定的软件包名称(例如 com.facebook.katana),这在提交 bug 或尝试在 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'

并非所有崩溃都有一行中止消息,但 abort 会有。这是从该 pid/tid 的致命 logcat 输出的最后一行自动收集的,并且在有意 abort 的情况下,很可能给出程序终止自身的原因的解释。

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

Tombstone written to: /data/tombstones/tombstone_06

这告诉您 debuggerd 在哪里写入了额外信息。debuggerd 最多会保留 10 个 tombstone,在数字 00 到 09 之间循环,并在必要时覆盖现有 tombstone。

tombstone 包含与崩溃转储相同的信息,以及一些额外信息。例如,它包括所有线程的回溯(而不仅仅是崩溃线程)、浮点寄存器、原始堆栈转储以及寄存器中地址周围的内存转储。最有用的是,它还包括完整的内存映射(类似于 /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]