ca133:Native Crash的一切

作者:陈冠有-小米

本文由作者授权发布。

Native Crash一直是各大app的痛,本文非常有深度,是小米公司的陈冠有同学的一个内部分享,无私的分享给大家,相关资料在互联网上非常少,有精通Native Crash分析的同学分享实属不易,因为知识深入的原因可能非常难理解,我也只能略懂一二,但是本文非常值得收藏,时常查阅。

下面开始正文。


1

常见的Native Crash类型

SIGSEGV

SEGV_MAPERR

地址不在 /proc/self/maps 映射中

SEGV_ACCERR

没有访问权限

SEGV_MTESERR

MTE特有类型

SIGABRT

程序主动退出,常见调用函数abort(),raise()等

SIGILL

ILL_ILLOPC

非法操作码(opcode)

ILL_ILLOPN

非法操作数(operand)

ILL_ILLADR

非法寻址

ILL_ILLTRP

非法trap,如_builtintrap()主动崩溃

ILL_PRVOPC

非法特权操作码(privileged opcode)

ILL_PRVREG

非法特权寄存器(privileged register)

ILL_COPROC

协处理器错误

ILL_BADSTK

内部堆栈错误

SIGBUS

BUS_ADRALN

访问地址未对齐

BUS_ADRERR

访问不存在的物理地址

BUS_OBJERR

特定对象的硬件错误

SIGFPE

FPE_INTDIV

整数除以0

FPE_INTOVF

整数溢出

FPE_FLTDIV

浮点数除以0

FPE_FLTOVF

浮点数上溢(overflow)

FPE_FLTUND

浮点数下溢(underflow)

FPE_FLTRES

浮点数结果不精确

FPE_FLTINV

无效的浮点运算

FPE_FLTSUB

越界

2

Android日志

当程序发生了 Native Crash 错误,Android 的日志会输出到 log crash buffer 上,因此我们通过

adb logcat -b crash 抓取到相应的错误报告,而日志本身能提供的信息是有限的,仅仅是错误堆栈,与当前线程的寄存器信息。

--------- beginning of crash

06-07 01:53:32.465 12027 12027 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

06-07 01:53:32.465 12027 12027 F DEBUG   : Revision: '0'

06-07 01:53:32.466 12027 12027 F DEBUG   : ABI: 'arm64'

06-07 01:53:32.466 12027 12027 F DEBUG   : Timestamp: 2022-06-07 01:53:32.033409857+0800

06-07 01:53:32.466 12027 12027 F DEBUG   : Process uptime: 0s

06-07 01:53:32.466 12027 12027 F DEBUG   : Cmdline: mediaserver64

06-07 01:53:32.466 12027 12027 F DEBUG   : pid: 1139, tid: 11981, name: NPDecoder  >>> mediaserver64 <<<

06-07 01:53:32.466 12027 12027 F DEBUG   : uid: 1013

06-07 01:53:32.466 12027 12027 F DEBUG   : tagged_addr_ctrl: 0000000000000001

06-07 01:53:32.466 12027 12027 F DEBUG   : signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x7c02d886f0

06-07 01:53:32.466 12027 12027 F DEBUG   :     x0  79748c5e568e2ddc  x1  0000007ca13c3618  x2  0000000000000000  x3  0000007ca1291000

06-07 01:53:32.466 12027 12027 F DEBUG   :     x4  0000000001909705  x5  0000000000000000  x6  0000007c02d88808  x7  b60625655bf0252f

06-07 01:53:32.467 12027 12027 F DEBUG   :     x8  0000000000000080  x9  0000007ca126fed7  x10 0000000000000006  x11 0000007bfd0a81fc

06-07 01:53:32.467 12027 12027 F DEBUG   :     x12 9ef8a95ca9649dbe  x13 e44782d5ac38720e  x14 0000007bfd0a8030  x15 0000001e56307b5c

06-07 01:53:32.467 12027 12027 F DEBUG   :     x16 0000007c95dfdb70  x17 0000007c9844f118  x18 0000007bfaa28000  x19 b400007c13c246d0

06-07 01:53:32.467 12027 12027 F DEBUG   :     x20 0000007c02d88730  x21 b400007c13c67c00  x22 0000000000000415  x23 0000007c02d89000

06-07 01:53:32.467 12027 12027 F DEBUG   :     x24 0000000000000002  x25 b400007c13c246d0  x26 b400007c13c67c00  x27 0000007c02d89000

06-07 01:53:32.467 12027 12027 F DEBUG   :     x28 0000007ca13c2c28  x29 0000007c02d886f0

06-07 01:53:32.467 12027 12027 F DEBUG   :     lr  0000007c02d886f0  sp  0000007c02d886d0  pc  0000007c02d886f0  pst 0000000080001000

06-07 01:53:32.467 12027 12027 F DEBUG   : backtrace:

06-07 01:53:32.467 12027 12027 F DEBUG   :       #00 pc 00000000000f86f0  [anon:stack_and_tls:11981]

当只有日志堆栈不能进行更详细的分析时,我们还需要程序的部分内存信息以及寄存器信息,而Android 的错误机制会相应的会生成一份 tombstone 文件保存到 /data/tombstones/tombstone_xx ,对于没有 Root 权限的机器则可以通过 adb bugreport 抓取出 tombstone 文件。

3

Tombstone 

tombstone 文件保存的信息有错误程序的体系结构,通俗的说 arm、arm64 等,发生时间点,程序名,错误类型,错误程序的进程号、线程号,错误现场寄存器,堆栈和部分寄存器地址附近的内存信息,程序内存映射表 /proc/self/maps ,FD 信息以及发生错误时该程序输出的日志。

ABI: 'arm64'   【 arm64的程序 】

Timestamp: 2022-06-07 01:53:32.033409857+0800  【 发生错误的时间戳 】

Process uptime: 0s

Cmdline: mediaserver64   【 程序名 】

pid: 1139, tid: 11981, name: NPDecoder  >>> mediaserver64 <<<  【 进程号、线程号 】

uid: 1013

错误类型

signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x7c02d886f0

【 错误类型是 SIGSEGV,子类是 SEGV_ACCERR,错误地址0x7c02d886f0 】

SIGSEGV 也是我们最常见的 Native Crash 类型,大部分时候我们称其为段错误,而错误意思是在 PC=0x7c02d886f0 上发生拒绝访问的段错误。

寄存器信息

x0  79748c5e568e2ddc  x1  0000007ca13c3618  x2  0000000000000000  x3  0000007ca1291000

x4  0000000001909705  x5  0000000000000000  x6  0000007c02d88808  x7  b60625655bf0252f

x8  0000000000000080  x9  0000007ca126fed7  x10 0000000000000006  x11 0000007bfd0a81fc

x12 9ef8a95ca9649dbe  x13 e44782d5ac38720e  x14 0000007bfd0a8030  x15 0000001e56307b5c

x16 0000007c95dfdb70  x17 0000007c9844f118  x18 0000007bfaa28000  x19 b400007c13c246d0

x20 0000007c02d88730  x21 b400007c13c67c00  x22 0000000000000415  x23 0000007c02d89000

x24 0000000000000002  x25 b400007c13c246d0  x26 b400007c13c67c00  x27 0000007c02d89000

x28 0000007ca13c2c28  x29 0000007c02d886f0

lr  0000007c02d886f0  sp  0000007c02d886d0  pc  0000007c02d886f0  pst 0000000080001000

堆栈信息

backtrace:

#00 pc 00000000000f86f0  [anon:stack_and_tls:11981]   【PC刚好落在线程栈地址上】

这种情况很少见,虽然它只有的一条堆栈,并不代表程序是从这里开始运行,出现的这种情况仅仅 unwind 无法正确的回溯。

我们是可以通过栈地址空间内存进行恢复调用栈,用户态主线程栈(红色部分)结构如下:

而线程栈位于 mmap segmemt上,我们可以在 /proc/self/maps 上找到该线程栈的地址空间范围。

0000007c'02c90000-0000007c'02d8bfff rw-         0     fc000  [anon:stack_and_tls:11981]

大多数 arm64 的 Linux Android 程序,它的线程调用栈结构样例如下:

 

//  bionic/libc/arch-arm64/bionic/__bionic_clone.S#include <private/bionic_asm.h>// pid_t __bionic_clone(int flags, void* child_stack, pid_t* parent_tid, void* tls, pid_t* child_tid, int (*fn)(void*), void* arg);ENTRY_PRIVATE(__bionic_clone)    # Push 'fn' and 'arg' onto the child stack.    stp     x5, x6, [x1, #-16]!    # Make the system call.    mov     x8, __NR_clone    svc     #0    # Are we the child?    cbz     x0, .L_bc_child    # Set errno if something went wrong.    cmn     x0, #(MAX_ERRNO + 1)    cneg    x0, x0, hi    b.hi    __set_errno_internal    ret.L_bc_child:    # We're in the child now. Set the end of the frame record chain.    mov     x29, #0    # Setting x30 to 0 will make the unwinder stop at __start_thread.    mov     x30, #0    # Call __start_thread with the 'fn' and 'arg' we stored on the child stack.    ldp     x0, x1, [sp], #16    b       __start_threadEND(__bionic_clone)NOTE_GNU_PROPERTY()

内存信息

tombstone 会记录当前有效地址的寄存器附近内存信息,大小为0x100,这个可以修改文件

system/core/debuggerd/libdebuggerd/utility.cpp 中的宏定义 MEMORY_BYTES_TO_DUMP 参数

像这种一条堆栈的情况下,栈的内存信息配合下边的映射表可以帮助到我们对栈进行恢复。

memory near x1 (/system/lib64/libstagefright.so):

0000007ca13c35f0 0000000000000000 0000000000000000  ................

0000007ca13c3600 0000000000000000 0000000000000000  ................

0000007ca13c3610 0000000000000000 0000007ca132326c  ........l22.|...

0000007ca13c3620 0000007ca1324008 0000007ca10552e4  .@2.|....R..|...

0000007ca13c3630 0000007ca10552e8 0000007ca10552ec  .R..|....R..|...

