什么时候用MAP文件调试
1.调试程序时,我们发现Debug版的exe可以完全正常运行,而Release版却经常莫名其妙Crash。
2.程序运行时直接崩溃,只显示一个出错地址,让联系开发人员。这样根本找不到程序Crash的位置。
3.但在大量进行压力测试时,尤其是多线程测试时,有可能同时出现十几个错误,这时VC本身的调试跳转可能跟不到核心出错的现场。
什么是MAP文件
MAP文件是程序的全局符号、源文件和代码行号信息的问一问本表示方法,是整个程序工程信息的静态文本。它可以直接打开,不需要其他支持。
在程序编译时加入MAP文件
vc6编译生成的MAP文件是带行号的,找到出错的内存地址后,直接通过行号就可以对应到代码,但是vs2005编译生成的MAP文件不带行号...那么就需要通过cod文件来定位行号。cod文件(机器码文件).
在vs2005中使编译时生成MAP文件和cod文件的方法:
(1).map文件:property->Configuration Properties->Linker->Debugging 中的Generate Map File选择Yes(/MAP);
(2).cod文件:property->Configuration Properties->C/C++->output Files中Assembler OutPut中选择Assembly,Maching Code and Source(/FAcs),生成机器,源代码
OK...
如何使用MAP和cod
先举个例子:
void Crash(void)
{
int i = 1;
int j = 0;
i /= j;
}
void Func1()
{
int b = 1000;
Crash();
}
void Func2()
{
int c = 1;
return ;
}
void main(void)
{
int a = 0;
a = 300;
Func1();
Func2();
}
运行后出现下图:0x004113d0的位置crash
打开MAP文件(后缀名.map,默认和.exe在同一级目录)。
从Address这里开始看,通过Rva+Base这里查找Crash的地址0x004113d0.Rva+Base是递增的,很好定位,当然一般也不可能直接找到Crash的地址,而是找比他大的那个地址,或者比他小的那个地址。
找到后发现0x004113d0就在上面这两个地址中间,根据函数调用规则(栈的方式):找到Crash地址大的那个地址,则该地址前一个的入口就是产生崩溃的函数,即?Crash@@YAXXZ,也就是说是函数Crash出现了问题,记录下Crash函数的入口地址0x004113a0.
下面的问题是找到Crash中那一步出问题了,这就需要用到cod文件(.cod)
在cod文件中会有很多类似上图的结构,
以?Crash@@YAXXZ PROC; Crash, COMDAT开头,
以?Crash@@YAXXZ ENDP; Crash
来标识一个函数。中间的19、20、21、22、23就是源代码的行号。
如果要查找代码行号,需要使用下面的公式做一些十六进制的减法运算:
崩溃行偏移 = 崩溃地址(Crash Address) - 基地址(ImageBase Address) - 0x1000
崩溃地址都是由 偏移地址(Rva)+ 基地址(Base) 得来的,所以在计算行号的时候要把基地址减去,一般情况下,基地址的值是 0x00400000 ,另外,由于一般的 PE 文件的代码段都是从 0x1000 偏移开始的,所以也必须减去 0x1000 。
而这里及地址就是Crash函数的入口地址0x004113a0,所以就减去函数入口地址就可以了
那么就是0x004113d0- 0x004113a0=0x00030
在cod文件的Crash函数中找到00030,则它对应的行号就是出错地方在源代码中的行号。就是22行。