利用strip前和strip后的so定位crash

一、带符号so和不带符号so

我们开发中用到的so,一般情况下有两个:带符号表的so和不带符号表的so

  • 不带符号的so是strip过的,体积会比原始so小很多,用于发布到apk中。
以我自己编译的libbreakpad-core.so为例
strip之前,占用大小为2.6M 
strip之后,占用大小334K
可见,在stip操作对于so的占用空间上优化还是挺大的。
  • 带符号的so是没有经过strip的的,用于线上so出现crash后,辅助帮助定位发生crash的具体函数

1.1、如何利用Android studio获取带符号表和不带符号表的同一个so

Android Studio 编译so过程 会自动加入strip操作,如下图所示

  • build->intermediates->cmake目录下是编译出的原始so
  • build->intermediates->stripped-native_libs 目录下是经过strip的so
strip_so

1.2、带符号和不带符号so,如何配合查找问题。

举例来说,apk发生natvie crash之后,系统会打


crash_info

工具目录:/Android/sdk/ndk-bundle/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin

我mac上的目录为:

/Users/feifei/Library/Android/sdk/ndk-bundle/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin
  • 1 标示出那个so出现了crash
  • 2 pc 此处的地址 并不是崩溃点的虚拟内存地址绝对值,而是崩溃点相对于so起始虚拟内存地址的相对偏移。利用这个相对地址,结合符号表就可以定位到具体是哪个函数出现了crash
  • 3 发生crash的方法名

3 处函数名可能有,也可能符号信息缺失crash信息里面没有,只有1和2。但是有1和2就足够了。

如何解析具体崩溃的函数呢?

需要用到aarch64-linux-android-addr2line工具和带符号的so

aarch64-linux-android-addr2line 位于ndk目录下
我的mac上具体路径如下:
 /Users/feifei/Library/Android/sdk/ndk-bundle/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-addr2line

由crash信息中1和2 可以知,是libbreakpad-core.so的偏移位置为18340的地方发生了crash

执行aarch64-linux-android-addr2line -f -C -e libbreakpad-core.so 18340 指令,可以获得18340偏移位置的具体函数符号,如下所示

feifeideMacBook-Pro:cmake feifei$ /Users/feifei/Library/Android/sdk/ndk-bundle/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-addr2line -f -C -e libbreakpad-core.so 18340
testCrash_null_point()
/Users/feifei/Desktop/TM/workspace/TrProbe/breakpad-build/src/main/cpp/breakpadcore.cpp:187

再对照看我们的源码:
调用关系为:go2crash()->call_dangerous_function()->testCrash_null_point()

问题得解!

/**
 * 创造一个 natvie crash
 * @param env
 * @param clazz
 */
JNIEXPORT void JNICALL Java_com_sogou_translate_breakpad_BreakPadCore_go2crash
        (JNIEnv * env, jclass clazz){

    call_dangerous_function();
//    testCrash_divide_zero();
//    testCrash_stackoverflow();
}

int call_dangerous_function() {
    testCrash_null_point();
    return 42;
}

int testCrash_null_point(){
    volatile int *a = (int *) (NULL);
    *a = 1;
    return 1;
}

1.3、排查crash的另一个思路

发生crash时,系统会打印出如下日志:

2021-02-04 11:06:04.621 3131-3131/? E//system/bin/tombstoned: Tombstone written to: /data/tombstones/tombstone_31

tombstone是什么文件呢?对于定制的智能硬件(Android系统),也可以利用这个文件排查问题

 adb root
 adb remount
 adb pull /data/tombstones/tombstone_31