0000007ca13c3640 0000007ca10552f4 0000007ca132402c  .R..|...,@2.|...

0000007ca13c3650 0000000000000000 0000000000000000  ................

0000007ca13c3660 0000000000000000 0000000000000000  ................

0000007ca13c3670 0000000000000000 0000007ca134ea84  ..........4.|...

0000007ca13c3680 0000007ca134ecec 0000007ca10552e4  ..4.|....R..|...

0000007ca13c3690 0000007ca10552e8 0000007ca10552ec  .R..|....R..|...

0000007ca13c36a0 0000007ca10552f4 0000007ca134ed10  .R..|.....4.|...

0000007ca13c36b0 0000000000000000 0000000000000000  ................

0000007ca13c36c0 0000000000000000 0000000000000000  ................

0000007ca13c36d0 0000000000000000 0000007ca135d02c  ........,.5.|...

0000007ca13c36e0 0000007ca135d4b8 0000007ca10552e4  ..5.|....R..|...

memory near x29 ([anon:stack_and_tls:11981]):

0000007c02d886d0 b400007c13c246d0 0000000001909705 .F..|………..    【SP = 0x0000007c02d886d0】

0000007c02d886e0 0000007c02d88700 6f2ab3b40fa2f8ef ….|………o  

0000007c02d886f0 0000007c02d88750 0000007ca133f8e0 P…|…..3.|…      【x29 = 0x0000007c02d886f0】

0000007c02d88700 0000000000000002 0000000000000000 …………….

0000007c02d88710 0000000000000415 0000000001909705 …………….

0000007c02d88720 0000000000000000 0000007c02d88808 …………|…

0000007c02d88730 b400007c13c67c00 0000000000000000 .|..|………..

0000007c02d88740 0000007c02d89000 6f2ab3b40fa2f8ef ….|………o

0000007c02d88750 0000007c02d88830 0000007ca796ee7c 0…|…|…|…

0000007c02d88760 0000007ca79f3dd8 0000007ca79edb80 .=..|…….|…

0000007c02d88770 0000007c02d89000 b400007c13c04680 ….|….F..|…

0000007c02d88780 0000000000000000 0000000000000002 …………….

0000007c02d88790 b400007c13c67c00 0000000000000000 .|..|………..

0000007c02d887a0 0000000000000000 b400007c13c7c100 …………|…

0000007c02d887b0 0000007c02d88830 0000007ca796d420 0…|… …|…

0000007c02d887c0 0000007c02d88830 0000007ca796d460 0…|…`…|…

memory near lr ([anon:stack_and_tls:11981]):

0000007c02d886d0 b400007c13c246d0 0000000001909705 .F..|………..

0000007c02d886e0 0000007c02d88700 6f2ab3b40fa2f8ef ….|………o

0000007c02d886f0 0000007c02d88750 0000007ca133f8e0 P…|…..3.|…

0000007c02d88700 0000000000000002 0000000000000000 …………….

0000007c02d88710 0000000000000415 0000000001909705 …………….

0000007c02d88720 0000000000000000 0000007c02d88808 …………|…

0000007c02d88730 b400007c13c67c00 0000000000000000 .|..|………..

0000007c02d88740 0000007c02d89000 6f2ab3b40fa2f8ef ….|………o

0000007c02d88750 0000007c02d88830 0000007ca796ee7c 0…|…|…|…

0000007c02d88760 0000007ca79f3dd8 0000007ca79edb80 .=..|…….|…

0000007c02d88770 0000007c02d89000 b400007c13c04680 ….|….F..|…

0000007c02d88780 0000000000000000 0000000000000002 …………….

0000007c02d88790 b400007c13c67c00 0000000000000000 .|..|………..

0000007c02d887a0 0000000000000000 b400007c13c7c100 …………|…

0000007c02d887b0 0000007c02d88830 0000007ca796d420 0…|… …|…

0000007c02d887c0 0000007c02d88830 0000007ca796d460 0…|…`…|…

memory near sp ([anon:stack_and_tls:11981]):

0000007c02d886b0 0000000000000018 0000007caae98c58 ……..X…|…

0000007c02d886c0 0000007c02d886f0 0000007c95de6754 ….|…Tg..|…

0000007c02d886d0 b400007c13c246d0 0000000001909705 .F..|………..

0000007c02d886e0 0000007c02d88700 6f2ab3b40fa2f8ef ….|………o

0000007c02d886f0 0000007c02d88750 0000007ca133f8e0 P…|…..3.|…

0000007c02d88700 0000000000000002 0000000000000000 …………….

0000007c02d88710 0000000000000415 0000000001909705 …………….

0000007c02d88720 0000000000000000 0000007c02d88808 …………|…

0000007c02d88730 b400007c13c67c00 0000000000000000 .|..|………..

0000007c02d88740 0000007c02d89000 6f2ab3b40fa2f8ef ….|………o

0000007c02d88750 0000007c02d88830 0000007ca796ee7c 0…|…|…|…

0000007c02d88760 0000007ca79f3dd8 0000007ca79edb80 .=..|…….|…

0000007c02d88770 0000007c02d89000 b400007c13c04680 ….|….F..|…

0000007c02d88780 0000000000000000 0000000000000002 …………….

0000007c02d88790 b400007c13c67c00 0000000000000000 .|..|………..

0000007c02d887a0 0000000000000000 b400007c13c7c100 …………|…

memory near pc ([anon:stack_and_tls:11981]):

0000007c02d886d0 b400007c13c246d0 0000000001909705 .F..|………..

0000007c02d886e0 0000007c02d88700 6f2ab3b40fa2f8ef ….|………o

0000007c02d886f0 0000007c02d88750 0000007ca133f8e0 P…|…..3.|…

0000007c02d88700 0000000000000002 0000000000000000 …………….

0000007c02d88710 0000000000000415 0000000001909705 …………….

0000007c02d88720 0000000000000000 0000007c02d88808 …………|…

0000007c02d88730 b400007c13c67c00 0000000000000000 .|..|………..

0000007c02d88740 0000007c02d89000 6f2ab3b40fa2f8ef ….|………o

0000007c02d88750 0000007c02d88830 0000007ca796ee7c 0…|…|…|…

0000007c02d88760 0000007ca79f3dd8 0000007ca79edb80 .=..|…….|…

0000007c02d88770 0000007c02d89000 b400007c13c04680 ….|….F..|…

0000007c02d88780 0000000000000000 0000000000000002 …………….

0000007c02d88790 b400007c13c67c00 0000000000000000 .|..|………..

0000007c02d887a0 0000000000000000 b400007c13c7c100 …………|…

0000007c02d887b0 0000007c02d88830 0000007ca796d420 0…|… …|…

