Android NDK中结合汇编分析Crash行为

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/dizuo/article/details/8876698

1. Crash后 logcat中输出绿色信息:

05-02 10:14:37.130: I/DEBUG(1890): backtrace:
05-02 10:14:37.130: I/DEBUG(1890):     #00  pc 00033fda  /data/data/com.XXXXX.map/lib/libmapengine.so (TextureCache::_touchListNode(TextureCacheItem*)+25)
05-02 10:14:37.130: I/DEBUG(1890):     #01  pc 0003407d  /data/data/com.XXXXX.map/lib/libmapengine.so (TextureCache::getTexItem(char, char, int, int)+32)
05-02 10:14:37.130: I/DEBUG(1890):     #02  pc 00032c9f  /data/data/com.XXXXX.map/lib/libmapengine.so (prepareTiles(int, int, int, double)+158)
05-02 10:14:37.130: I/DEBUG(1890):     #03  pc 000332cf  /data/data/com.XXXXX.map/lib/libmapengine.so (nativePrepareRender+566)
05-02 10:14:37.130: I/DEBUG(1890):     #04  pc 0002fb79  /data/data/com.XXXXX.map/lib/libmapengine.so (Java_com_XXXXX_map_gl_JNI_nativePrepareRender+192)
05-02 10:14:37.130: I/DEBUG(1890):     #05  pc 0001de70  /system/lib/libdvm.so (dvmPlatformInvoke+112)
05-02 10:14:37.130: I/DEBUG(1890):     #06  pc 0004d0c3  /system/lib/libdvm.so (dvmCallJNIMethod(unsigned int const*, JValue*, Method const*, Thread*)+394)
05-02 10:14:37.130: I/DEBUG(1890):     #07  pc 000009e0  /dev/ashmem/dalvik-jit-code-cache (deleted)


2. 找到APP中对应的SO包,获取so的汇编源码

注意编译so包时需要注释mk文件中两句:

cmd-strip = $(TOOLCHAIN_PREFIX)strip --strip-all -x $1
-fvisibility=hidden

cmd-strip 是对编译符号进行过滤的脚本,-fvisibility=hidden 是隐藏jni库内部符号表

D:\android-ndk-r7c\toolchains\arm-linux-androideabi-4.4.3\prebuilt\windows\bin

下面的objdump工具,生成so包的汇编。
生成so包汇编代码的命令: arm-linux-androideabi-objdump.exe -dx libmapengine.so > temp.txt


3. 定位问题位置

如果幸运的话可以,logcat输出可以直接定位在函数,接下来要做的就是定位在错误的代码行数,注意指的是C/C++代码行 而不是汇编。

结合so的汇编和logcat输出,函数代码较短的话可以直接阅读arm汇编,函数长的话直接看汇编会很痛苦。


4. arm assemble的一些基本指令

ldr 从指定地址加载寄存器运算数,
str 将寄存器运算数存到指定地址,
add两个寄存器相加,
adds寄存器和数值相加,
mov寄存器之间赋值,
movs将数值赋给寄存器,
cmp为比较两个寄存器
比较条件判断:

b 表示无条件分支:http://sourceware.org/cgen/gen-doc/arm-thumb-insn.html#insn-b
bx lr 表示一个函数执行结束,参见【3】


5. 示例

C/C++ 源码如下:

void TextureCache::_touchListNode(TextureCacheItem* node)
{
	if (node==NULL) {
		return;
	}
	
	// 将*item移至队尾
	if(tail != node){
		// 将node结点单独取出
		if(head == node){
			head = head->next;
			head->pre = NULL;
		} else{	// node != head && node != tail
			node->pre->next = node->next;
			node->next->pre = node->pre;		// ###node->next为空,寻址pre导致CRASH###
		}

		tail->next = node;
		node->pre = tail;
		tail = node;
		tail->next = NULL;
	}
}
汇编代码一共28行:

00033fc0 <_ZN12TextureCache14_touchListNodeEP16TextureCacheItem>:
   33fc0:	2900      	cmp	r1, #0
   33fc2:	d012      	beq.n	33fea <_ZN12TextureCache14_touchListNodeEP16TextureCacheItem+0x2a>
   33fc4:	68c3      	ldr	r3, [r0, #12]
   33fc6:	428b      	cmp	r3, r1
   33fc8:	d00f      	beq.n	33fea <_ZN12TextureCache14_touchListNodeEP16TextureCacheItem+0x2a>
   33fca:	6883      	ldr	r3, [r0, #8]
   33fcc:	428b      	cmp	r3, r1
   33fce:	d00d      	beq.n	33fec <_ZN12TextureCache14_touchListNodeEP16TextureCacheItem+0x2c>
   33fd0:	694b      	ldr	r3, [r1, #20]
   33fd2:	698a      	ldr	r2, [r1, #24]
   33fd4:	619a      	str	r2, [r3, #24]
   33fd6:	698b      	ldr	r3, [r1, #24]
   33fd8:	694a      	ldr	r2, [r1, #20]
   33fda:	615a      	str	r2, [r3, #20]
   33fdc:	68c3      	ldr	r3, [r0, #12]
   33fde:	6199      	str	r1, [r3, #24]
   33fe0:	68c3      	ldr	r3, [r0, #12]
   33fe2:	614b      	str	r3, [r1, #20]
   33fe4:	2300      	movs	r3, #0
   33fe6:	60c1      	str	r1, [r0, #12]
   33fe8:	618b      	str	r3, [r1, #24]
   33fea:	4770      	bx	lr
   33fec:	698b      	ldr	r3, [r1, #24]
   33fee:	2200      	movs	r2, #0
   33ff0:	6083      	str	r3, [r0, #8]
   33ff2:	615a      	str	r2, [r3, #20]
   33ff4:	e7f2      	b.n	33fdc <_ZN12TextureCache14_touchListNodeEP16TextureCacheItem+0x1c>
   33ff6:	46c0      	nop			(mov r8, r8)

分析:

touchListNode函数将双向链表中的node结点移至队列尾部。

r0寄存器存放整个当前对象地址,r0 + 8 为head,r0 + 12为tail
r1存到函数参数node指针
指针即地址,即寄存器中的值

00033fc0 <_ZN12TextureCache14_touchListNodeEP16TextureCacheItem>:
   33fc0:	2900      	cmp	r1, #0
   33fc2:	d012      	beq.n	33fea <_ZN12TextureCache14_touchListNodeEP16TextureCacheItem+0x2a>
						第一次比较node是否为NULL,相等则直接跳至33fea行退出函数
						
   33fc4:	68c3      	ldr	r3, [r0, #12]			// 通过r0寄存器取tail指针
   33fc6:	428b      	cmp	r3, r1					// 比较tail和node指针
   33fc8:	d00f      	beq.n	33fea <_ZN12TextureCache14_touchListNodeEP16TextureCacheItem+0x2a>
						第二次比较tail是否等于node,相等则直接跳至33fea行退出函数
						
   33fca:	6883      	ldr	r3, [r0, #8]			// 通过r0寄存器取head指针
   33fcc:	428b      	cmp	r3, r1					// 比较head和node指针
   33fce:	d00d      	beq.n	33fec <_ZN12TextureCache14_touchListNodeEP16TextureCacheItem+0x2c>	// head!=node直接跳到33fec行
						第三次比较head是否等于node,相等则直接跳至33fea行退出函数
						
   33fd0:	694b      	ldr	r3, [r1, #20]	// r3 = r1::_pre 	取node的pre指针赋给r3
   33fd2:	698a      	ldr	r2, [r1, #24]	// r2 = r1::_next 	取node的next指针赋给r2
   33fd4:	619a      	str	r2, [r3, #24]	// r3::_next = r2	node->pre->next = node->next
   33fd6:	698b      	ldr	r3, [r1, #24]	// r3 = r1::_next;	
   33fd8:	694a      	ldr	r2, [r1, #20]	// r2 = r1::_pre;
   33fda:	615a      	str	r2, [r3, #20]	// r3::_pre = r2;	node->next->pre = node->pre
   
   33fdc:	68c3      	ldr	r3, [r0, #12]	// r3 = tail;		取链表的tail赋给r3
   33fde:	6199      	str	r1, [r3, #24]	// r3::_next = r1	tail->next = node
   33fe0:	68c3      	ldr	r3, [r0, #12]	// r3 = tail;		取链表的tail赋给r3
   33fe2:	614b      	str	r3, [r1, #20]	// r1::_pre = r3;	node->pre = tail
   33fe4:	2300      	movs	r3, #0		// reset r3 register 	清零r3寄存器
   33fe6:	60c1      	str	r1, [r0, #12]	// tail = node;		将r1(node)赋给r0+12即tail
   33fe8:	618b      	str	r3, [r1, #24]	// r1::_next = r3;	将r3赋给r1的next指针,此时r3等于0	
   33fea:	4770      	bx	lr				// 子函数 执行结束!
   
   33fec:	698b      	ldr	r3, [r1, #24]	// r3 = r1::_next;
   33fee:	2200      	movs	r2, #0		// reset r2 register
   33ff0:	6083      	str	r3, [r0, #8]	// r0::_head = r3;
   33ff2:	615a      	str	r2, [r3, #20]	// r3::_pre = r2;	## r2==0 ##
   33ff4:	e7f2      	b.n	33fdc <_ZN12TextureCache14_touchListNodeEP16TextureCacheItem+0x1c>
						无条件跳转到33fdc行执行
						
   33ff6:	46c0      	nop			(mov r8, r8)

结合第一部分crash时堆栈顶部信息:#00  pc 00033fda ,对应汇编代码中的33fda行,通过阅读汇编代码可以知道33fda行对应C/C++源码:
node->next->pre = node->pre;

Crash原因是因为[r3, #20]寻址错误 即node->next为空并且执行node->next->pre。


6. 参考:

1. http://sourceware.org/cgen/gen-doc/arm-thumb-insn.html

2. http://www.peter-cockerell.net/aalp/html/ch-3.html

3. http://hi.baidu.com/wuqi19881003/item/f293c7a7e228e613a8cfb756


猜你喜欢

转载自blog.csdn.net/dizuo/article/details/8876698