tombstone实际上是android系统在发生crash时,dump出的一份内存堆栈信息。

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Native Crash TIME: 5009246
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'Sogou/sl8541e_1h10_oversea/sl8541e_1h10:8.1.0/OPM2.171019.012/29301:userdebug/test-keys'
Revision: '0'
ABI: 'arm64'
pid: 11371, tid: 11371, name: anslate.example  >>> com.sogou.translate.example <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
Cause: null pointer dereference
    x0   0000006fbf6e8140  x1   0000007fd97bc5d4  x2   0000000000000000  x3   0000006fbf6bea00
    x4   0000007fd97bc9e0  x5   0000006fa5bfb268  x6   0000000000000000  x7   0000000000000000
    x8   0000000000000000  x9   0000000000000001  x10  0000000000430000  x11  0000006fbf5db7a8
    x12  0000007043d46cd0  x13  0965de0ad6551339  x14  0000007043d46000  x15  ffffffffffffffff
    x16  0000007042ce3ca8  x17  0000006fa5063338  x18  0000000000000008  x19  0000006fbf6bea00
    x20  0000006fbf4f3ea0  x21  0000006fbf6bea00  x22  0000007fd97bc85c  x23  0000006fa5bfb268
    x24  0000000000000000  x25  0000007044348a40  x26  0000006fbf6beaa0  x27  0000000000000000
    x28  0000000000000000  x29  0000007fd97bc688  x30  0000006fa58930b4
    sp   0000007fd97bc5c0  pc   0000006fa5063340  pstate 0000000060000000
    v0   00000000000000000000000000000000  v1   00000000000000000000007fd97bcbd0
    v2   00000000000000004008000000000000  v3   00000000000000000000000000000000
    v4   00000000000000000000000000000000  v5   00000000000000004000000000000000
    v6   00000000000000000000000000000000  v7   00000000000000008020080280200802
    v8   00000000000000000000000000000000  v9   00000000000000000000000000000000
    v10  00000000000000000000000000000000  v11  00000000000000000000000000000000
    v12  00000000000000000000000000000000  v13  00000000000000000000000000000000
    v14  00000000000000000000000000000000  v15  00000000000000000000000000000000
    v16  40100401401004014010040140100401  v17  000100010004000001010400aaaaaaaa
    v18  00000001000000010000040000000000  v19  00000000000000000000007040509624
    v20  000000000000000000000070405099b4  v21  00000000000000000000007040509c84
    v22  0000000000000000000000704050c8f0  v23  0000000000000000000000704050c930
    v24  0000000000000000000000704050c9dc  v25  0000000000000000000000704050bbb4
    v26  0000000000000000000000704050c31c  v27  0000000000000000000000704050c374
    v28  0000000000000000000000704050c6b4  v29  0000000000000000000000704050bd0c
    v30  0000000000000000000000704050bf04  v31  0000000000000000000000000000000b
    fpsr 00000013  fpcr 00000000

backtrace:
    #00 pc 0000000000018340  /data/app/com.sogou.translate.example-Sj02X6XFCw3dgjlkJnlrHw==/lib/arm64/libbreakpad-core.so (Java_com_sogou_translate_breakpad_BreakPadCore_go2crash+8)
    #01 pc 00000000000100b0  /data/app/com.sogou.translate.example-Sj02X6XFCw3dgjlkJnlrHw==/oat/arm64/base.odex (offset 0x10000)

stack:
         0000007fd97bc540  0000000000000000
         0000007fd97bc548  0000006fbf6bea00  [anon:libc_malloc]
         0000007fd97bc550  0000007fd97bc9e0  [stack]
         0000007fd97bc558  0000006fa5bfb268  /data/app/com.sogou.translate.example-Sj02X6XFCw3dgjlkJnlrHw==/oat/arm64/base.vdex
         0000007fd97bc560  0000000000000000
         0000007fd97bc568  0000000000000000
         0000007fd97bc570  0000000000000000
         0000007fd97bc578  0000007fd97bcbd0  [stack]
         0000007fd97bc580  4008000000000000
         0000007fd97bc588  0000000000000000
         0000007fd97bc590  0000000000000000
         0000007fd97bc598  4000000000000000
         0000007fd97bc5a0  0000000000000000
         0000007fd97bc5a8  8020080280200802
         0000007fd97bc5b0  0000007fd97bc688  [stack]
         0000007fd97bc5b8  0000006fa58930b4  /data/app/com.sogou.translate.example-Sj02X6XFCw3dgjlkJnlrHw==/oat/arm64/base.odex
    #00  0000007fd97bc5c0  0000007042500d28  /dev/ashmem/dalvik-LinearAlloc (deleted)