0000007c02d887c0 0000007c02d88830 0000007ca796d460 0…|…`…|…

内存映射表

memory map (1146 entries):

0000005f'fabc7000-0000005f'fabc7fff r--         0      1000  /system/bin/mediaserver64

0000005f'fabc8000-0000005f'fabc9fff r-x      1000      2000  /system/bin/mediaserver64

0000005f'fabca000-0000005f'fabcafff r--      3000      1000  /system/bin/mediaserver64

0000007b'e79a3000-0000007b'e7d93fff ---         0    3f1000

0000007c'a120e000-0000007c'a128efff r--         0     81000  /system/lib64/libstagefright.so

0000007c'a128f000-0000007c'a13c0fff r-x     81000    132000  /system/lib64/libstagefright.so

0000007c'a13c1000-0000007c'a13cffff r--    1b3000      f000  /system/lib64/libstagefright.so

0000007c'a13d0000-0000007c'a13d1fff rw-    1c1000      2000  /system/lib64/libstagefright.so

0000007c'a787c000-0000007c'a78f4fff r--         0     79000  /system/lib64/libmediaplayerservice.so

0000007c'a78f5000-0000007c'a79ecfff r-x     79000     f8000  /system/lib64/libmediaplayerservice.so

0000007c'a79ed000-0000007c'a79f8fff r--    171000      c000  /system/lib64/libmediaplayerservice.so

0000007c'a79f9000-0000007c'a79f9fff rw-    17c000      1000  /system/lib64/libmediaplayerservice.so

FD信息

open files:

fd 0: /dev/null (unowned)

fd 1: /dev/null (unowned)

fd 2: /dev/null (unowned)

fd 3: socket:[62562] (unowned)

fd 4: /dev/binderfs/binder (unowned)

fd 5: /dev/binderfs/hwbinder (unowned)

fd 6: /sys/kernel/tracing/trace_marker (unowned)

fd 7: /dev/ashmem4945d9b6-db30-413c-88c5-e50674f154c7 (unowned)

fd 8: /dmabuf: (unowned)

fd 9: /dev/ashmem4945d9b6-db30-413c-88c5-e50674f154c7 (unowned)

fd 10: /storage/emulated/0/zapya/folder/华语音乐/IN-K&王忻辰&苏星婕 - 落日与晚风.mp3 (owned by unique_fd 0x7c13c7a498)

fd 11: /dev/ashmem4945d9b6-db30-413c-88c5-e50674f154c7 (unowned)

...

4

Coredump

 

前面 tombstone 文件内容,可以了解到它的信息很有限,当我们需要更多的内存信息时,它无法满足我们,这时 coredump 就尤为重要了,它可以按我们配置抓取相应的内存信息,关于 core 的介绍,见:

https://man7.org/linux/man-pages/man5/core.5.html

AOSP方法

# build/envsetup.sh# coredump_setup - enable core dumps globally for any process#                  that has the core-file-size limit set correctly## NOTE: You must call also coredump_enable for a specific process#       if its core-file-size limit is not set already.# NOTE: Core dumps are written to ramdisk; they will not survive a reboot!function coredump_setup(){    echo "Getting root...";    adb root;    adb wait-for-device;    echo "Remounting root partition read-write...";    adb shell mount -w -o remount -t rootfs rootfs;    sleep 1;    adb wait-for-device;    adb shell mkdir -p /cores;    adb shell mount -t tmpfs tmpfs /cores;    adb shell chmod 0777 /cores;    echo "Granting SELinux permission to dump in /cores...";    adb shell restorecon -R /cores;    echo "Set core pattern.";    adb shell 'echo /cores/core.%p > /proc/sys/kernel/core_pattern';    echo "Done."}# coredump_enable - enable core dumps for the specified process# $1 = PID of process (e.g., $(pid mediaserver))## NOTE: coredump_setup must have been called as well for a core#       dump to actually be generated.function coredump_enable(){    local PID=$1;    if [ -z "$PID" ]; then        printf "Expecting a PID!\n";        return;    fi;    echo "Setting core limit for $PID to infinite...";    adb shell /system/bin/ulimit -P $PID -c unlimited}

常用方法

给 system_server 配置 coredump 参数,由于目标进程的 coredump 生成的目录受 selinux 权限限制,因此这种方法配置抓 coredump 的方法要注意目标进程对哪些目录文件有读写的 selinux 权限,再配置相应的目录。

adb wait-for-deviceadb rootadb shell mkdir /data/coresadb shell chmod 777 /data/cores#adb shell setenforce 0adb shell restorecon -R /data/coresadb shell 'echo /data/cores/core.%e.%p > /proc/sys/kernel/core_pattern'adb shell 'system/bin/ulimit -P `pidof system_server` -c unlimited'#adb shell 'echo 2 > /proc/sys/fs/suid_dumpable'

注意:确定问题与selinux权限无关,可以通过adb shell setenforce 0关闭selinux权限

给 com.android.settings 配置抓取 coredump 的参数,由于前面的配置中 /data/cores 目录恢复 selinux 权限后如下:

drwxrwxrwx   2 root     root     u:object_r:system_data_file:s0          3452 2022-07-04 15:08 cores

我们知道 app 一定有权限对自身的 /data/data/$PACKAGE/ 目录下的文件具有读写权限,于是可以配置成如下参数:

adb wait-for-deviceadb rootadb shell mkdir /data/data/com.android.settings/coresadb shell chmod 777 /data/data/com.android.settings/coresadb shell restorecon -R /data/data/com.android.settings/coresadb shell 'echo /data/data/com.android.settings/cores/core.%e.%p > /proc/sys/kernel/core_pattern'adb shell 'system/bin/ulimit -P `pidof com.android.settings` -c unlimited'#adb shell 'echo 2 > /proc/sys/fs/suid_dumpable'

当我们在机器上验证对这个app进行kill -11模拟时

$ kill -11 `pidof com.android.settings`

$ ls /data/data/com.android.settings/cores/core.ndroid.settings.27946

参数说明

coredump_filter 进程默认值是0x23,只抓取:私有匿名/共享匿名/私有大尺寸页,需要抓全部内存信息则 adb shell 'echo 0x27 > /proc/$PID/coredump_filter' 即可。

/proc/$PID/coredump_filter

bit0: 私有匿名

bit1: 共享匿名

bit2: 有底层文件的私有映射

bit3: 有底层文件共享映射

bit4: ELF头

bit5: 私有大尺寸页

bit6: 共享大尺寸页

core_pattern 控制生成 core 的文件名,以及输出的 core 的位置。例如:

adb shell 'echo /data/cores/core.%p > /proc/sys/kernel/core_pattern'

/proc/sys/kernel/core_pattern

%p: 添加pid

%u: 添加当前uid

%g: 添加当前gid

%s: 添加导致产生core的信号

%t: 添加core文件生成时的unix时间

%h: 添加主机名

%e: 添加命令名

%E: 可执行文件的路径名,用斜杠(’/’)替换为感叹号(’!’)。

当程序调用了 seteuid()/setegid() 改变进程的有效用户或组,则在默认情况下系统不会为这些进程生成 core,此时你可能需要调整 suid_dumpable 参数进入调试模式或安全模式下进行。

/proc/sys/fs/suid_dumpable

0:默认模式

1:调试模式

2:安全模式

文件格式

core 文件也是ELF文件的一种,因此它的主体格式组成部分与 ELF 文件相

同,以案例讲解的 core 文件为例,它主要组成部分为 /proc/self/maps 下的VMA以及各个线程寄存器。其中寄存器信息存放在PT_NOTE,各VMA存放在 PT_LOAD,当被过滤掉的 VMA,它只有 Program Header 描述,没有对应的 segment。

 

离线调试

注:MTK 平台的 MINIDUMP 也是 coredump 的一种,它所保存的内存信息有限,core 的分析可以使用 GDB、lldb 等调试工具,如何使用这些调试工具,这里就不一一介绍。

$ ~/work/debug/gdb_arm64/gdb-12.1/output/bin/aarch64-linux-gdb

当我们没有符号表时,仅加载 core-file 也是可以使用的。

(gdb) core-file PROCESS_MINIDUMP

当我们有相应的符号表时,即可加载符号表目录

(gdb) set solib-search-path symbols/

或者

(gdb) set sysroot symbols/

(gdb) info sharedlibrary

显示所有共享库的地址范围

(gdb) info registers

显示当前线程的当前帧寄存器信息

(gdb) info locals

显示当前帧的局部变量

(gdb) info thread

显示有哪些线程

(gdb) thread 2

切换到2号线程上

(gdb) bt

显示当前线程的堆栈

(gdb) thread apply all [command]

例如打印所有线程堆栈

(gdb) thread apply all bt

让所有线程做同样的命令

(gdb) frame

显示当前帧信息

(gdb) frame 3

切换到#3帧

(gdb) print 或 (gdb) p

打印变量

(gdb) ptype 'android::AHandler'

查看某个class或struct的数据结构

(gdb) ptype /o 'android::AHandler'

查看数据类型占多少字节

(gdb) set print pretty on

格式化输出

(gdb) set log on

保存gdb输出的结果

(gdb) x /gx 0x7c02d886f0

读取地址0x7c02d886f0内存内容,其中输出格式如下:

o(octal), x(hex), d(decimal),

u(unsigned decimal), t(binary),

f(float), a(address), i(instruction),

c(char), s(string),

z(hex, zero padded on the left).

(gdb) disassemble 0x0000007c95de6708

(gdb) disassemble 'android::AMessage::setTarget'

显示函数汇编信息

5

内存检测机制

 

ASAN

在 Android 11 之后的 AOSP master 中,弃用了 arm64 上的平台开发 ASAN,改为使用 HWASAN,AddressSanitizer (ASAN) 是一种基于编译器的快速检测工具,用于检测内存错误。

Stack and heap buffer overflow/underflow

堆栈和堆缓冲区上溢/下溢

Heap use after free

使用已释放的内存

Stack use outside scope

超出栈范围

Double free/wild free

多次释放内存/错误释放

HWASAN

HWASan 仅适用于 Android 10 及更高版本,且只能用于 AArch64 硬件,具备 ASAN 同样的检测能力,在 Linux-4.14 版本以上支持 tagged-pointers才能使用。

编译Android版本时带入环境变量如下:

$ export SANITIZE_TARGET=hwaddress

跳过某个模块,在相应模块下的Android.bp文件下添加内容如下:

sanitize: {

hwaddress: false,

address: false,

},

Android.mk则添加内容如下:

LOCAL_NOSANITIZE := hwaddress

APP构建支持HWASAN则在Application.mk下添加内容如下:

APP_STL := c++_shared

APP_CFLAGS := -fsanitize=hwaddress -fno-omit-frame-pointer

APP_LDFLAGS := -fsanitize=hwaddress

MTE

最新 Android S 上引入的 ARM Memory Tagging Extension (MTE),MTE 的原理和 HWASan 类似,最大的区别在于 HWASan 需要重新编译,在所有内存访问前插桩相应的检测函数来实现,而 MTE 则在读写指令内部完成检测,完全由硬件支持。

更多内容转至掘金大佬-芦半山:

https://juejin.cn/post/6844904111570157575

https://juejin.cn/post/7013595058125406238

6

野指针的危害

当所指向的对象被释放或者收回,但是对该指针没有作任何的修改,以至于该指针仍旧指向已经回收的内存地址,此情况下该指针便称 Wild pointer (野指针),如果这个野指针所指向的内存被分配给其它指针,而这个野指针仍在使用,程序将难以预测。

#include <stdio.h>class A {public:    virtual ~A() = default;    virtual void foo() {        printf("A:%ld\n", a);    }    long a;};class B {public:    virtual ~B() = default;    virtual void foo() {        printf("B:%ld\n", b);    }    long b;};int main(int /*argc*/, char** /*argv[]*/) {    A *a = new A();    A *a_bak = a;    a->a = 1000L;    printf("A ptr = %p\n", a);    delete a;   //  此时a指针已经被free掉,那么指针a_bak是个野指针    B *b = new B();    printf("B ptr = %p\n", b);    b->b = 2000L;    b->foo();    a_bak->foo();   //  此处程序会运行怎么结果    delete b;    return 0;}

以上程序会怎么输出,由于 B 与 A 的数据结构大小一致,运行在同一个线程,极大可能会分配刚释放的指针地址,因此这个程序最后指针 b 与指针 a_bak 是同一个。

# ./data/Tester64                                                                                                  

A ptr = 0xb400007690205010

B ptr = 0xb400007690205010

B:2000

B:2000

像以上这个结果,那程序如下改写,那么可怕的事情就会出现,下面这个程序会报什么错。

#include <stdio.h>class A {public:    long bad;    long a;};class B {public:    virtual ~B() = default;    virtual void foo() {        b = 2000L;        printf("B:%ld\n", b);    }    long b;};int main(int /*argc*/, char** /*argv[]*/) {    A *a = new A();    A *a_bak = a;    printf("A ptr = %p\n", a);    delete a;    B *b = new B();    printf("B ptr = %p\n", b);    a_bak->bad = 0x20L;    b->foo();    delete b;    return 0;}

上面这个程序在27行处,bad 会将 B 的虚函数表破坏,导致28和29行在虚函数表上寻找 foo 函数与析构函数地址时发段错误。

Timestamp: 2022-07-06 14:47:50.925654058+0800

Process uptime: 0s

Cmdline: ./data/Tester64

pid: 12652, tid: 12652, name: Tester64  >>> ./data/Tester64 <<<

uid: 0

tagged_addr_ctrl: 0000000000000001

signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x28

Cause: null pointer dereference

x0  b40000762f005010  x1  b40000762f01b000  x2  0000000000000007  x3  ffffffffffffffff

x4  ffffffffffffffff  x5  0000000040100401  x6  b40000762f01b006  x7  3637303030303462

x8  0000000000000020  x9  a454ef76eb4317d3  x10 0000000000004001  x11 0000000000000000

x12 0000000000000000  x13 0000000000000002  x14 0000000000000010  x15 0000000000000010

x16 000000762f5a0c58  x17 000000762f5910d4  x18 000000763745c000  x19 b40000762f005010

x20 b40000762f005010  x21 0000007fe173d378  x22 0000000000000001  x23 0000000000000000

x24 0000000000000000  x25 0000000000000000  x26 0000000000000000  x27 0000000000000000

x28 0000000000000000  x29 0000007fe173d2e0

lr  0000005ac14600c8  sp  0000007fe173d2e0  pc  0000005ac14600d0  pst 0000000060001000

backtrace:

#00 pc 00000000000010d0  /data/Tester64 (main+124)

#01 pc 000000000008436c  /apex/com.android.runtime/lib64/bionic/libc.so (__libc_init+100)

程序会出错还好,如果程序不会出错,仍继续运行,那么这个程序将变得很可怕,因为你不知道程序将会怎么运行,像下面这个程序,刻意改写控制程序往其它方向运行。

#include <stdio.h>class A {public:    void *bad;    long a;};class B {public:    virtual ~B() {        printf("delete B\n");    };    virtual void foo() {        b = 2000L;        printf("B:%ld\n", b);    }    long b;};void func1() {    printf("Hello !!\n");}void func2() {    printf("GoGoGo !!\n");}int main(int /*argc*/, char** /*argv[]*/) {    A *a = new A();    A *a_bak = a;    printf("A ptr = %p\n", a);    delete a;    B *b = new B();    printf("B ptr = %p\n", b);    long *data = new long[4] {0x0L, (long)func2, (long)func1, 0x0L};    a_bak->bad = data;    printf("Test .. \n");    b->foo();    delete b;    printf("Done.\n");    return 0;}

# ./data/Tester64

A ptr = 0xb400007ce9605010

B ptr = 0xb400007ce9605010

Test ..

Hello !!

GoGoGo !!

Done.

7

数组越界的危害

比起前面的野指针,数组越界大多数情况都能被 HWASAN 等内存检测发现,当然前面的野指针情况也是能被检测发现的,数组越界往往会出现某个对象的内存前半部分被污染,而后半部分数据正常的情况。

#include <stdio.h>class A {public:    long a = 0x55AA;    long b = 0xDEAD;};int main(int /*argc*/, char** /*argv[]*/) {    long *b = new long[2] {0x0L, 0x1L};    A *a = new A();    printf("A:%p\n", a);    printf("B:%p\n", b);    b[2] = 0xDEAD;    printf("B2:%p\n", &b[2]);    printf("0x%lx-0x%lx\n", a->a, a->b);    return 0;}

因为 b 的数据大小与对象 a 的数据大小相同,因此在这个程序刚启动分配指针地址时,基本会连在一块,因此 b[2] 越界行为将对象 a 的内容破坏,往往程序运行了很久,内存碎片化增加,就不知道 b[2] 越界操作会破坏哪个对象的内存。

# ./data/Tester64

A:0xb400007fa7c05020

B:0xb400007fa7c05010

B2:0xb400007fa7c05020

0xdead-0xdead

8

机器码翻译

本文案例中的 tombstone 文件 PC 跑飞,没有落在 text 段地址上,这里换一份 tombstone 方便说明,我们可以通过编译的方法生成对应的 ELF 文件,即可用 objdump 得到对应的汇编。

signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x40

Cause: null pointer dereference

x0  0000000000000020  x1  b400007bf95f0e00  x2  63692f6863726165  x3  0000000000000008

x4  b400007bf95f0e74  x5  b400007bf8ea36f4  x6  656863732f676e69  x7  732f7269645f616d

x8  db4cf552ad46f717  x9  0000000000000001  x10 00000000000349e0  x11 0000007bc0000000

x12 0000000000000000  x13 0000000000034960  x14 b400007b58d7c4c0  x15 00000000374d2457

x16 0000007ca3543d50  x17 0000007ca35338ec  x18 0000007b38c74000  x19 0000000000000020

x20 0000000000000000  x21 0000007b548e9000  x22 0000007b548e9000  x23 b400007bf946ffd0

x24 0000007b548e76c0  x25 0000007b548e9000  x26 0000007b548e7b30  x27 0000007b548e7b18

x28 0000007b548e7a10  x29 0000007b548e7410

lr  0000007a8e4e7980  sp  0000007b548e73e0  pc  0000007a8e4d0e00  pst 0000000060001000

memory near pc (/apex/com.android.appsearch/lib64/libicing.so):

0000007a8e4d0de0 a90557f6f90023f7 9100c3fda9064ff4 .#…W…O……

0000007a8e4d0df0 f94016c8d53bd056 f81f83a8aa0003f3 V.;…@………

0000007a8e4d0e00 350001c839408008 91181c42b0fffde2 ..@9…5….B…

0000007a8e4d0e10 2a1f03e1910023e0 910023f452808ee3 .#…..…R.#..

0000007a8e4d0e20 910022809400d695 912b742190fffde1 ….."……!t+.

编写汇编文件 code.S,内容如下:

.inst 0xf90023f7.inst 0xa90557f6.inst 0xa9064ff4.inst 0x9100c3fd.inst 0xd53bd056.inst 0xf94016c8.inst 0xaa0003f3.inst 0xf81f83a8.inst 0x39408008.inst 0x350001c8.inst 0xb0fffde2.inst 0x91181c42.inst 0x910023e0.inst 0x2a1f03e1.inst 0x52808ee3.inst 0x910023f4.inst 0x9400d695.inst 0x91002280.inst 0x90fffde1.inst 0x912b7421

编译:aarch64-linux-android-as code.S -o code.o

反汇编:aarch64-linux-android-objdump -d code.o

code.o:     file format elf64-littleaarch64

Disassembly of section .text:

0000000000000000 <.text>:

0:        f90023f7         str        x23, [sp, #64]

4:        a90557f6         stp        x22, x21, [sp, #80]

8:        a9064ff4         stp        x20, x19, [sp, #96]

c:        9100c3fd         add        x29, sp, #0x30

10:        d53bd056         mrs        x22, tpidr_el0

14:        f94016c8         ldr        x8, [x22, #40]

18:        aa0003f3         mov        x19, x0

1c:        f81f83a8         stur        x8, [x29, #-8]

 20:        39408008         ldrb        w8, [x0, #32]

24:        350001c8         cbnz        w8, 5c <.text+0x5c>

28:        b0fffde2         adrp        x2, fffffffffffbd000 <.text+0xfffffffffffbd000>

2c:        91181c42         add        x2, x2, #0x607

30:        910023e0         add        x0, sp, #0x8

34:        2a1f03e1         mov        w1, wzr

38:        52808ee3         mov        w3, #0x477                         // #1143

3c:        910023f4         add        x20, sp, #0x8

40:        9400d695         bl        35a94 <.text+0x35a94>

44:        91002280         add        x0, x20, #0x8

48:        90fffde1         adrp        x1, fffffffffffbc000 <.text+0xfffffffffffbc000>

4c:        912b7421         add        x1, x1, #0xadd

由于x0=0x0000000000000020,因此在指令0x39408008( ldrb        w8, [x0, #32]),会发生读取内存0x40的段错误。

8

汇编翻译

有些时候我们可能需要手动修改 ELF 文件的机器码达到一些调试的目的,这时候我们就需要知道汇编对应的机器码,去查手册也比较麻烦,这时我们直接编写汇编文件即可。

编写汇编文件 code_asm.S,内容如下:

str   x30, [sp, #-16]!str   x29, [sp, #-16]!

编译:aarch64-linux-android-as code_asm.S -o code_asm.o

反汇编:aarch64-linux-android-objdump -d code_asm.o

code_asm.o:     file format elf64-littleaarch64

Disassembly of section .text:

0000000000000000 <.text>:

0:        f81f0ffe         str        x30, [sp, #-16]!

4:        f81f0ffd         str        x29, [sp, #-16]!

9

案例讲解

错误日志

Timestamp: 2022-06-07 01:53:32.033409857+0800

Process uptime: 0s

Cmdline: mediaserver64

pid: 1139, tid: 11981, name: NPDecoder  >>> mediaserver64 <<<

uid: 1013

tagged_addr_ctrl: 0000000000000001

signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x7c02d886f0

x0  79748c5e568e2ddc  x1  0000007ca13c3618  x2  0000000000000000  x3  0000007ca1291000

x4  0000000001909705  x5  0000000000000000  x6  0000007c02d88808  x7  b60625655bf0252f

x8  0000000000000080  x9  0000007ca126fed7  x10 0000000000000006  x11 0000007bfd0a81fc

x12 9ef8a95ca9649dbe  x13 e44782d5ac38720e  x14 0000007bfd0a8030  x15 0000001e56307b5c

x16 0000007c95dfdb70  x17 0000007c9844f118  x18 0000007bfaa28000  x19 b400007c13c246d0

x20 0000007c02d88730  x21 b400007c13c67c00  x22 0000000000000415  x23 0000007c02d89000

x24 0000000000000002  x25 b400007c13c246d0  x26 b400007c13c67c00  x27 0000007c02d89000

x28 0000007ca13c2c28  x29 0000007c02d886f0

lr  0000007c02d886f0  sp  0000007c02d886d0  pc  0000007c02d886f0  pst 0000000080001000

backtrace:

#00 pc 00000000000f86f0  [anon:stack_and_tls:11981]

分析

直接原因

0000007c'02c90000-0000007c'02d8bfff rw-         0     fc000  [anon:stack_and_tls:11981]

memory near pc ([anon:stack_and_tls:11981]):

0000007c02d886d0 b400007c13c246d0 0000000001909705 .F..|………..

0000007c02d886e0 0000007c02d88700 6f2ab3b40fa2f8ef ….|………o

0000007c02d886f0 0000007c02d88750 0000007ca133f8e0 P…|…..3.|…

0000007c02d88700 0000000000000002 0000000000000000 …………….

0000007c02d88710 0000000000000415 0000000001909705 …………….

code.o:     file format elf64-littleaarch64

Disassembly of section .text:

0000000000000000 <.text>:

  0:        02d886f0         .inst        0x02d886f0 ; undefined

4:        0000007c         udf        #124

从 tombstone 的 backtrace 信息上看,很明显是PC跑飞了,跑到线程栈地址空间下,由于栈内存地址不可运行,因此被拒绝访问而出错,如果有x的权限那么也是错误,会发生 SIGILL 指令错误。

栈回溯

从 tombstone 中的 x29 和 SP 寄存器附近的内存中进行栈回溯,由于大小受限,往往无法回溯到栈底,无法完全证明程序就是经过这几个函数。

memory near x29 ([anon:stack_and_tls:11981]):

0000007c02d886d0 b400007c13c246d0 0000000001909705 .F..|………..    【SP = 0x0000007c02d886d0】

0000007c02d886e0 0000007c02d88700 6f2ab3b40fa2f8ef ….|………o  

0000007c02d886f0 0000007c02d88750 0000007ca133f8e0 P…|…..3.|…      【x29 = 0x0000007c02d886f0】

0000007c02d88700 0000000000000002 0000000000000000 …………….

0000007c02d88710 0000000000000415 0000000001909705 …………….

0000007c02d88720 0000000000000000 0000007c02d88808 …………|…

0000007c02d88730 b400007c13c67c00 0000000000000000 .|..|………..

0000007c02d88740 0000007c02d89000 6f2ab3b40fa2f8ef ….|………o

0000007c02d88750 0000007c02d88830 0000007ca796ee7c 0…|…|…|…

0000007c02d88760 0000007ca79f3dd8 0000007ca79edb80 .=..|…….|…

0000007c02d88770 0000007c02d89000 b400007c13c04680 ….|….F..|…

0000007c02d88780 0000000000000000 0000000000000002 …………….

0000007c02d88790 b400007c13c67c00 0000000000000000 .|..|………..

0000007c02d887a0 0000000000000000 b400007c13c7c100 …………|…

0000007c02d887b0 0000007c02d88830 0000007ca796d420 0…|… …|…

0000007c02d887c0 0000007c02d88830 0000007ca796d460 0…|…`…|…

