Android NDK开发Crash问题分析

目前市场上越来越多的 Android App采用 C/C++ 来实现其关键逻辑,尤其是很多第三方的SDK,出于效率、安全,复用的考虑,比如人脸识别,语音识别等等。所以能分析 C/C++ 崩溃日志并能从日志中分析出原因,成为 Android 开发人员一项必备技能。本文将通过一个简单的Demo分析 Native 崩溃日志来定位出错的 C/C++ 代码及出错原因。

1、问题现场

为了方便,直接使用Android Studio中Sample:hello-jni,修改app/src/main/cpp/hello-jni.c文件内容如下,手动造一个Native Crash问题,这样在App启动后,就会出现闪退,抓取logcat日志。

因为使用的user版本的手机,所有没有权限读取到/data/tombstones日志,不过logcat日志对于分析本文的问题已经足够;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <string.h>
#include <jni.h>

/* This is a trivial JNI example where we use a native method
* to return a new VM String. See the corresponding Java source
* file located at:
*
* hello-jni/app/src/main/java/com/example/hellojni/HelloJni.java
*/

void willCrash() {
int *p = NULL;
*p = 123;
}

JNIEXPORT jstring JNICALL
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
jobject thiz )
{
willCrash();
return (*env)->NewStringUTF(env, "Hello from JNI ! Compiled with ABI");
}

2、分析流程

2.1、关于日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
11-10 22:08:45.968  4449  4449 I crash_dump64: performing dump of process 4418 (target tid = 4418)
11-10 22:08:45.976 4449 4449 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
11-10 22:08:45.976 4449 4449 F DEBUG : Build fingerprint: 'Xiaomi/sagit/sagit:9/PKQ1.190118.001/V10.4.2.0.PCACNXM:user/release-keys'
11-10 22:08:45.976 4449 4449 F DEBUG : Revision: '0'
11-10 22:08:45.976 4449 4449 F DEBUG : ABI: 'arm64'
11-10 22:08:45.976 4449 4449 F DEBUG : pid: 4418, tid: 4418, name: xample.hellojni >>> com.example.hellojni <<<
11-10 22:08:45.976 4449 4449 F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
11-10 22:08:45.976 4449 4449 F DEBUG : Cause: null pointer dereference
11-10 22:08:45.977 4449 4449 F DEBUG : x0 0000006f510e8460 x1 0000007ff2acb514 x2 0000000000000000 x3 0000006f51014c00
11-10 22:08:45.977 4449 4449 F DEBUG : x4 0000007ff2acb980 x5 0000006f38f34471 x6 766468752e63686e x7 0000000000000000
11-10 22:08:45.977 4449 4449 F DEBUG : x8 000000000000007b x9 0000000000000000 x10 0000000000430000 x11 0000006f50936688
11-10 22:08:45.977 4449 4449 F DEBUG : x12 000000000000018c x13 0000006fd3eb1018 x14 0000006fd3e7e000 x15 ffffffffffffffff
11-10 22:08:45.977 4449 4449 F DEBUG : x16 0000006f38bd8fe0 x17 0000006f38bc8648 x18 0000000000000000 x19 0000006f51014c00
11-10 22:08:45.977 4449 4449 F DEBUG : x20 0000006f50850880 x21 0000006f51014c00 x22 0000007ff2acb7b0 x23 0000006f38f34471
11-10 22:08:45.977 4449 4449 F DEBUG : x24 0000000000000004 x25 0000006fd6f7a5e0 x26 0000006f51014ca0 x27 0000000000000001
11-10 22:08:45.977 4449 4449 F DEBUG : x28 0000000000000002 x29 0000007ff2acb4f0
11-10 22:08:45.977 4449 4449 F DEBUG : sp 0000007ff2acb4c0 lr 0000006f38bc8680 pc 0000006f38bc865c
11-10 22:08:46.305 4449 4449 F DEBUG :
11-10 22:08:46.305 4449 4449 F DEBUG : backtrace:
11-10 22:08:46.305 4449 4449 F DEBUG : #00 pc 000000000000065c /data/app/com.example.hellojni-Sytg7I9fr97KTcTz7Rrsjg==/lib/arm64/libhello-jni.so (willCrash+20)
11-10 22:08:46.305 4449 4449 F DEBUG : #01 pc 000000000000067c /data/app/com.example.hellojni-Sytg7I9fr97KTcTz7Rrsjg==/lib/arm64/libhello-jni.so (Java_com_example_hellojni_HelloJni_stringFromJNI+20)
11-10 22:08:46.305 4449 4449 F DEBUG : #02 pc 000000000000909c /data/app/com.example.hellojni-Sytg7I9fr97KTcTz7Rrsjg==/oat/arm64/base.odex (offset 0x9000) (com.example.hellojni.HelloJni.stringFromJNI+124)
11-10 22:08:46.305 4449 4449 F DEBUG : #03 pc 000000000055d788 /system/lib64/libart.so (art_quick_invoke_stub+584)
11-10 22:08:46.305 4449 4449 F DEBUG : #04 pc 00000000000d074c /system/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+200)
11-10 22:08:46.305 4449 4449 F DEBUG : #05 pc 0000000000280dbc /system/lib64/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+344)
11-10 22:08:46.305 4449 4449 F DEBUG : #06 pc 000000000027add0 /system/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+968)
---------------------------------省略部分-----------------------------------