彩蛋

Android studio 执行cmake 参数的配置文件为android.toolchain.cmake

具体位置如下:
/Users/feifei/Library/Android/sdk/cmake/3.6.4111459/android.toolchain.cmake

需要查看或者修改AS cmake参数的可以参照此文件

二、一些必备的ndk工具

工具位置:

/Android/sdk/ndk-bundle/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/

常用工具如下:

feifeideMacBook-Pro:bin feifei$ ls
aarch64-linux-android-addr2line aarch64-linux-android-elfedit   aarch64-linux-android-nm    aarch64-linux-android-size
aarch64-linux-android-ar    aarch64-linux-android-gprof aarch64-linux-android-objcopy   aarch64-linux-android-strings
aarch64-linux-android-as    aarch64-linux-android-ld    aarch64-linux-android-objdump   aarch64-linux-android-strip
aarch64-linux-android-c++filt   aarch64-linux-android-ld.bfd    aarch64-linux-android-ranlib
aarch64-linux-android-dwp   aarch64-linux-android-ld.gold   aarch64-linux-android-readelf

2.1、 aarch64-linux-android-addr2line

Addr2line 工具(它是标准的 GNU Binutils 中的一部分)是一个可以将指令的地址和可执行映像转换成文件名、函数名和源代码行数的工具

常用使用方式:

aarch64-linux-android-addr2line -f -C -e xxx.so 偏移地址

参数介绍:

-a --addresses:在函数名、文件和行号信息之前,显示地址,以十六进制形式。
-b --target=:指定目标文件的格式为bfdname。
-e --exe=:指定需要转换地址的可执行文件名。
-i --inlines : 如果需要转换的地址是一个内联函数,则输出的信息包括其最近范围内的一个非内联函数的信息。
-j --section=:给出的地址代表指定section的偏移,而非绝对地址。
-p --pretty-print:使得该函数的输出信息更加人性化:每一个地址的信息占一行。
-s --basenames:仅仅显示每个文件名的基址(即不显示文件的具体路径,只显示文件名)。
-f --functions:在显示文件名、行号输出信息的同时显示函数名信息。
-C --demangle[=style]:将低级别的符号名解码为用户级别的名字。
-h --help:输出帮助信息。
-v --version:输出版本号。

2.2、 aarch64-linux-android-nm

nm 是name的缩写,用于读取目标文件中的符号名称(可以理解为一些函数和全局变量)

aarch64-linux-android-nm libbreakpad-core.so
0000000000061090 b _ZZ65Java_com_sogou_translate_breakpad_BreakPadCore_initBreakpadNativeE2eh
00000000000426dc r _ZZN12_GLOBAL__N_114MinidumpWriter18WriteOSInformationEP15MDRawSystemInfoE9separator
000000000004245b r _ZZN12_GLOBAL__N_115MicrodumpWriter9LogAppendItEEvT_E3HEX
0000000000062720 b _ZZN15google_breakpad12_GLOBAL__N_127InstallAlternateStackLockedEvE13kSigStackSize
00000000000421d4 r _ZZN15google_breakpad16ExceptionHandler12GenerateDumpEPNS0_12CrashContextEE11no_pipe_msg
0000000000042204 r _ZZN15google_breakpad16ExceptionHandler12GenerateDumpEPNS0_12CrashContextEE3msg
00000000000422ab r _ZZN15google_breakpad16ExceptionHandler15sendKeyInfoBackEPcE3msg
0000000000042272 r _ZZN15google_breakpad16ExceptionHandler21WaitForContinueSignalEvE3msg
00000000000422de r _ZZN15google_breakpad16ExceptionHandler23readKeyInfoInSubProcessEvE3msg
0000000000042233 r _ZZN15google_breakpad16ExceptionHandler25SendContinueSignalToChildEvE19okToContinueMessage
0000000000042234 r _ZZN15google_breakpad16ExceptionHandler25SendContinueSignalToChildEvE3msg
00000000000455e0 r _ZZNK10__cxxabiv129__pointer_to_member_type_info9can_catchEPKNS_16__shim_type_infoERPvE12null_ptr_rep
00000000000455f0 r _ZZNK10__cxxabiv129__pointer_to_member_type_info9can_catchEPKNS_16__shim_type_infoERPvE12null_ptr_rep_0
00000000000271f8 W _ZdaPv