这里我们通过 FP(x29) 回溯,如果这个 x29 是有效的,那么 x29 存放的是上一个 Caller 的 FP 寄存器,x29+0x8 的地址存放上一个 Caller 的 LR 寄存器。

0x0000007ca133f8e0 地址落在 /system/lib64/libstagefright.so 的 TEXT 段上。

0000007c'a120e000-0000007c'a128efff r--         0     81000  /system/lib64/libstagefright.so

 0000007c'a128f000-0000007c'a13c0fff r-x     81000    132000  /system/lib64/libstagefright.so

0000007c'a13c1000-0000007c'a13cffff r--    1b3000      f000  /system/lib64/libstagefright.so

0000007c'a13d0000-0000007c'a13d1fff rw-    1c1000      2000  /system/lib64/libstagefright.so

[14] .text             PROGBITS         0000000000081000  00081000

000000000012bed8  0000000000000000  AX       0     0     4096

Funcation_Offset  =  PC - TEXT_LOAD + .text_offset

(gdb) p /x 0x0000007ca133f8e0-0x7ca128f000+0x81000

$1 = 0x1318e0

(gdb) x /2i 0x1318e0-0x4

0x1318dc <_ZN7android10MediaCodec16queueInputBufferEmmmljPNS_7AStringE+200>:        bl        0x1ad390 <_ZN7android8AMessageC1EjRKNS_2spIKNS_8AHandlerEEE@plt>

