DATA abort定位方法
来源:互联网 发布:数据库的三范式是什么 编辑:程序博客网 时间:2024/05/22 02:14
这是我一直收藏的一篇文章,出处已经无法知道。
根据自己实践增加了部分说明,现在分享出来。
该方法只能定位显性 泄漏,定位到的C语句一定产生泄漏了,但可能这个位置是 “ 理论上 ” 不会出问题的代码.那么这是由于同进程内其他代码泄漏而影响了进程的堆区或栈区(隐性泄漏,这个地方不会产生data abort exception),然后被定位出来的代码才被动地显性泄露, 产生data abort 。
首先在DEBUG版本中定位DATA ABORT的方法,地球人应该都知道了吧,我就不废话了。Platform Builder或VS2005、EVC这类IDE工具会在DEBUG模式下自动停在出错的那句,情况就很显然了。
RELEASE版本下的泄漏就要稍微麻烦一点,如何快速定位呢?
案例一:用EVC编译的应用程序泄漏
首先我做了一个内存泄漏的程序MemoryLeakTest.exe,里面做了一个泄漏的函数:
- void MemoryLeak()
- {
- int *p = (int *)0x81000000;
- *p = 10;
- }
编译的时候注意先在project settings里的Link页里勾选“Generate mapfile”,会生成一个map文件。
另外,VS2005/VS2008也可以在工程属性中找到类似的选项,然后指定 MAP 文件名即可。
把编译出来的RELEASE版本可执行文件放到CE5平台下运行。出错的时候串口打印了一句
Data Abort: Thread=83ad1d38 Proc=820266d0 'MemoryLeakTest.exe'
AKY=00000021 PC=00011008(MemoryLeakTest.exe+0x00001008) RA=00011030(MemoryLeakTest.exe+0x00001030) BVA=81000000 FSR=0000000d
这句是系统自动输出的。我们得到了一个关键的信息:PC指针。和PC指针在MemoryLeakTest.exe中的偏移量。然后打开编译时生成的MemoryLeakTest.map文件,文件内容如下:
MemoryLeakTest
Timestamp is 46fcbb17 (Fri Sep 28 16:28:07 2007)
Preferred load address is 00010000
Start Length Name Class
0001:00000000 00000258H .text CODE
0002:00000000 00000014H .xdata DATA
0002:00000014 00000014H .idata$2 DATA
0002:00000028 00000014H .idata$3 DATA
0002:0000003c 00000010H .idata$4 DATA
0002:0000004c 0000000cH .idata$6 DATA
0002:00000058 00000000H .edata DATA
0003:00000000 00000010H .idata$5 DATA
0003:00000010 00000004H .CRT$XCA DATA
0003:00000014 00000004H .CRT$XCZ DATA
0003:00000018 00000004H .CRT$XIA DATA
0003:0000001c 00000004H .CRT$XIZ DATA
0003:00000020 00000004H .CRT$XPA DATA
0003:00000024 00000004H .CRT$XPZ DATA
0003:00000028 00000004H .CRT$XTA DATA
0003:0000002c 00000004H .CRT$XTZ DATA
0003:00000030 00000009H .bss DATA
0004:00000000 00000038H .pdata DATA
0005:00000000 00000010H .rsrc$01 DATA
0005:00000010 00000000H .rsrc$02 DATA
Address Publics by Value Rva+Base Lib:Object
0001:00000000 ?MemoryLeak@@YAXXZ 00011000 f MemoryLeakTest.obj
0001:00000010 WinMain 00011010 f MemoryLeakTest.obj
0001:0000002c WinMainCRTStartup 0001102c f corelibc:pegwmain.obj
0001:000000a0 _cinit 000110a0 f corelibc:crt0dat.obj
0001:00000210 exit 00011210 f corelibc:crt0dat.obj
0001:00000228 _XcptFilter 00011228 f coredll:COREDLL.dll
0001:00000238 __C_specific_handler 00011238 f coredll:COREDLL.dll
0001:00000248 LocalFree 00011248 f coredll:COREDLL.dll
0002:00000014 __IMPORT_DESCRIPTOR_COREDLL 00012014 coredll:COREDLL.dll
0002:00000028 __NULL_IMPORT_DESCRIPTOR 00012028 coredll:COREDLL.dll
0003:00000000 __imp___C_specific_handler 00013000 coredll:COREDLL.dll
0003:00000004 __imp_LocalFree 00013004 coredll:COREDLL.dll
0003:00000008 __imp__XcptFilter 00013008 coredll:COREDLL.dll
0003:0000000c /177COREDLL_NULL_THUNK_DATA 0001300c coredll:COREDLL.dll
0003:00000010 __xc_a 00013010 corelibc:crt0init.obj
0003:00000014 __xc_z 00013014 corelibc:crt0init.obj
0003:00000018 __xi_a 00013018 corelibc:crt0init.obj
0003:0000001c __xi_z 0001301c corelibc:crt0init.obj
0003:00000020 __xp_a 00013020 corelibc:crt0init.obj
0003:00000024 __xp_z 00013024 corelibc:crt0init.obj
0003:00000028 __xt_a 00013028 corelibc:crt0init.obj
0003:0000002c __xt_z 0001302c corelibc:crt0init.obj
0003:00000030 __onexitend 00013030 <common>
0003:00000034 __onexitbegin 00013034 <common>
0003:00000038 _exitflag 00013038 <common>
entry point at 0001:0000002c
Static symbols
0001:0000010c doexit 0001110c f corelibc:crt0dat.obj
OK,首先,里面有一句“Preferred load address is 00010000”,这意味着DATA ABORT那句的PC=00011008(MemoryLeakTest.exe+0x00001008) 我们必须把括号里的0x1008加上这个load address的偏移量,得到0x11008(注意不能直接用PC,一会儿再给个案例就知道了),然后我们在函数偏移列表里看Rva+Base这栏,找到0x11008落在了MemoryLeak函数的地址范围里,所以是MemoryLeak函数泄漏了。
案例二:在OAL层做了一个泄漏的函数,用Platform Builder进行RELEASE版编译并LINK到NK.exe里,然后应用程序MemoryLeakPB.exe调用该函数导致泄漏
步骤类似,只是Platform Builder默认就会在RELEASE目录下生成.map文件。应用程序去调用泄漏的OAL函数时,出现
Data Abort: Thread=822fc000 Proc=820267c0 'MemoryLeakPB.exe'
AKY=00000041 PC=8023e3c8(NK.EXE+0x0003e3c8) RA=8023e1d4(NK.EXE+0x0003e1d4)
BVA=8e000000 FSR=00000005
注意我们这次是NK.EXE里制造泄漏,所以PC指针不是在0x00011008这样的Slot 0低地址了,而是在0x80000000以上的KERNEL区域了。
nk.map很长,我选择关键段落来贴
************************************************************
kern
Timestamp is 46fca696 (Fri Sep 28 15:00:38 2007)
Preferred load address is 00010000
...
0001:0003d2c8 OALIoCtlHal_GetDeviceId 0004e2c8 f oal_ioctl:deviceid.obj
0001:0003d398 OALIoCtlHal_MemoryLeak 0004e398 f oal_ioctl:leaktest.obj
0001:0003d438 OALIoCtlHal_DdkCall 0004e438 f oal_io:ioctl.obj
...
************************************************************
所以 NK.EXE + 0x0003e3c8 = 0x10000(Preferred load address) + 0x0003e3c8 = 0x4e3c8, 落在了OALIoCtlHal_MemoryLeak函数里
结论:原理上很简单,就是利用DATA ABORT消息中的PC值,配合MAP文件可以快速定位到泄漏的函数。定位到之后,嘿嘿,谁LEAK谁请客咯。
用上文的方法,不但网友kevin没有成功定位到泄漏的原因(见后面回帖,他定位到微软USBFN的MDD层代码),我这项目组里的人也没抓到原因(定位到PRIVATE下LoadLibrary函数相关的代码)。昨晚我想了下,这个方法的确有漏洞,早上我做了个试验,建立个DialogBox,主处理函数如下
- static DWORD *gpTest = NULL;
- int CALLBACK LeakProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
- {
- switch(uMsg)
- {
- case WM_INITDIALOG:
- gpTest = (DWORD*)malloc(1);
- break;
- case WM_COMMAND:
- switch(wParam)
- {
- case ID_LEAK:
- *((int*)0x1A013860) = 0x1C000000;
- break;
- case ID_CLOSE:
- if(gpTest)
- {
- *gpTest = 1;
- }
- EndDialog(hDlg, 0);
- break;
- }
- break;
- }
- return FALSE;
- }
我运行了几次,每次该进程的BaseAddr = 0x1A000000, 而 &gpTest = 0x1A013860,在进程的全局变量区; malloc 之后 gpTest = 0x00030230 在进程的堆区。所以我 :
(1) 在ID_LEAK 按钮按下后,模拟一次内存泄漏,直接对&gpTest 地址上的数值改写,把malloc 后的0x00030230 改成 0x00000000, 注意虽然该处已经泄漏了,但是并没有产生 data abort exception.
(此处当然不会出错!如果出错,那么给gpTest赋值为空时一样会出错)
(2) 然后在ID_CLOSE 按钮按下后,装做不知道前面那处泄漏,代码风格严谨地先判断下指针是否为空,然后试图在malloc 得到的堆区 0x00030230 地址上写个值,但是由于 gpTest 这个指针已经被改写为指向其他进程空间了,所以在 *gpTest = 1这句产生 data abort exception并停下来了。
(说明 gpTest 的值被误修改了,否则不会出错)
(3) 所以,按照上文的方法,只能抓到*gpTest = 1 这句泄露,而实际上这句是无辜的,真正的元凶 *((int*)0x1A013860) = 0x1C000000这句却没有被抓出来。
DataAbort简单分析
DataAbort中止表示当前存储器访问不能被完成。
有两种类型的中止:
预取指中止: 发生在指令预取指过程中
数据中止: 发生在对数据访问时
1.预取指中止
当发生预取指中止时ARM7TDMI-S内核将预取的指令标记为无效但在指令到达流水线的执行阶段时才进入异常,如果指令在流水线中因为发生分支而没有被执行,中止将不会发生。
在处理中止的原因之后不管处于哪种处理器操作状态处理程序都会执行下面的指令
SUBSPC,R14_abt,#4
这个动作恢复了PC和CPSR并重试被中止的指令
2.数据中止
当发生数据中止时根据指令的类型产生不同的动作
●数据转移指令LDR,STR回写到被修改的基址寄存器中止处理程序必须注意这一点
●交还指令SWP中止好像没有被执行过一样中止必须发生在SWP指令进行读访问时
●块数据转移指令LDM,STM完成 当回写被设置时基址寄存器被更新在指示出现中止后。ARM7TDMI-S内核防止所有寄存器被覆盖这意味着ARM7TDMI-S内核总是会保护被中止的LDM指令中的r15(总是最后一个被转移的寄存器)
中止的机制使指令分页的虚拟存储器系统能够被实现。在这样一个系统中,处理器允许产生仲裁地址。当某一地址的数据无法访问时,存储器管理单元MMU通知产生了中止,中止处理程序必须找出中止的原因,使请求的数据可以被访问并重新执行被中止的指令。应用程序不必知道可用存储器的数量,也不必知道它的被中止时所处的状态。在修复产生中止的原因后,不管处于哪种处理器操作状态,处理程序都必须执行下面的返回指令 SUBSPC,R14_abt,#8 这个动作恢复了PC和CPSR并重试被中止的指令
- DATA abort定位方法
- DATA abort定位方法
- DATA abort定位方法
- DATA abort定位方法
- DATA abort定位方法
- 在RELEASE版本中快速定位DATA ABORT的方法
- 在RELEASE版本中快速定位DATA ABORT的方法
- 在debug版本中定位DATA ABORT
- WinCE中的Data/ Prefetch Abort异常定位
- data abort
- 如何定位WinCE产生Data Abort错误的位置
- 如何定位WinCE产生Data Abort错误的位置(转载)
- 如何定位WinCE产生Data Abort错误的位置
- 如何定位WinCE产生Data Abort错误的位置
- 如何定位WinCE产生Data Abort错误的位置
- 如何定位WinCE产生Data Abort错误的位置
- 如何定位WinCE产生Data Abort错误的位置
- WinCE中的Data/ Prefetch Abort等异常定位
- Android美女拼图游戏
- a:-webkit-any-link是什么意思
- Socket套接字
- Android控件的隐藏与显示
- html中input不可用或是只读的几种方式和区别
- DATA abort定位方法
- Spring Boot 环境变量读取 和 属性对象的绑定
- Android ViewDragHelper完全解析 自定义ViewGroup神器
- ABAP report printer的基本概念介绍
- 第2.1.2章 WEB系统最佳实践Spring文件配置之spring-common.xml
- 洛谷1373小a和uim之大逃离
- 第十章 Oracle 触发器
- sdnuACM1012区间合并问题
- 修饰符 transient的用法