可以看到,日志内容主要由下面几部分组成:(我们最主要的就是分析崩溃的过程和PID,终止的信号和故障地址和调用堆栈部分)

  • 构建指纹
  • 崩溃的过程和PID
  • 终止信号和故障地址
  • CPU寄存器
  • 调用堆栈

2.1.1、崩溃过程和PID信息

从上面日志中的第6行中我们可以看到崩溃进程的基本信息,如下所示:

1
pid: 8902, tid: 8902, name: xample.hellojni  >>> com.example.hellojni <<<

如果pid等于tid,那么就说明这个程序是在主线程中Crash掉的,名称的属性则表示Crash进程的名称以及在文件系统中位置。

2.1.2、终止信号和故障地址信息

从上面日志中的第7、8行中我们可以看到程序是因为什么信号导致了Crash以及出现错误的地址,如下所示:

1
2
11-10 19:53:21.890  8926  8926 F DEBUG   : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
11-10 19:53:21.890 8926 8926 F DEBUG : Cause: null pointer dereference

第7行的信息说明出现进程Crash的原因是因为程序产生了段错误的信号,访问了非法的内存空间,而访问的非法地址是0x0。另外这个例子中直接给出来问题原因是因为空指针,其他问题并不一定会给出此信息。

2.1.3、调用堆栈信息

调用栈信息是分析程序崩溃的非常重要的一个信息,它主要记录了程序在Crash前的函数调用关系以及当前正在执行函数的信息,上面例中的backtrace的信息如下所示:

1
2
3
4
5
6
7
8
11-10 22:08:46.305  4449  4449 F DEBUG   : backtrace:
11-10 22:08:46.305 4449 4449 F DEBUG : #00 pc 000000000000065c /data/app/com.example.hellojni-Sytg7I9fr97KTcTz7Rrsjg==/lib/arm64/libhello-jni.so (willCrash+20)
11-10 22:08:46.305 4449 4449 F DEBUG : #01 pc 000000000000067c /data/app/com.example.hellojni-Sytg7I9fr97KTcTz7Rrsjg==/lib/arm64/libhello-jni.so (Java_com_example_hellojni_HelloJni_stringFromJNI+20)
11-10 22:08:46.305 4449 4449 F DEBUG : #02 pc 000000000000909c /data/app/com.example.hellojni-Sytg7I9fr97KTcTz7Rrsjg==/oat/arm64/base.odex (offset 0x9000) (com.example.hellojni.HelloJni.stringFromJNI+124)
11-10 22:08:46.305 4449 4449 F DEBUG : #03 pc 000000000055d788 /system/lib64/libart.so (art_quick_invoke_stub+584)
11-10 22:08:46.305 4449 4449 F DEBUG : #04 pc 00000000000d074c /system/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+200)
11-10 22:08:46.305 4449 4449 F DEBUG : #05 pc 0000000000280dbc /system/lib64/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+344)
11-10 22:08:46.305 4449 4449 F DEBUG : #06 pc 000000000027add0 /system/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+968)