0x1318e0 <_ZN7android10MediaCodec16queueInputBufferEmmmljPNS_7AStringE+204>:        eor        x8, x29, x25

(gdb) p _ZN7android10MediaCodec16queueInputBufferEmmmljPNS_7AStringE+200

$2 = (android::status_t (*)(struct android::MediaCodec * const, size_t, size_t, size_t, int64_t, uint32_t,

struct android::AString *)) 0x1318dc <android::MediaCodec::queueInputBuffer(unsigned long, unsigned long, unsigned long, long, unsigned int, android::AString*)+200>

因此0x0000007ca133f8e0所在的函数是android::MediaCodec::queueInputBuffer。

一般我们直接 PC 减去库文件的起始地址即可得到函数的偏移地址。

即:0x0000007ca133f8e0 - 0x0000007ca120e000

最后这份 tombstone 只能通过 FP 推导出这两个函数,是否正确待取证,这也体现了 tombstone 的局限性,此时我们希望能完整的推导,并取证栈是完整性,需要使用到 coredump 文件进行。

FP

LR

函数名

0x0000007c02d886f0

0x0000007c02d88750

0x0000007ca133f8e0

android::MediaCodec::queueInputBuffer

0x0000007c02d88750

0x0000007c02d88830

0x0000007ca796ee7c

android::NuPlayer::Decoder::onInputBufferFetched

MTK 的 aee 会将保存一份 minidump ,将 minidump 装载到gdb上回溯。

core-file PROCESS_MINIDUMP

(gdb) bt

#0  0x0000007c02d886f0 in ?? ()

由此可见 GDB 也无法正确回溯这个栈,我们重复前面的步骤在完整的栈上进行计算,可以得到

当前FP

Caller FP

Caller LR

Caller 函数名

0x7c02d886f0

0x0000007c02d88750

0x0000007ca133f8e0

android::MediaCodec::queueInputBuffer

0x7c02d88750

0x0000007c02d88830

0x0000007ca796ee7c

android::NuPlayer::Decoder::onInputBufferFetched

0x7c02d88830

0x0000007c02d888a0

0x0000007ca796c984

android::NuPlayer::Decoder::doRequestBuffers

0x7c02d888a0

0x0000007c02d88920

0x0000007ca7963d48

android::NuPlayer::DecoderBase::onRequestInputBuffers

0x7c02d88920

0x0000007c02d88980

0x0000007ca79717bc

android::NuPlayer::Decoder::handleAnInputBuffer

0x7c02d88980

0x0000007c02d88a20

0x0000007ca7969328

android::NuPlayer::Decoder::onMessageReceived

0x7c02d88a20

0x0000007c02d88a70

0x0000007c95de2874

android::AHandler::deliverMessage

0x7c02d88a70

0x0000007c02d88ad0

0x0000007c95de8608

android::AMessage::deliver

0x7c02d88ad0

0x0000007c02d88b30

0x0000007c95de3bbc

android::ALooper::loop

0x7c02d88b30

0x0000007c02d88ba0

0x0000007ca105907c

android::Thread::_threadLoop

0x7c02d88ba0

0x0000007c02d88c10

0x0000007ca105886c

thread_data_t::trampoline

0x7c02d88c10

0x0000007c02d88c50

0x0000007caaf3be74

__pthread_start

0x7c02d88c50

0x0000007c02d88c80

0x0000007caaedb830

__start_thread

这个线程栈内存能被回溯到 __start_thread 函数,基本可以确定这个栈是可信的,并且最后的 FP 地址存放了 Caller FP 与 Caller LR,并且从汇编的指令流程上是吻合的,可以确定最后的 FP 应该是某个函数的栈顶,记这个函数为 A 函数。

A 函数是谁?

(gdb) x /12gx 0x7c02d886d0

0x7c02d886d0:   0xb400007c13c246d0  0x0000000001909705        /SP

0x7c02d886e0:   0x0000007c02d88700  0x6f2ab3b40fa2f8ef

此处是 A 函数的栈。

0x7c02d886f0:   0x0000007c02d88750  0x0000007ca133f8e0          // FP

0x7c02d88700:   0x0000000000000002  0x0000000000000000

0x7c02d88710:   0x0000000000000415  0x0000000001909705

0x7c02d88720:   0x0000000000000000  0x0000007c02d88808

当 A 函数运行完成后返回 LR 地址 0x0000007ca133f8e0   (android::MediaCodec::queueInputBuffer)

注:以下“_ZN7android10MediaCodec16queueInputBufferEmmmljPNS_7AStringE” 用 "android::MediaCodec::queueInputBuffer" 替代

(gdb) disassemble 0x0000007ca133f8e0-0x20,+0x30

Dump of assembler code from 0x7ca133f8c0 to 0x7ca133f8f0:

0x0000007ca133f8c0 <android::MediaCodec::queueInputBuffer+172>: mov x1, sp

0x0000007ca133f8c4 <android::MediaCodec::queueInputBuffer+176>: mov x0, x26

0x0000007ca133f8c8 <android::MediaCodec::queueInputBuffer+180>: bl 0x7ca13bb130 <_ZNK7android7RefBase9incStrongEPKv@plt>

0x0000007ca133f8cc <android::MediaCodec::queueInputBuffer+184>: mov x2, sp

0x0000007ca133f8d0 <android::MediaCodec::queueInputBuffer+188>: mov x0, x25

0x0000007ca133f8d4 <android::MediaCodec::queueInputBuffer+192>: mov w1, #0x6549 // #25929

0x0000007ca133f8d8 <android::MediaCodec::queueInputBuffer+196>: movk w1, #0x7175, lsl #16

0x0000007ca133f8dc <android::MediaCodec::queueInputBuffer+200>: bl 0x7ca13bb390 <_ZN7android8AMessageC1EjRKNS_2spIKNS_8AHandlerEEE@plt>

0x0000007ca133f8e0 <android::MediaCodec::queueInputBuffer+204>: eor x8, x29, x25

0x0000007ca133f8e4 <android::MediaCodec::queueInputBuffer+208>: cmp x8, #0xfff