2.3、 aarch64-linux-android-strip

strip经常用来去除目标文件中的一些符号表、调试符号表信息,以减小程序的大小。

aarch64-linux-android-strip xxx.so

会strip掉 xxx.so中的符号信息和调试信息,覆盖原有的xxx.so

2.4、 aarch64-linux-android-readelf

2.4.1、什么是elf (Executable and Linkable Format)

目标文件有三种类型: 可重定位的对象文件(.o),可执行的对象文件,可被共享的对象文件(.so文件)

现代x86-64Linux和Unix系统使用“可执行可链接格式(Executable and Linkable Format,ELF) 来组织目标文件(如.so或可执行文件)

ELF格式的文件在Linux系统下有.axf、 .bin、 .elf、 .o、 .prx、 .puff、 .ko、 .mod和.so等等

ELF格式由以下组成:


elf格式
- .text 节里装载了程序的可执行机器码 
- .rodata 节里装载了只读数据
- .data 节里面装载了被初始化的数据,包括全局和静态C变量
- .bss 节里面装载了未被初始化的全局和静态C变量(在目标文件中只是占位符,不占空间)
- .symtab 或者 .dynsym 节里面装载了符号信息
- 以 .rel 打头的 节里面装载了重定位条目
- .debug 一个调试符号表,只有使用了-g参数编译时才会有,用于debug 
- .line 用于记录C源程序的行号和.text节中机器指令之间的映射,也是只有使用了-g参数编译时才会有 
- .strtab 或者 .dynstr 节里面装载了字符串信息(以null结尾的字符串信息)

2.4.2、readelf 读取elf信息

  • h 查看使用说明
  • all 会打印所有的elf信息
  • s 会打印符号表信息
aarch64-linux-android-readelf -all  libbreakpad-core.so 
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           AArch64
  Version:                           0x1
  Entry point address:               0x17980
  Start of program headers:          64 (bytes into file)
  Start of section headers:          2641088 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         8
  Size of section headers:           64 (bytes)
  Number of section headers:         35
  Section header string table index: 32

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .note.gnu.build-i NOTE             0000000000000200  00000200
       0000000000000024  0000000000000000   A       0     0     4
  [ 2] .hash             HASH             0000000000000228  00000228
       0000000000001358  0000000000000004   A       4     0     8
  [ 3] .gnu.hash         GNU_HASH         0000000000001580  00001580
aarch64-linux-android-readelf -s libbreakpad-core.so 
Symbol table '.dynsym' contains 715 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000017980     0 SECTION LOCAL  DEFAULT   11 
     2: 000000000005d200     0 SECTION LOCAL  DEFAULT   18 
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND sleep@LIBC (2)
     4: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND sem_wait@LIBC (2)
     5: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND sigemptyset@LIBC (2)
     6: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_create@LIBC (2)
     7: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND realloc@LIBC (2)
     8: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND gettid@LIBC (2)
     9: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND open@LIBC (2)
    10: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_key_create@LIBC (2)
    11: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND sem_post@LIBC (2)
    12: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND pthread_once@LIBC (2)
    13: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_finalize@LIBC (2)
    14: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND calloc@LIBC (2)

三、参考文章

https://www.jianshu.com/p/c2e2b8f8ea0d
https://zhuanlan.zhihu.com/p/62039158

你可能感兴趣的:(利用strip前和strip后的so定位crash)