nativeCrash4AnalysisRootCause

汇编

ARM汇编指令集汇总

基本语法

汇编语言的主体是汇编指令。汇编指令和机器指令的差别在于指令的表示方法上。汇编指令是机器指令便于记忆的书写格式。

#操作:寄存器BX的内容送到AX中
1000100111011000              #机器指令
mov ax,bx                   #汇编指令

汇编语言语句的语法 汇编语言语句输入每行一个语句。每个语句如下的格式如下:

[label]   mnemonic   [operands]   [;comment]

方括号中的字段是可选的。基本指令有两部分组成,第一个是要执行的指令(助记符)的名称和所述第二命令的操作数或参数的。

以下是一些典型的汇编语言语句的例子:

INC COUNT        ; Increment the memory variable COUNT
MOV TOTAL, 48    ; Transfer the value 48 in the 
                 ; memory variable TOTAL
ADD AH, BH       ; Add the content of the 
                 ; BH register into the AH register
AND MASK1, 128   ; Perform AND operation on the 
                 ; variable MASK1 and 128
ADD MARKS, 10    ; Add 10 to the variable MARKS
MOV AL, 10       ; Transfer the value 10 to the AL register

disassembler tool

IDAPro is best disassembler tool for many processors and file types. HexRays ARM - plugin for IDAPro (doesn’t work separately), which trying to decompile assembler to C-like source code

both not free

https://www.hex-rays.com/index.shtml

nativeCrash objdump分析流程:

分析步骤

首先查看如下关键信息进行方向确定

“signal”: “11 (SIGSEGV)”,

“code”: “2 (SEGV_ACCERR)”,

“fault addr”: “0x15049024”,

“backtrace”: “#00 pc 0017c126 /system/lib/libart.so (_ZN3art2gc9allocator8RosAlloc8BulkFreeEPNS_6ThreadEPPvj+149)\n”,

