1、准备材料
-
android-ndk-r21b工具:addr2line和objdump工具。
-
.so库:crash对应的带符号表的共享库
-
源代码code:crash对应的源码文件
-
tombstone:Android系统产生的墓碑文件
2、 tombstone墓碑文件分析
关键信息:
-
crash的进程id(pid)和线程id(tid)
-
错误类型是段错误(SIGSEGV)和错误地址(0x0000007172d3a000)
-
crash的事故现场,即x0-x31寄存器的值
-
crash的backtrace信息
其中,段错误(SIGSEGV)一般是由于访问无效内存引起,错误地址(0x0000007172d3a000)和 x0-x31 寄存器的值在后续的反汇编分析中有用;backtrace可以看到crash前的调用栈。详细的分析过程如下:
crash时,PC寄存器的值是00000072d376d468,指向代码最后执行的绝对地址,其相对地址是backtrace中的"#00 pc 0000000000138468"的0000000000138468,两个地址相减,得到.so的起始加载地址00000072d376d468-0000000000138468=72d3635000,72d3635000是一个绝对地址。通过查看tombstone文件中的memory map地址映射表,我们可以看到.so的地址映射范围是:
其中的起始地址就是我们刚才计算的00000072d3635000。"rwx"分别表示可读、可写、可执行权限。"---"表示无权限,即无效地址区域。
其次,通过bactrace可以得到crash前的调用栈,如下图所示:
上述的地址信息看起来并不直观,可以使用ndk-stack工具(在android-ndk/ndk-stack路径下,是对addr2line工具的包装)将地址转换为方便阅读的代码:
ndk-stack -sym /Users/lijian/work/Crash/obj/local/arm64-v8a/libmorpho_Lowlight.so -i /Users/lijian/work/Crash/tombstone_03 > backtrace.txt
由此我们得到crash前完整的调用路径,以及代码最后crash的位置是多少行:
可以从源代码中找到相应的行数。
3、反编译
最后,我们来分析段错误地址0x0000007172d3a000。分析这个地址的产生需要进行反汇编分析,我们可以使用objdump工具得到.so对应的汇编代码:
./bin/aarch64-linux-android-objdump -d /Users/lijian/work/Crash/obj/local/arm64-v8a/libmorpho_Lowlight.so > asm.txt
打开asm.txt,并定位到代码crash的位置,即backtrace中的第一个地址"#00 pc 0000000000138468"中的0000000000138468,如下图所示:
crash对应的汇编代码是"ldrb q1, [x5]#16",该汇编指令从地址x5+16的地方加载一个字节。通过查看tombstone文件中的x24寄存器的值,可以知道x5=0000007172d3a000。如下图所示:
x5+16 = 0000007172d3a000 +16=7172d3a016,刚好得到段错误的地址7172d3a000。通过进一步查看memory map,可以看到地址是非法的地址区间,如下图所示:
综上,我们可以确认crash产生的原因是出现了地址越界,通过进一步分析代码,进而解决问题。
参考连接:Android Native crash问题分析 (qq.com)