0x0000007ca133f8e8 <android::MediaCodec::queueInputBuffer+212>: str x25, [sp, #8]

0x0000007ca133f8ec <android::MediaCodec::queueInputBuffer+216>: b.hi 0x7ca133f8f4 <_ZN7android10MediaCodec16queueInputBufferEmmmljPNS_7AStringE+224> // b.pmore

End of assembler dump.

这个 A 函数是 android::AMessage::AMessage(unsigned int, android::sp<android::AHandler const> const&)?

2523status_t MediaCodec::queueInputBuffer( 2524        size_t index, 2525        size_t offset, 2526        size_t size, 2527        int64_t presentationTimeUs, 2528        uint32_t flags, 2529        AString *errorDetailMsg) { 2530    if (errorDetailMsg != NULL) { 2531        errorDetailMsg->clear(); 2532    } 2533 2534    sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this); 2535    msg->setSize("index", index); 2536    msg->setSize("offset", offset); 2537    msg->setSize("size", size); 2538    msg->setInt64("timeUs", presentationTimeUs); 2539    msg->setInt32("flags", flags); 2540    msg->setPointer("errorDetailMsg", errorDetailMsg); 2541 2542    sp<AMessage> response; 2543    return PostAndAwaitResponse(msg, &response); 2544}

A 函数会是 AMessage 的构造方法吗?

(gdb) disassemble 0x7ca13bb390

Dump of assembler code for function _ZN7android8AMessageC1EjRKNS_2spIKNS_8AHandlerEEE@plt:

0x0000007ca13bb390 <+0>:        adrp        x16, 0x7ca13cd000