在上面的输出信息中,## 00,#01,#02 ……等表示的都是函数调用栈中栈帧的编号,其中编号越小的栈帧表示着当前最近调用的函数信息,所以栈帧标号#00表示的就是当前正在执行并导致程序崩溃函数的信息。

在栈帧的每一行中,pc后面的16进制数值表示的是当前函数正在执行语句的在共享链接库或者可执行文件中的位置,然后/lib/arm/libhello-jni.so则表示的是当前执行指令是在哪个文件当中,后面的小括号则是注明对应的是哪个函数。

例如,在上面的例子中,我们就可以定位到是程序是在willCrash中出现了错误,但是具体在那一行呢,我们还不是特别清楚,所以就需要我们进一步地使用更加高级的工具来帮助我们解析日志中有关调用栈的信息。

2.2、addr2line

addr2line是NDK中用来获得指定动态链接库文件或者可执行文件中指定地址对应的源代码信息,它们位于NDK包中的如下位置中,以arm64架构为例:

1
$$NDK_HOME/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-addr2line

其中NDK_HOME表示你的NDK的安装路径,另外具体架构和目录的对应关系如下:

工具链 位置
arm $TOOLCHAIN/arm-linux-androideabi/lib/
arm64 $TOOLCHAIN/aarch64-linux-android/lib/
x86 $TOOLCHAIN/i686-linux-android/lib/
x86_64 $TOOLCHAIN/x86_64-linux-android/lib/

addr2line的使用说明如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Usage: ./aarch64-linux-android-addr2line [option(s)] [addr(s)]
Convert addresses into line number/file name pairs.
If no addresses are specified on the command line, they will be read from stdin
The options are:
@<file> Read options from <file>
-a --addresses Show addresses
-b --target=<bfdname> Set the binary file format
-e --exe=<executable> Set the input file name (default is a.out)
-i --inlines Unwind inlined functions
-j --section=<name> Read section-relative offsets instead of addresses
-p --pretty-print Make the output easier to read for humans
-s --basenames Strip directory names
-f --functions Show function names
-C --demangle[=style] Demangle function names
-h --help Display this information
-v --version Display the program's version

./aarch64-linux-android-addr2line: supported targets: elf64-littleaarch64 elf64-bigaarch64 elf32-littleaarch64 elf32-bigaarch64 elf32-littlearm elf32-bigarm elf64-little elf64-big elf32-little elf32-big plugin srec symbolsrec verilog tekhex binary ihex
Report bugs to <http://source.android.com/source/report-bugs.html>

addr2line的基本用法如下所示:

1
2
3
./aarch64-linux-android-addr2line -f -e ~/Desktop/workspace/android/min/Demo/HelloJNI/app/build/intermediates/cmake/arm8Debug/obj/arm64-v8a/libhello-jni.so 000000000000065c
willCrash
/Users/min/Desktop/workspace/android/min/Demo/HelloJNI/app/src/main/cpp/hello-jni.c:29

如上所示,通过addr2line工具,我们可以看到libhello-jni.so文件中地址000000000000065c对应的源码是什么了,它对应的是源码中app/src/main/cpp/hello-jni.c:29处代码,查看上下文后,确定为空指针问题。

2.3、ndk-stack

Android NDK自从版本r6开始,提供了一个工具ndk-stack。这个工具能自动分析tombstone文件,能将崩溃时的调用内存地址和c ++代码一行一行对应起来。

ndk-stack工具同样也位于NDK包中,它的路径如下所示:

1
$NDK_HOME/ndk-stack

ndk-stack的使用说明如下所示:

