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日志对于分析本文的问题已经足够;

* 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、关于日志

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行中我们可以看到崩溃进程的基本信息,如下所示:

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

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

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

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

    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的信息如下所示:

    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架构为例:

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

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

    addr2line的使用说明如下所示:

    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的基本用法如下所示:

    ./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包中,它的路径如下所示:

    $NDK_HOME/ndk-stack
    

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

    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的基本用法如下所示:

    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处代码,查看上下文后,确定为空指针问题。

    ********** 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在同一目录:

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

    objdump的使用说明如下所示:

    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的基本用法如下所示:

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

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

    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这个地址的相关两个汇编指令如下:

     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信息,配合崩溃信号列表: