[置顶] Android(安卓)NDK中结合汇编分析Crash行为
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 != tailnode->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 cmpr1, #0 33fc2:d012 beq.n33fea <_ZN12TextureCache14_touchListNodeEP16TextureCacheItem+0x2a> 33fc4:68c3 ldrr3, [r0, #12] 33fc6:428b cmpr3, r1 33fc8:d00f beq.n33fea <_ZN12TextureCache14_touchListNodeEP16TextureCacheItem+0x2a> 33fca:6883 ldrr3, [r0, #8] 33fcc:428b cmpr3, r1 33fce:d00d beq.n33fec <_ZN12TextureCache14_touchListNodeEP16TextureCacheItem+0x2c> 33fd0:694b ldrr3, [r1, #20] 33fd2:698a ldrr2, [r1, #24] 33fd4:619a strr2, [r3, #24] 33fd6:698b ldrr3, [r1, #24] 33fd8:694a ldrr2, [r1, #20] 33fda:615a strr2, [r3, #20] 33fdc:68c3 ldrr3, [r0, #12] 33fde:6199 strr1, [r3, #24] 33fe0:68c3 ldrr3, [r0, #12] 33fe2:614b strr3, [r1, #20] 33fe4:2300 movsr3, #0 33fe6:60c1 strr1, [r0, #12] 33fe8:618b strr3, [r1, #24] 33fea:4770 bxlr 33fec:698b ldrr3, [r1, #24] 33fee:2200 movsr2, #0 33ff0:6083 strr3, [r0, #8] 33ff2:615a strr2, [r3, #20] 33ff4:e7f2 b.n33fdc <_ZN12TextureCache14_touchListNodeEP16TextureCacheItem+0x1c> 33ff6:46c0 nop(mov r8, r8)
分析:
touchListNode函数将双向链表中的node结点移至队列尾部。r0寄存器存放整个当前对象地址,r0 + 8 为head,r0 + 12为tailr1存到函数参数node指针指针即地址,即寄存器中的值00033fc0 <_ZN12TextureCache14_touchListNodeEP16TextureCacheItem>: 33fc0:2900 cmpr1, #0 33fc2:d012 beq.n33fea <_ZN12TextureCache14_touchListNodeEP16TextureCacheItem+0x2a>第一次比较node是否为NULL,相等则直接跳至33fea行退出函数 33fc4:68c3 ldrr3, [r0, #12]// 通过r0寄存器取tail指针 33fc6:428b cmpr3, r1// 比较tail和node指针 33fc8:d00f beq.n33fea <_ZN12TextureCache14_touchListNodeEP16TextureCacheItem+0x2a>第二次比较tail是否等于node,相等则直接跳至33fea行退出函数 33fca:6883 ldrr3, [r0, #8]// 通过r0寄存器取head指针 33fcc:428b cmpr3, r1// 比较head和node指针 33fce:d00d beq.n33fec <_ZN12TextureCache14_touchListNodeEP16TextureCacheItem+0x2c>// head!=node直接跳到33fec行第三次比较head是否等于node,相等则直接跳至33fea行退出函数 33fd0:694b ldrr3, [r1, #20]// r3 = r1::_pre 取node的pre指针赋给r3 33fd2:698a ldrr2, [r1, #24]// r2 = r1::_next 取node的next指针赋给r2 33fd4:619a strr2, [r3, #24]// r3::_next = r2node->pre->next = node->next 33fd6:698b ldrr3, [r1, #24]// r3 = r1::_next; 33fd8:694a ldrr2, [r1, #20]// r2 = r1::_pre; 33fda:615a strr2, [r3, #20]// r3::_pre = r2;node->next->pre = node->pre 33fdc:68c3 ldrr3, [r0, #12]// r3 = tail;取链表的tail赋给r3 33fde:6199 strr1, [r3, #24]// r3::_next = r1tail->next = node 33fe0:68c3 ldrr3, [r0, #12]// r3 = tail;取链表的tail赋给r3 33fe2:614b strr3, [r1, #20]// r1::_pre = r3;node->pre = tail 33fe4:2300 movsr3, #0// reset r3 register 清零r3寄存器 33fe6:60c1 strr1, [r0, #12]// tail = node;将r1(node)赋给r0+12即tail 33fe8:618b strr3, [r1, #24]// r1::_next = r3;将r3赋给r1的next指针,此时r3等于0 33fea:4770 bxlr// 子函数 执行结束! 33fec:698b ldrr3, [r1, #24]// r3 = r1::_next; 33fee:2200 movsr2, #0// reset r2 register 33ff0:6083 strr3, [r0, #8]// r0::_head = r3; 33ff2:615a strr2, [r3, #20]// r3::_pre = r2;## r2==0 ## 33ff4:e7f2 b.n33fdc <_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。
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
更多相关文章
- Android游戏框架AndEngine使用入门
- Android将InputStream转换为String和byte[]
- Android下AlarmManager完整调用流程
- Android(安卓)Audio代码分析20 - queryEffects函数
- [Android] Smali基本语法
- Rokon引擎主要类介绍
- Android(安卓)Activity返回结果startActivityForResult函数
- untiy实现手机摇一摇震动效果
- NDK开发之Android(安卓)Studio生成.h头文件的方法