1
2
3
4
5
6
7
Usage: ndk-stack -sym PATH [-dump PATH]
Symbolizes the stack trace from an Android native crash.

-sym PATH sets the root directory for symbols
-dump PATH sets the file containing the crash dump (default stdin)

See <https://developer.android.com/ndk/guides/ndk-stack.html>.

其中,

  • dump参数很容易理解,即dump下来的log文本文件,可以是logcat日志或者tombstones日志;
  • sym参数就是你的android项目下,编译成功之后,obj目录下的文件。

ndk-stack的基本用法如下所示:

1
ndk-stack -sym ~/Desktop/workspace/android/min/Demo/HelloJNI/app/build/intermediates/cmake/arm8Debug/obj/arm64-v8a/ -dump ~/Desktop/min.log |tee ~/Desktop/ndk-stack.txt

最后产生的结果文件如下,可以看到,堆栈信息的最后对应的是源码中app/src/main/cpp/hello-jni.c:29处代码,查看上下文后,确定为空指针问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
********** Crash dump: **********
Build fingerprint: 'Xiaomi/sagit/sagit:9/PKQ1.190118.001/V10.4.2.0.PCACNXM:user/release-keys'
pid: 4418, tid: 4418, name: xample.hellojni >>> com.example.hellojni <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
Stack frame #00 pc 000000000000065c /data/app/com.example.hellojni-Sytg7I9fr97KTcTz7Rrsjg==/lib/arm64/libhello-jni.so (willCrash+20): Routine willCrash at /Users/min/Desktop/workspace/android/min/Demo/HelloJNI/app/src/main/cpp/hello-jni.c:29
Stack frame #01 pc 000000000000067c /data/app/com.example.hellojni-Sytg7I9fr97KTcTz7Rrsjg==/lib/arm64/libhello-jni.so (Java_com_example_hellojni_HelloJni_stringFromJNI+20): Routine Java_com_example_hellojni_HelloJni_stringFromJNI at /Users/min/Desktop/workspace/android/min/Demo/HelloJNI/app/src/main/cpp/hello-jni.c:36
Stack frame #02 pc 000000000000909c /data/app/com.example.hellojni-Sytg7I9fr97KTcTz7Rrsjg==/oat/arm64/base.odex (offset 0x9000) (com.example.hellojni.HelloJni.stringFromJNI+124)
Stack frame #03 pc 000000000055d788 /system/lib64/libart.so (art_quick_invoke_stub+584)
Stack frame #04 pc 00000000000d074c /system/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+200)
Stack frame #05 pc 0000000000280dbc /system/lib64/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+344)
Stack frame #06 pc 000000000027add0 /system/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+968)
Stack frame #07 pc 000000000052c8d8 /system/lib64/libart.so (MterpInvokeVirtual+588)
Stack frame #08 pc 000000000054fd14 /system/lib64/libart.so (ExecuteMterpImpl+14228)
Stack frame #09 pc 00000000001de676 /data/app/com.example.hellojni-Sytg7I9fr97KTcTz7Rrsjg==/oat/arm64/base.vdex (com.example.hellojni.HelloJni.onCreate+36)
Stack frame #10 pc 0000000000254ad4 /system/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEb.llvm.1893940555+488)
Stack frame #11 pc 000000000025a5c8 /system/lib64/libart.so (art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+216)
---------------------------------省略部分-----------------------------------

2.4、ndk-stack

上面两种工具都是将崩溃点对应到源码再进行分析,objdump 则是可以在汇编层对崩溃原因进行分析。所以这要求我们必须了解一些 arm/x86 汇编知识。

objdump也是ndk自带的一个工具,通常与addr2line在同一目录:

1
$$NDK_HOME/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-objdump