0x0000007ca13bb394 <+4>:        ldr        x17, [x16, #992]

0x0000007ca13bb398 <+8>:        add        x16, x16, #0x3e0

0x0000007ca13bb39c <+12>:        br        x17

End of assembler dump.

(gdb) x /gx 0x7ca13cd000+0x3e0

0x7ca13cd3e0 <_ZN7android8AMessageC1EjRKNS_2spIKNS_8AHandlerEEE@got.plt>:        0x0000007c95de66a8

(gdb) disassemble 0x0000007c95de66a8

Dump of assembler code for function _ZN7android8AMessageC2EjRKNS_2spIKNS_8AHandlerEEE:

  0x0000007c95de66a8 <+0>:        stp        x29, x30, [sp, #-48]!

  0x0000007c95de66ac <+4>:        str        x21, [sp, #16]

  0x0000007c95de66b0 <+8>:        stp        x20, x19, [sp, #32]

  0x0000007c95de66b4 <+12>:        mov        x29, sp

0x0000007c95de66b8 <+16>:        mov        x19, x2

0x0000007c95de66bc <+20>:        mov        w20, w1

0x0000007c95de66c0 <+24>:        mov        x21, x0

0x0000007c95de66c4 <+28>:        bl        0x7c95dfb490 <_ZN7android7RefBaseC2Ev@plt>

0x0000007c95de66c8 <+32>:        movi        v0.2d, #0x0

0x0000007c95de66cc <+36>:        adrp        x8, 0x7c95dfd000 <__dso_handle_const>

0x0000007c95de66d0 <+40>:        ldr        x8, [x8, #2744]

0x0000007c95de66d4 <+44>:        str        w20, [x21, #16]

0x0000007c95de66d8 <+48>:        stur        q0, [x21, #24]

0x0000007c95de66dc <+52>:        add        x8, x8, #0x10

0x0000007c95de66e0 <+56>:        stur        q0, [x21, #40]

0x0000007c95de66e4 <+60>:        mov        x0, x21

0x0000007c95de66e8 <+64>:        mov        x1, x19

0x0000007c95de66ec <+68>:        stur        q0, [x21, #56]

0x0000007c95de66f0 <+72>:        str        x8, [x21]

0x0000007c95de66f4 <+76>:        str        xzr, [x21, #72]

0x0000007c95de66f8 <+80>:        ldp        x20, x19, [sp, #32]

0x0000007c95de66fc <+84>:        ldr        x21, [sp, #16]

0x0000007c95de6700 <+88>:        ldp        x29, x30, [sp], #48

0x0000007c95de6704 <+92>:        b        0x7c95dfb950 <_ZN7android8AMessage9setTargetERKNS_2spIKNS_8AHandlerEEE@plt>

End of assembler dump.

这个函数的栈大小为0x30,并且 FP=SP,如果A函数是 AMessage 的构造函数,那么在该函数未退栈时 FP=SP 不成立。

继续往前看栈上内存信息:

0x7c02d88600:   0xb400007c13c24bd0  0x6f2ab3b40fa2f8ef

0x7c02d88610:   0x0000007c02d88660  0x0000007c95de6510

0x7c02d88620:   0x0000007c02d89000  0x6f2ab3b40fa2f8ef

0x7c02d88630:   0x0000007c02d88670  0x0000007caae98c58

0x7c02d88640:   0x0000007c02d886b0  0x0000007caae9ce40

0x7c02d88650:   0x0000000000000005  0x6f2ab3b40fa2f8ef

0x7c02d88660:   0x0000007c02d886a0  0x0000007caae98c58

0x7c02d88670:   0x0000000000000002  0x0000000000000000

0x7c02d88680:   0x0000000000000415  0xb400007c13c246d0

0x7c02d88690:   0x0000000071756549  0x0000000000000018

0x7c02d886a0:   0x0000007c02d886c0  0x0000007c9dccfb74  operator new(unsigned long)

0x7c02d886b0:   0x0000000000000018  0x0000007caae98c58

0x7c02d886c0:   0x0000007c02d886f0  0x0000007c95de6754

0x7c02d886d0:   0xb400007c13c246d0  0x0000000001909705        /SP

0x7c02d886e0:   0x0000007c02d88700  0x6f2ab3b40fa2f8ef

此处是 A 函数的栈。

0x7c02d886f0:   0x0000007c02d88750  0x0000007ca133f8e0          // FP

0x7c02d88700:   0x0000000000000002  0x0000000000000000

0x7c02d88710:   0x0000000000000415  0x0000000001909705

0x7c02d88720:   0x0000000000000000  0x0000007c02d88808

地址 0x7c02d886c0 存放的内存正好指向 A 函数 FP 地址,因此这里很可能是 A 函数的下一个函数的栈

函数栈{x29,x30} = {0x0000007c02d886f0,0x0000007c95de6754}

注:以下将字符串"_ZN7android8AMessage9setTargetERKNS_2spIKNS_8AHandlerEEE"更替为"android::AMessage::setTarget"

(gdb) disassemble 0x0000007c95de6754-0x20,+0x40

Dump of assembler code from 0x7c95de6734 to 0x7c95de6774:

0x0000007c95de6734 <android::AMessage::setTarget+44>:        cbz        x21, 0x7c95de6880 <android::AMessage::setTarget+376>

0x0000007c95de6738 <android::AMessage::setTarget+48>:        mov        x20, x1

0x0000007c95de673c <android::AMessage::setTarget+52>:        ldr        x1, [x21]

0x0000007c95de6740 <android::AMessage::setTarget+56>:        mov        x0, #0x2ddc                // #11740

0x0000007c95de6744 <android::AMessage::setTarget+60>:        movk        x0, #0x568e, lsl #16

0x0000007c95de6748 <android::AMessage::setTarget+64>:        movk        x0, #0x8c5e, lsl #32

0x0000007c95de674c <android::AMessage::setTarget+68>:        movk        x0, #0x7974, lsl #48

  0x0000007c95de6750 <android::AMessage::setTarget+72>:        bl        0x7c95dfb370 <__cfi_slowpath@plt>

0x0000007c95de6754 <android::AMessage::setTarget+76>:        ldr        w8, [x21, #16]

0x0000007c95de6758 <android::AMessage::setTarget+80>:        str        w8, [x19, #20]

0x0000007c95de675c <android::AMessage::setTarget+84>:        ldr        x21, [x20]

0x0000007c95de6760 <android::AMessage::setTarget+88>:        ldr        x1, [x21]

0x0000007c95de6764 <android::AMessage::setTarget+92>:        mov        x0, #0x2ddc                // #11740

0x0000007c95de6768 <android::AMessage::setTarget+96>:        movk        x0, #0x568e, lsl #16

0x0000007c95de676c <android::AMessage::setTarget+100>:        movk        x0, #0x8c5e, lsl #32

0x0000007c95de6770 <android::AMessage::setTarget+104>:        movk        x0, #0x7974, lsl #48

End of assembler dump.

(gdb) p _ZN7android8AMessage9setTargetERKNS_2spIKNS_8AHandlerEEE+72

$64 = (void (*)(android::AMessage * const, const android::sp<android::AHandler const> &)) 0x7c95de6750 <android::AMessage::setTarget(android::sp<android::AHandler const> const&)+72>

A函数会是android::AMessage::setTarget?

(gdb) disassemble 0x0000007c95de6708,+0x30

Dump of assembler code from 0x7c95de6708 to 0x7c95de6738:

  0x0000007c95de6708 <android::AMessage::setTarget+0>:        sub        sp, sp, #0x60

0x0000007c95de670c <android::AMessage::setTarget+4>:        stp        x29, x30, [sp, #32]

0x0000007c95de6710 <android::AMessage::setTarget+8>:        stp        x24, x23, [sp, #48]

0x0000007c95de6714 <android::AMessage::setTarget+12>:        stp        x22, x21, [sp, #64]

0x0000007c95de6718 <android::AMessage::setTarget+16>:        stp        x20, x19, [sp, #80]

  0x0000007c95de671c <android::AMessage::setTarget+20>:        add        x29, sp, #0x20

0x0000007c95de6720 <android::AMessage::setTarget+24>:        mrs        x23, tpidr_el0

0x0000007c95de6724 <android::AMessage::setTarget+28>:        ldr        x8, [x23, #40]

0x0000007c95de6728 <android::AMessage::setTarget+32>:        stur        x8, [x29, #-8]

0x0000007c95de672c <android::AMessage::setTarget+36>:        ldr        x21, [x1]

0x0000007c95de6730 <android::AMessage::setTarget+40>:        mov        x19, x0

0x0000007c95de6734 <android::AMessage::setTarget+44>:        cbz        x21, 0x7c95de6880 <android::AMessage::setTarget+376>

从汇编看函数栈正是0x60与A函数的栈相等。

回去再看看 AMessage::AMessage 的汇编,可以发现它是退栈后跳转到 android::AMessage::setTarget

0x0000007c95de6700 <+88>:        ldp        x29, x30, [sp], #48

 0x0000007c95de6704 <+92>:        b        0x7c95dfb950 <_ZN7android8AMessage9setTargetERKNS_2spIKNS_8AHandlerEEE@plt>

x0 79748c5e568e2ddc x1 0000007ca13c3618 x2 0000000000000000 x3 0000007ca1291000
x4 0000000001909705 x5 0000000000000000 x6 0000007c02d88808 x7 b60625655bf0252f
x8 0000000000000080 x9 0000007ca126fed7 x10 0000000000000006 x11 0000007bfd0a81fc
x12 9ef8a95ca9649dbe x13 e44782d5ac38720e x14 0000007bfd0a8030 x15 0000001e56307b5c
x16 0000007c95dfdb70 x17 0000007c9844f118 x18 0000007bfaa28000 x19 b400007c13c246d0
x20 0000007c02d88730 x21 b400007c13c67c00 x22 0000000000000415 x23 0000007c02d89000
x24 0000000000000002 x25 b400007c13c246d0 x26 b400007c13c67c00 x27 0000007c02d89000
x28 0000007ca13c2c28 x29 0000007c02d886f0
lr 0000007c02d886f0 sp 0000007c02d886d0 pc 0000007c02d886f0 pst 0000000080001000

再回看 setTarget 汇编刚好 x0=0x79748c5e568e2ddc

0x0000007c95de6740 <android::AMessage::setTarget+56>:        mov        x0, #0x2ddc                        // #11740

0x0000007c95de6744 <android::AMessage::setTarget+60>:        movk        x0, #0x568e, lsl #16

0x0000007c95de6748 <android::AMessage::setTarget+64>:        movk        x0, #0x8c5e, lsl #32

0x0000007c95de674c <android::AMessage::setTarget+68>:        movk        x0, #0x7974, lsl #48

0x0000007c95de6750 <android::AMessage::setTarget+72>:        bl        0x7c95dfb370 <__cfi_slowpath@plt>

x16 = 0x0000007c95dfdb70与x17= 0x0000007c9844f118 也满足tombstone信息。

(gdb) disassemble 0x7c95dfb370

Dump of assembler code for function __cfi_slowpath@plt:

0x0000007c95dfb370 <+0>: adrp x16, 0x7c95dfd000 <__dso_handle_const>

0x0000007c95dfb374 <+4>: ldr x17, [x16, #2928]

0x0000007c95dfb378 <+8>: add x16, x16, #0xb70

0x0000007c95dfb37c <+12>: br x17

End of assembler dump.

(gdb) x /gx 0x7c95dfd000+0xb70

0x7c95dfdb70 <__cfi_slowpath@got.plt>: 0x0000007c9844f118

(gdb) disassemble 0x0000007c9844f118

Dump of assembler code for function __cfi_slowpath(uint64_t, void):

0x0000007c9844f118 <+0>: stp x29, x30, [sp, #-16]!

0x0000007c9844f11c <+4>: mov x29, sp

0x0000007c9844f120 <+8>: lsr x8, x1, #17

0x0000007c9844f124 <+12>: and x8, x8, #0x7ffffffffe

0x0000007c9844f128 <+16>: mov w9, #0x80000000 // #-2147483648

0x0000007c9844f12c <+20>: cmp x8, x9

0x0000007c9844f130 <+24>: b.hi 0x7c9844f14c <__cfi_slowpath(uint64_t, void)+52> // b.pmore

0x0000007c9844f134 <+28>: adrp x9, 0x7c98452000 <_ZL19shadow_base_storage>

0x0000007c9844f138 <+32>: ldr x9, [x9]

0x0000007c9844f13c <+36>: ldrh w8, [x9, x8]

0x0000007c9844f140 <+40>: cmp w8, #0x1

0x0000007c9844f144 <+44>: b.eq 0x7c9844f15c <__cfi_slowpath(uint64_t, void)+68> // b.none

0x0000007c9844f148 <+48>: cbnz w8, 0x7c9844f164 <__cfi_slowpath(uint64_t, void)+76>

0x0000007c9844f14c <+52>: xpaclri

0x0000007c9844f150 <+56>: mov x2, xzr

0x0000007c9844f154 <+60>: mov x3, x30

0x0000007c9844f158 <+64>: bl 0x7c9844f2a0 <__loader_cfi_fail@plt>

0x0000007c9844f15c <+68>: ldp x29, x30, [sp], #16

0x0000007c9844f160 <+72>: ret

0x0000007c9844f164 <+76>: and x9, x1, #0xfffffffffffc0000

0x0000007c9844f168 <+80>: sub x8, x9, x8, lsl #12

0x0000007c9844f16c <+84>: add x3, x8, #0x42, lsl #12

0x0000007c9844f170 <+88>: mov x2, xzr

0x0000007c9844f174 <+92>: ldp x29, x30, [sp], #16

0x0000007c9844f178 <+96>: br x3

End of assembler dump.

因此 A 函数的栈是 android::AMessage::setTarget(android::sp<android::AHandler const> const&)

计算汇编走向

根据 tombstone 最后的栈地址 SP=0x0000007c02d886d0 说明函数  __cfi_slowpath(uint64_t, void) 已经退栈。

0x7c02d88620:   0x0000007c02d89000  0x6f2ab3b40fa2f8ef

0x7c02d88630:   0x0000007c02d88670  0x0000007caae98c58

0x7c02d88640:   0x0000007c02d886b0  0x0000007caae9ce40

0x7c02d88650:   0x0000000000000005  0x6f2ab3b40fa2f8ef

je_malloc

0x7c02d88660:   0x0000007c02d886a0  0x0000007caae98c58

0x7c02d88670:   0x0000000000000002  0x0000000000000000

0x7c02d88680:   0x0000000000000415  0xb400007c13c246d0

0x7c02d88690:   0x0000000071756549  0x0000000000000018

malloc(size_t)

0x7c02d886a0:   0x0000007c02d886c0  0x0000007c9dccfb74  operator new(unsigned long)

0x7c02d886b0:   0x0000000000000018  0x0000007caae98c58

__cfi_slowpath(uint64_t, void*)

0x7c02d886c0:   0x0000007c02d886f0  0x0000007c95de6754

0x7c02d886d0:   0xb400007c13c246d0  0x0000000001909705

0x7c02d886e0:   0x0000007c02d88700  0x6f2ab3b40fa2f8ef

android::AMessage::setTarget(android::sp<android::AHandler const> const&)

0x7c02d886f0:   0x0000007c02d88750  0x0000007ca133f8e0

0x7c02d88700:   0x0000000000000002  0x0000000000000000

0x7c02d88710:   0x0000000000000415  0x0000000001909705

0x7c02d88720:   0x0000000000000000  0x0000007c02d88808

0x7c02d88730:   0xb400007c13c67c00  0x0000000000000000

0x7c02d88740:   0x0000007c02d89000  0x6f2ab3b40fa2f8ef

android::MediaCodec::queueInputBuffer(unsigned long, unsigned long, unsigned long, long, unsigned int, android::AString*)

0x7c02d88750:   0x0000007c02d88830  0x0000007ca796ee7c

0x7c02d88760:   0x0000007ca79f3dd8  0x0000007ca79edb80

0x7c02d88770:   0x0000007c02d89000  0xb400007c13c04680

0x7c02d88780:   0x0000000000000000  0x0000000000000002

0x7c02d88790:   0xb400007c13c67c00  0x0000000000000000

0x7c02d887a0:   0x0000000000000000  0xb400007c13c7c100

其中恢复其调用栈如下:

backtrace:

unknown?

android::AMessage::setTarget(android::sp<android::AHandler const> const&)

android::MediaCodec::queueInputBuffer(unsigned long, unsigned long, unsigned long, long, unsigned int, android::AString*)

...

__cfi_slowpath函数有两处退栈相关。

第一部分:

0x0000007c9844f14c <+52>: xpaclri

0x0000007c9844f150 <+56>: mov x2, xzr

0x0000007c9844f154 <+60>: mov x3, x30

0x0000007c9844f158 <+64>: bl 0x7c9844f2a0 <__loader_cfi_fail@plt>

0x0000007c9844f15c <+68>: ldp x29, x30, [sp], #16

0x0000007c9844f160 <+72>: ret

第二部分:

0x0000007c9844f164 <+76>: and x9, x1, #0xfffffffffffc0000

0x0000007c9844f168 <+80>: sub x8, x9, x8, lsl #12

0x0000007c9844f16c <+84>: add x3, x8, #0x42, lsl #12

0x0000007c9844f170 <+88>: mov x2, xzr

0x0000007c9844f174 <+92>: ldp x29, x30, [sp], #16

0x0000007c9844f178 <+96>: br x3

如果走的是第一部分退栈,那么 x16 和 x17 将与 tombstone 不符合,因此可以确定走的是第二部分。

而这里会跳转至 x3,可以看下当前 tombstone 里 x3=0x0000007ca1291000 是否为一个有效的函数地址。

(gdb) disassemble 0x0000007ca1291000,+0x40

Dump of assembler code from 0x7ca1291000 to 0x7ca1291040:

  0x0000007ca1291000 <__cfi_check+0>:        str        x30, [sp, #-16]!

0x0000007ca1291004 <__cfi_check+4>:        mov        x8, #0x48f1                        // #18673

0x0000007ca1291008 <__cfi_check+8>:        movk        x8, #0xd799, lsl #16

0x0000007ca129100c <__cfi_check+12>:        movk        x8, #0xcf94, lsl #32

0x0000007ca1291010 <__cfi_check+16>:        movk        x8, #0xfc57, lsl #48

0x0000007ca1291014 <__cfi_check+20>:        cmp        x0, x8

0x0000007ca1291018 <__cfi_check+24>:        b.le        0x7ca1291100 <__cfi_check+256>

0x0000007ca129101c <__cfi_check+28>:        mov        x8, #0xd5c9                        // #54729

0x0000007ca1291020 <__cfi_check+32>:        movk        x8, #0x5fef, lsl #16

0x0000007ca1291024 <__cfi_check+36>:        movk        x8, #0x22f2, lsl #32

0x0000007ca1291028 <__cfi_check+40>:        movk        x8, #0x4e31, lsl #48

0x0000007ca129102c <__cfi_check+44>:        cmp        x0, x8

0x0000007ca1291030 <__cfi_check+48>:        b.gt        0x7ca12911f4 <__cfi_check+500>

0x0000007ca1291034 <__cfi_check+52>:        mov        x8, #0x90ef                        // #37103

0x0000007ca1291038 <__cfi_check+56>:        movk        x8, #0x3c9, lsl #16

0x0000007ca129103c <__cfi_check+60>:        movk        x8, #0x8d30, lsl #32

这个函数非常的大,但有规律,很显然先将 x8与x0 作比较,并且数值与 tombstone 中

x0=0x79748c5e568e2ddc 类似,那么我们可以搜索部分关键字找到相应的 case。

0x0000007ca12920b4 <+4276>:  mov x8, #0x2ddc                 // #11740

0x0000007ca12920b8 <+4280>:  movk    x8, #0x568e, lsl #16

0x0000007ca12920bc <+4284>:  movk    x8, #0x8c5e, lsl #32

0x0000007ca12920c0 <+4288>:  movk    x8, #0x7974, lsl #48

0x0000007ca12920c4 <+4292>:  cmp x0, x8

0x0000007ca12920c8 <+4296>:  b.eq    0x7ca129576c <__cfi_check+18284>  // b.none

0x0000007ca129576c <+18284>: adrp    x8, 0x7ca13c2000 <_ZTVN7android17BufferChannelBaseE+24>

0x0000007ca1295770 <+18288>: add x8, x8, #0xc28

0x0000007ca1295774 <+18292>: sub x8, x1, x8

0x0000007ca1295778 <+18296>: sub x8, x8, #0x630

0x0000007ca129577c <+18300>: ror x8, x8, #4

0x0000007ca1295780 <+18304>: cmp x8, #0x5a

0x0000007ca1295784 <+18308>: b.hi    0x7ca1296680 <__cfi_check+22144>  // b.pmore

  tombstone 中 x9=0x0000007ca126fed7

  0x0000007ca1295788 <+18312>: adrp    x9, 0x7ca126f000 <_ZL5__txt+844>  

  0x0000007ca129578c <+18316>: add x9, x9, #0xed7

0x0000007ca1295790 <+18320>: b   0x7ca12963a4 <__cfi_check+21412>

0x0000007ca12963a4 <+21412>: ldrb    w8, [x9, x8]

  tombstone 中x8=0x0000000000000080

  0x0000007ca12963a8 <+21416>: tbnz    w8, #7, 0x7ca1296644 <__cfi_check+22084>  

0x0000007ca12963ac <+21420>: b   0x7ca1296680 <__cfi_check+22144>

  0x0000007ca1296644 <+22084>: ldr x30, [sp], #16

  0x0000007ca1296648 <+22088>: ret

最后在此处退栈回到 android::AMessage::setTarget ,然后 PC=LR=0x0000007c02d886f0 发生段错误。

结论

不难发现一个细节 0x7c02d886c0 地址上存储的内容是有问题的。

__cfi_slowpath(uint64_t, void*)

0x7c02d886c0:   0x0000007c02d886f0  0x0000007c95de6754

0x7c02d886d0:   0xb400007c13c246d0  0x0000000001909705

0x7c02d886e0:   0x0000007c02d88700  0x6f2ab3b40fa2f8ef

android::AMessage::setTarget(android::sp<android::AHandler const> const&)

0x7c02d886f0:   0x0000007c02d88750  0x0000007ca133f8e0

0x7c02d88700:   0x0000000000000002  0x0000000000000000

0x7c02d88710:   0x0000000000000415  0x0000000001909705

0x7c02d88720:   0x0000000000000000  0x0000007c02d88808

0x0000007c9844f118 < __cfi_slowpath +0>: stp x29, x30, [sp, #-16]!  

压栈应该是 0x7c02d886c0:   0x0000007c02d886f0  0x0000007c95de6754

0x0000007c9844f11c < __cfi_slowpath +4>: mov x29, sp

...

0x0000007c9844f174 < __cfi_slowpath +92>: ldp x29, x30, [sp], #16

0x0000007c9844f178 < __cfi_slowpath +96>: br x3

 0x0000007ca1291000 <__cfi_check+0>:        str        x30, [sp, #-16]!  

压栈应该是 0x7c02d886c0:   0x0000007c95de6754  0x0000007c95de6754

...

  0x0000007ca1296644 < __cfi_check +22084>: ldr x30, [sp], #16

  0x0000007ca1296648 < __cfi_check +22088>: ret

(gdb) p /t 0xf81f0ffe  【str   x30, [sp, #-16]!】

$5 = 11111000000111110000111111111110

(gdb) p /t 0xf81f0ffd  【str   x29, [sp, #-16]!】

$6 = 11111000000111110000111111111101

因此能符合 tombstone 信息,只有此处指令异常导致。

错误模拟

# ps -ef | grep "medias"

media         1145     1 0 01:35:04 ?     00:00:06 mediaserver64

mediacodec    1223     1 0 01:35:04 ?     00:00:01 media.swcodec oid.media.swcodec/bin/mediaswcodec

# cat /proc/1145/maps | grep "libstagefright.so"

7abb428000-7abb4a9000 r--p 00000000 fe:05 84399283                       /system/lib64/libstagefright.so

7abb4a9000-7abb5db000 r-xp 00081000 fe:05 84399283                       /system/lib64/libstagefright.so

7abb5db000-7abb5ea000 r--p 001b3000 fe:05 84399283                       /system/lib64/libstagefright.so

7abb5ea000-7abb5ec000 rw-p 001c1000 fe:05 84399283                       /system/lib64/libstagefright.so

PC=0x7abb428000+0x83000

修改机器码模拟报错场景。

# ./data/memory_get64 1145 0x7abb4ab000

pid 1145, target 0x7abb4ab000

0xd2891e28f81f0ffe

# ./data/memory_set64 1145 0x7abb4ab000 0xd2891e28 0xf81f0ffd

pid 1145, target 0x7abb4ab000, val 0xd2891e28f81f0ffd

06-27 23:10:07.311  1145  1145 F libc    : Fatal signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x7fdc95f510 in tid 1145 (mediaserver64), pid 1145 (mediaserver64)

06-27 23:10:07.579 27515 27515 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

06-27 23:10:07.579 27515 27515 F DEBUG   : Revision: '0'

06-27 23:10:07.579 27515 27515 F DEBUG   : ABI: 'arm64'

06-27 23:10:07.579 27515 27515 F DEBUG   : Timestamp: 2022-06-27 23:10:07.361670464+0800

06-27 23:10:07.579 27515 27515 F DEBUG   : Process uptime: 0s

06-27 23:10:07.580 27515 27515 F DEBUG   : Cmdline: mediaserver64

06-27 23:10:07.580 27515 27515 F DEBUG   : pid: 1145, tid: 1145, name: mediaserver64  >>> mediaserver64 <<<

06-27 23:10:07.580 27515 27515 F DEBUG   : uid: 1013

06-27 23:10:07.580 27515 27515 F DEBUG   : tagged_addr_ctrl: 0000000000000001

06-27 23:10:07.580 27515 27515 F DEBUG   : signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x7fdc95f510

06-27 23:10:07.580 27515 27515 F DEBUG   :     x0  bf2d51af2cdcf7a8  x1  0000007abb5df510  x2  0000000000000000  x3  0000007abb4ab000

06-27 23:10:07.580 27515 27515 F DEBUG   :     x4  0065006300690076  x5  7600690063006500  x6  0065006300690076  x7  b400007ac2afd3c8

06-27 23:10:07.580 27515 27515 F DEBUG   :     x8  0000000000000003  x9  0000007abb489b40  x10 0000000000000002  x11 0000000000000001

06-27 23:10:07.580 27515 27515 F DEBUG   :     x12 0000000000000000  x13 0000007aac820058  x14 0000000000000010  x15 0000000000000010

06-27 23:10:07.580 27515 27515 F DEBUG   :     x16 0000007ab0cbd538  x17 0000007ac1210118  x18 0000007ac3726000  x19 0000007fdc95f5d0

06-27 23:10:07.580 27515 27515 F DEBUG   :     x20 b400007a26e03048  x21 0000007ac13b1b98  x22 0000007ab0cadc50  x23 0000007ac2d13000

06-27 23:10:07.580 27515 27515 F DEBUG   :     x24 0000007fdc95f470  x25 b400007ac2aea1c4  x26 0000000000000000  x27 0000000000000479

06-27 23:10:07.580 27515 27515 F DEBUG   :     x28 0000000000000000  x29 0000007fdc95f510

06-27 23:10:07.580 27515 27515 F DEBUG   :     lr  0000007fdc95f510  sp  0000007fdc95f440  pc  0000007fdc95f510  pst 0000000080001000

06-27 23:10:07.580 27515 27515 F DEBUG   : backtrace:

06-27 23:10:07.580 27515 27515 F DEBUG   :       #00 pc 000000000001f510  [stack]

Coredump寄存器恢复栈

通过readelf读取MINIDUMP文件,保存寄存器上下文在ELF文件的第一个节中。

Program Headers:

Type           Offset             VirtAddr           PhysAddr

FileSiz            MemSiz              Flags  Align

 NOTE           0x000000000000fb28 0x0000000000000000 0x0000000000000000

                0x00000000000032cc 0x0000000000000000         0x0

LOAD           0x0000000000013000 0x0000005ffabc7000 0x0000000000000000

0x0000000000001000 0x0000000000001000  R      0x0


上图绿色为LR和PC,将它们修改为正确的返回地址0x0000007c95de6754 

 

转自:Android开发太难了,Native Crash的一切!

相关推荐

相关文章