在程序异常时,通常通过debug调试的方法找原因。在非调试的模式下或者实际应用中,程序出现崩溃,当程序比较复杂,通过查看代码不容易查出问题。
在evc和vs2005的工具中,可以通过生成map(映射文件)和cod(程序集、机器码、源代码)文件查找异常地址。map可定位在发生崩溃的函数,code文件可定位在具体函数中的某一行。
在evc中配置设置如下
生成Cod文件:在Project/Setting/C/C++/Listing file type中,选择Assembli,Machine Code,and Source。如何1.1.
生成Map文件:在Project/Setting/Link中,在Generate mapfile选项中打勾。如图1.2
图1.1
图1.2
在vs2005中配置设置如下
生成Cod文件:在属性à配置属性àC/C++à输出文件中,选择程序集、机器码和原代码(/FAcs)。如图1.3
生成Map文件:在属性à配置属性à链接器à调试à生成映射文件选择是(/NAP).如图1.4
图1.3
图1.4
设置该2项后,编译工程,会生成.cod和.map文件。这些文件就是用来查找异常地址。
下面举个例子:
void CHelloDlg::Fun(char *pbuf)
{
*pbuf = 1;
}
// CHelloDlg 消息处理程序
BOOL CHelloDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
char *p = 0;
Fun(p);
return TRUE; // 除非将焦点设置到控件,否则返回TRUE
}
很容易看出来,在函数Fun中pbuf是参数p没有申请空间,所以*pbuf = 1会异常。
所以在运行时出现如下异常:
PC=0001119c(Hello.exe+0x0000119c) RA=00011194(Hello.exe +0x00001194) SP=0004f620, BVA=00000000
在这里我们要注意在Hello.map文件中,0x0000119c需要加上Preferred load address后来查找接近的地址,最接近的小的那个值为函数的开始地址,
这里面最关键的信息是PC和RA给出的地址信息。PC就是上面提到的崩溃地址,根据这个地址可以定位到导致崩溃的源代码行;RA是PC的返回地址(Return Address),根据这个地址可以找到导致崩溃的上一级函数。除了PC和RA,其他信息也可以提供一些参考作用:BVA是ARM中的Fault Address Register(FAR),是引起Data Abort的虚拟地址,比如说你的程序试图访问一个非法地址里的内容,那么Data Abort时BVA就是这个非法地址;FSR是Fault Status Register,指明导致异常的原因。
1、查找异常的函数:
打开Hello.map文件,内容如下(截取部分)
Address Publics by Value Rva+Base Lib:Object
0001:0000015c ?DoDataExchange@CHelloDlg@@MAAXPAVCDataExchange@@@Z 0001115c f HelloDlg.obj
0001:0000015c ?Serialize@CObject@@UAAXAAVCArchive@@@Z 0001115c f Hello.obj
0001:0000015c ?OnBnClickedButton1@CHelloDlg@@QAAXXZ 0001115c f HelloDlg.obj
0001:0000015c ?DoDataExchange@CWnd@@MAAXPAVCDataExchange@@@Z 0001115c f HelloDlg.obj
0001:00000160 ?OnInitDialog@CHelloDlg@@MAAHXZ 00011160 f HelloDlg.obj
0001:000001a8 ?GetMessageMap@CHelloDlg@@MBAPBUAFX_MSGMAP@@XZ 000111a8 f HelloDlg.obj
0001:000001b4 ?GetMessageMap@CWinApp@@MBAPBUAFX_MSGMAP@@XZ 000111b4 f uafxcw:appcore.obj
0001:000001b4 ?GetThisMessageMap@CWinApp@@KAPBUAFX_MSGMAP@@XZ 000111b4 f uafxcw:appcore.obj
0001:000001c0 ?_LoadSysPolicies@CWinApp@@IAAHXZ 000111c0 f uafxcw:appcore.obj
0001:000002e4 ?InitApplication@CWinApp@@UAAHXZ 000112e4 f uafxcw:appcore.obj
0001:00000370 ??1CWinApp@@UAA@XZ 00011370 f uafxcw:appcore.obj
0001:000004bc ?ExitInstance@CWinApp@@UAAHXZ 000114bc f uafxcw:appcore.obj
通过异常地址0001119c,找到最相近的2个地址
OnInitDialog@CHelloDlg@@MAAHXZ 00011160 f HelloDlg.obj
GetMessageMap@CHelloDlg@@MBAPBUAFX_MSGMAP@@XZ 000111a8 f HelloDlg.obj
以上2个地址表示函数的起始地址,所以直接看HelloDlg,异常的地址在HelloDlg .obj的CHelloDlg的OnInitDialog。相应的代码在HelloDlg.cod
2、查找异常的行号
异常地址为0001119c,减去起始地址00011160,得0x3c。
异常地址的上一层函数地址为00011194,减去起始地址00011160,得0x34。
打开HelloDlg.cod,部分内容如下
; 48 :
; 49 : // TODO: 在此添加额外的初始化代码
; 50 : char *p = 0;
00034 e3a02000 mov r2, #0
; 51 : FunOSAD(p);
00038 e3a03001 mov r3, #1
0003c e5c23000 strb r3, [r2]
; 52 :
; 53 : return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
(说明:48是行号,00034是十六进制地址)
根据0x3c,可找到0003c e5c23000 strb r3, [r2],往上看,可以知道在FunOSAD中异常了。第51行。即*pbuf = 1;这一行代码异常。
上一层地址0x34,可找到上一级函数00034 e3a02000 mov r2, #0,往上看可知道上一级地址为char *p = 0;,第50行。