objdump的使用说明如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
Usage: ./aarch64-linux-android-objdump <option(s)> <file(s)>
Display information from object <file(s)>.
At least one of the following switches must be given:
-a, --archive-headers Display archive header information
-f, --file-headers Display the contents of the overall file header
-p, --private-headers Display object format specific file header contents
-P, --private=OPT,OPT... Display object format specific contents
-h, --[section-]headers Display the contents of the section headers
-x, --all-headers Display the contents of all headers
-d, --disassemble Display assembler contents of executable sections
-D, --disassemble-all Display assembler contents of all sections
-S, --source Intermix source code with disassembly
-s, --full-contents Display the full contents of all sections requested
-g, --debugging Display debug information in object file
-e, --debugging-tags Display debug information using ctags style
-G, --stabs Display (in raw form) any STABS info in the file
-W[lLiaprmfFsoRt] or
--dwarf[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,
=frames-interp,=str,=loc,=Ranges,=pubtypes,
=gdb_index,=trace_info,=trace_abbrev,=trace_aranges,
=addr,=cu_index]
Display DWARF info in the file
-t, --syms Display the contents of the symbol table(s)
-T, --dynamic-syms Display the contents of the dynamic symbol table
-r, --reloc Display the relocation entries in the file
-R, --dynamic-reloc Display the dynamic relocation entries in the file
@<file> Read options from <file>
-v, --version Display this program's version number
-i, --info List object formats and architectures supported
-H, --help Display this information