“java stacktrace”: [ “at dalvik.system.VMRuntime.runHeapTasks(Native method)”,

  1. 根据tombstone找到对应的出错方法名,在objdump之后的反汇编文件中找到该方法(根据pc值或方法签名(例如_ZN3art2gc9allocator8RosAlloc8BulkFreeEPNS_6ThreadEPPvj)找到方法定义)
  2. 根据tombstone中backtrace的pc信息找到对应的指令执行位置
  3. 结合源码和反编译objdump指令以及上一步得到的出错位置, 向上寻找造成问题的原因

分析例子1

  1. xc_test_call_4
  2. c2df
    rax 0000000000000000  rbx 0000743b3f05f200  rcx 0b8b8de9d74c4547  rdx 0000743b50a00000
    r8  ffffffffffffffe0  r9  0000000000000000  r10 0000000000000022  r11 0000000000000206
    r12 0000743b3ff4c410  r13 0000743b5c2451d0  r14 0000743b3ff4c630  r15 0000743b3ff4c630
    rdi 0000000000000003  rsi 0000743b50b6f940
    rbp 0000743b3ff4c110  rsp 0000743b3ff4c100  rip 0000743b569942df
backtrace:
    #00 pc 000000000000c2df  /data/app/xcrash.sample-1/lib/x86_64/libxcrash.so (xc_test_call_4+31)
    #01 pc 000000000000c315  /data/app/xcrash.sample-1/lib/x86_64/libxcrash.so (xc_test_call_3+21)
    #02 pc 000000000000c345  /data/app/xcrash.sample-1/lib/x86_64/libxcrash.so (xc_test_call_2+21)
    #03 pc 000000000000c371  /data/app/xcrash.sample-1/lib/x86_64/libxcrash.so (xc_test_call_1+17)
    #04 pc 000000000000c412  /data/app/xcrash.sample-1/lib/x86_64/libxcrash.so (xc_test_crash+130)
    #05 pc 0000000000267f23  /data/app/xcrash.sample-1/oat/x86_64/base.odex


int xc_test_call_4(int v)
{
    int *a = NULL;
    xc_test_set_abort_msg();
    *a = v; // crash!
    (*a)++;
    v = *a;
    return v;
}

000000000000c2c0 <xc_test_call_4>:
    c2c0:	55                   	push   %rbp
    c2c1:	48 89 e5             	mov    %rsp,%rbp
    c2c4:	48 83 ec 10          	sub    $0x10,%rsp
    c2c8:	89 7d fc             	mov    %edi,-0x4(%rbp)
    c2cb:	48 c7 45 f0 00 00 00 	movq   $0x0,-0x10(%rbp)
    c2d2:	00 
    c2d3:	e8 38 02 00 00       	callq  c510 <xc_test_set_abort_msg>
    c2d8:	8b 7d fc             	mov    -0x4(%rbp),%edi
    c2db:	48 8b 45 f0          	mov    -0x10(%rbp),%rax
    c2df:	89 38                	mov    %edi,(%rax) //尝试从rax中提取数据时,由于地址为0报错
    c2e1:	48 8b 45 f0          	mov    -0x10(%rbp),%rax
    c2e5:	8b 38                	mov    (%rax),%edi
    c2e7:	83 c7 01             	add    $0x1,%edi

分析例子2

tombstone->
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x4798
    r0  00004780  r1  00000780  r2  00000060  r3  e0cbc018
    r4  e71f01d0  r5  db53b4b0  r6  db53b4a8  r7  00000000
    r8  e71f01d4  r9  e71efa10  r10 e7cd94ec  r11 e16ff848
    ip  b9839850  sp  e16ff830  lr  b951a62c  pc  b9519f10

backtrace:
    #00 pc 000a3f10  /data/data/com.test/files/testapp

stack:
         e16ff7f0  00000000
         e16ff7f4  00000000
         e16ff7f8  00000002
         e16ff7fc  ffffffff
         e16ff800  e0c8f1c0  [anon:libc_malloc]
         e16ff804  e16ff8a8  <anonymous:e1603000>
         e16ff808  e0c83640  [anon:libc_malloc]
         e16ff80c  0000c50b
         e16ff810  e71e6900  [anon:libc_malloc]
         e16ff814  00000000
         e16ff818  e71efa17  [anon:libc_malloc]
         e16ff81c  e71efa18  [anon:libc_malloc]
         e16ff820  dfea3014  [anon:libc_malloc]
         e16ff824  00000000
         e16ff828  acb0ac7c
         e16ff82c  00000000
    #00  e16ff830  00000000
         e16ff834  b98c1694  <anonymous:b983a000>
         e16ff838  00400014
         e16ff83c  00004000
         e16ff840  00004780
         e16ff844  80140000 //fp-4,所以fp-2是0x8014(注意这里是小端字节序)
         e16ff848  e16ff8b8  <anonymous:e1603000> //fp地址
         e16ff84c  b951a62c  /data/data/com.test/files/testapp


#代码示例,做了简化,方便理解
struct entry *testGetEntryById(unsigned short Id)
{
    ...
    blockId = Id >> 9;
    blockOffset = Id & (1 << 9 - 1);
    
    block = g_Table.EntryBlock[blockId];//问题出在这一行
    if (block == NULL)
    {
        return NULL
    }

    entry = &block->Entry[blockOffset];
    if (!entry->Valid)	
    {
        return NULL;
    }
    ...
}
objdump-->
000a3e54 <testGetEntryById>:
   a3e54:       e92d4800        push    {fp, lr}
   a3e58:       e1a0b00d        mov     fp, sp
   a3e5c:       e24dd018        sub     sp, sp, #24
   a3e60:       e59f10d0        ldr     r1, [pc, #208]  ; a3f38 <testGetEntryById+0xe4>
   a3e64:       e08f1001        add     r1, pc, r1
   a3e68:       e2811004        add     r1, r1, #4
   a3e6c:       e14b00b2        strh    r0, [fp, #-2] //fp-2位置存放了入参Id
...//省略
   a3f08:       e50b0008        str     r0, [fp, #-8]
   a3f0c:       e51b0008        ldr     r0, [fp, #-8]
   a3f10:       e5900018        ldr     r0, [r0, #24]	//问题发生位置

#第一列对应tombstone文件中的pc值,也就是so文件的相对地址
#在tombstone中可以找到fp(即r11)为e16ff848
#在stack中可以找到fp-2的值为0x8014。

#Id = 32788(对应16进制数0x8014), 右移9bit后是64,即blockId是64。但是g_Table.EntryBlock[]数组大小为64, index范围 0~63。64超出数组范围,造成访问溢出,取到了错误的block地址。从而导致的最终的问题发生。

其他

SIGSEGV Code定义

https://elixir.bootlin.com/linux/latest/source/include/uapi/asm-generic/siginfo.h#L223

/*
 * SIGSEGV si_codes
 */
#define SEGV_MAPERR	1	/* address not mapped to object */
#define SEGV_ACCERR	2	/* invalid permissions for mapped object */
#define SEGV_BNDERR	3	/* failed address bound checks */
#ifdef __ia64__
# define __SEGV_PSTKOVF	4	/* paragraph stack overflow */
#else
# define SEGV_PKUERR	4	/* failed protection key checks */
#endif
#define SEGV_ACCADI	5	/* ADI not enabled for mapped object */
#define SEGV_ADIDERR	6	/* Disrupting MCD error */
#define SEGV_ADIPERR	7	/* Precise MCD exception */
#define SEGV_MTEAERR	8	/* Asynchronous ARM MTE error */
#define SEGV_MTESERR	9	/* Synchronous ARM MTE exception */
#define NSIGSEGV	9

###