The following switches are optional:
-b, --target=BFDNAME Specify the target object format as BFDNAME
-m, --architecture=MACHINE Specify the target architecture as MACHINE
-j, --section=NAME Only display information for section NAME
-M, --disassembler-options=OPT Pass text OPT on to the disassembler
-EB --endian=big Assume big endian format when disassembling
-EL --endian=little Assume little endian format when disassembling
--file-start-context Include context from start of file (with -S)
-I, --include=DIR Add DIR to search list for source files
-l, --line-numbers Include line numbers and filenames in output
-F, --file-offsets Include file offsets when displaying information
-C, --demangle[=STYLE] Decode mangled/processed symbol names
The STYLE, if specified, can be `auto', `gnu',
`lucid', `arm', `hp', `edg', `gnu-v3', `java'
or `gnat'
-w, --wide Format output for more than 80 columns
-z, --disassemble-zeroes Do not skip blocks of zeroes when disassembling
--start-address=ADDR Only process data whose address is >= ADDR
--stop-address=ADDR Only process data whose address is <= ADDR
--prefix-addresses Print complete address alongside disassembly
--[no-]show-raw-insn Display hex alongside symbolic disassembly
--insn-width=WIDTH Display WIDTH bytes on a single line for -d
--adjust-vma=OFFSET Add OFFSET to all displayed section addresses
--special-syms Include special symbols in symbol dumps
--prefix=PREFIX Add PREFIX to absolute paths for -S
--prefix-strip=LEVEL Strip initial directory names for -S
--dwarf-depth=N Do not display DIEs at depth N or greater
--dwarf-start=N Display DIEs starting with N, at the same depth
or deeper
--dwarf-check Make additional dwarf internal consistency checks.

./aarch64-linux-android-objdump: supported targets: elf64-littleaarch64 elf64-bigaarch64 elf32-littleaarch64 elf32-bigaarch64 elf32-littlearm elf32-bigarm elf64-little elf64-big elf32-little elf32-big plugin srec symbolsrec verilog tekhex binary ihex
./aarch64-linux-android-objdump: supported architectures: aarch64 aarch64:ilp32 arm armv2 armv2a armv3 armv3m armv4 armv4t armv5 armv5t armv5te xscale ep9312 iwmmxt iwmmxt2 arm_any plugin

The following AARCH64 specific disassembler options are supported for use
with the -M switch (multiple options should be separated by commas):

no-aliases Don't print instruction aliases.

aliases Do print instruction aliases.


The following ARM specific disassembler options are supported for use with
the -M switch:
reg-names-special-atpcs Select special register names used in the ATPCS
reg-names-atpcs Select register names used in the ATPCS
reg-names-apcs Select register names used in the APCS
reg-names-std Select register names used in ARM's ISA documentation
reg-names-gcc Select register names used by GCC
reg-names-raw Select raw register names
force-thumb Assume all insns are Thumb insns
no-force-thumb Examine preceding label to determine an insn's type

Report bugs to <http://source.android.com/source/report-bugs.html>.

objdump的基本用法如下所示:

1
objdump  ~/Desktop/workspace/android/min/Demo/HelloJNI/app/build/intermediates/cmake/arm8Debug/obj/arm64-v8a/ -dump ~/Desktop/min.log |tee ~/Desktop/ndk-stack.txt

最后产生的结果文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
0000000000000648 <willCrash>:
willCrash():
/Users/min/Desktop/workspace/android/min/Demo/HelloJNI/app/src/main/cpp/hello-jni.c:27
648: d10043ff sub sp, sp, #0x10
64c: 52800f68 mov w8, #0x7b // #123
650: aa1f03e9 mov x9, xzr
/Users/min/Desktop/workspace/android/min/Demo/HelloJNI/app/src/main/cpp/hello-jni.c:28
654: f90007e9 str x9, [sp,#8]
/Users/min/Desktop/workspace/android/min/Demo/HelloJNI/app/src/main/cpp/hello-jni.c:29
658: f94007e9 ldr x9, [sp,#8]
65c: b9000128 str w8, [x9]
/Users/min/Desktop/workspace/android/min/Demo/HelloJNI/app/src/main/cpp/hello-jni.c:30
660: 910043ff add sp, sp, #0x10
664: d65f03c0 ret

0000000000000668 <Java_com_example_hellojni_HelloJni_stringFromJNI>:
Java_com_example_hellojni_HelloJni_stringFromJNI():
/Users/min/Desktop/workspace/android/min/Demo/HelloJNI/app/src/main/cpp/hello-jni.c:35
668: d100c3ff sub sp, sp, #0x30
66c: a9027bfd stp x29, x30, [sp,#32]
670: 910083fd add x29, sp, #0x20
674: f81f83a0 stur x0, [x29,#-8]
678: f9000be1 str x1, [sp,#16]
/Users/min/Desktop/workspace/android/min/Demo/HelloJNI/app/src/main/cpp/hello-jni.c:36
67c: 97ffffd5 bl 5d0 <willCrash@plt>
680: 90000000 adrp x0, 0 <__cxa_finalize@plt-0x5c0>
684: 911ad001 add x1, x0, #0x6b4
/Users/min/Desktop/workspace/android/min/Demo/HelloJNI/app/src/main/cpp/hello-jni.c:37
688: f85f83a0 ldur x0, [x29,#-8]
68c: f9400000 ldr x0, [x0]
690: f9429c00 ldr x0, [x0,#1336]
694: f85f83be ldur x30, [x29,#-8]
698: f90007e0 str x0, [sp,#8]
69c: aa1e03e0 mov x0, x30
6a0: f94007fe ldr x30, [sp,#8]
6a4: d63f03c0 blr x30
6a8: a9427bfd ldp x29, x30, [sp,#32]
6ac: 9100c3ff add sp, sp, #0x30
6b0: d65f03c0 ret

可以看到,000000000000065c这个地址的相关两个汇编指令如下:

1
2
658:	f94007e9 	ldr	x9, [sp,#8]
65c: b9000128 str w8, [x9]

1、LDR R0, [R1]
LDR是把R1中的值取出放到寄存器R0中LDR:load R0 from register R1

2、STR R0, [R1]
STR是把R0中的值存入寄存器R1中,STR:store R0 to register R1

结合signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0信息,配合崩溃信号列表:

信号 描述
SIGSEGV 内存引用无效。
SIGBUS 访问内存对象的未定义部分。
SIGFPE 算术运算错误,除以零。
SIGILL 非法指令,如执行垃圾或特权指令
SIGSYS 糟糕的系统调用
SIGXCPU 超过CPU时间限制。
SIGXFSZ 文件大小限制。

我们大体可以猜出来这一个空指针的问题。

3、参考