如何使用MAP文件找到程序崩溃的原因
学化妆盘发作者 Wouter Dhondt 翻译 冯亦成(fengyc@pt.suntec)
[译者] 在我们调试程序的时候,习惯于不停的Step in, 可是如果我们发现Debug版的exe可以完全正常运行,而Relea版却经常莫名其妙Crash。那该怎么办??没有关系,这篇文章就是帮你解决这个问题的:) 当然,你如果希望全面提高你的Debug能力,不妨去读一下John Robbins的"Debugging Applications"一书,不过你要读英文版,中文版翻译的太烂了!
导言
编写整洁的程序是一回事。而当用户通知你的程序崩溃了,你知道在增加程序新属性之前最好先修正这些错误,如果你足够幸运的话,用户会给你提供一个崩溃地址,要解决这个问题还是要有很长的路要走。有了崩溃地址,你怎么确定到底是在什么出了错呢?
创建MAP文件
首先,你需要MAP文件。如果你没有MAP文件,那你几乎是不可能通过崩溃地址找到你程序出错的具体代码行。那么先让我教你怎么创建合适的MAP文件。为此我们创建了一个新工程(MAPFILE):我在VC++6.0中创建了应用Win32 Application选项的新工程,并且选择'typical "Hello Word!" application',这样可以使得生成的MAP文件能够满足我下边解说的需要。
当生成新工程后,我们调整relea版的工程设置信息。在C/C++属性页,设置Debug Info的值为"Line Numbers Only"。
很多人都忘了这一步,但是如果你想得到合适的MAP文件,你就需要设置这个选项,这不会对你的relea程序造成任何影响。下一步是Link属性页,你需要选择"Generate mapfile"选项。在Project Options编辑框中输入/MAPINFO:LINES和/MAPINFO:EXPORTS开关。
现在你可以编译和链接你的工程了,链接之后,你可以在你的中间目录中找到.map文件(和exe文件在一起)。罗列
阅读MAP文件
在上面这些无趣的工作之后,接下去就是很有趣的部分:怎么读MAP文件。我们通过一个崩溃实例来介绍怎么读MAP文件。那么我们先得让程序崩溃,于是我在InitInstance()函数的最后增加了下边的两行代码:
char* pEmpty = NULL;
*pEmpty = 'x'; // 第119行
我相信你能够找到其它代码使得你的程序崩溃。现在重新编译且链接工程。如果你运行你的程序,程序将崩溃,并且得到怎样的消息:'The instruction at "0x004011a1" referenced memory at "0x00000000"。0x00000000内存不能写。
现在,可以用Notepad或者类似的编辑工具打开MAP文件。MAP文件如下所示:
在MAP文件的头部包含了模块名称,表示工程链接时刻的时间戳,以及首选加载地址(一般是0x00400000,除非是dll)。文件头之后就是一些ction信息,是由链接程序把各种OBJ和LIB文件的ction信息组织起来的。
MAPFILE
Timestamp is 3df6394d (Tue Dec 10 19:58:21 2002)
Preferred load address is 00400000
Start Length Name Class
0001:00000000 000038feH .text CODE
0002:00000000 000000f4H .idata$5 DATA
0002:000000f8 00000394H .rdata DATA
gallery是什么意思0002:0000048c 00000028H .idata$2 DATA
0002:000004b4 00000014H .idata$3 DATA
0002:000004c8 000000f4H .idata$4 DATA
0002:000005bc 0000040aH .idata$6 DATA
0002:000009c6 00000000H .edata DATA
0003:00000000 00000004H .CRT$XCA DATA
0003:00000004 00000004H .CRT$XCZ DATA
英语四六级考试延期0003:00000008 00000004H .CRT$XIA DATA
0003:0000000c 00000004H .CRT$XIC DATA
0003:00000010 00000004H .CRT$XIZ DATA
0003:00000014 00000004H .CRT$XPA DATA
0003:00000018 00000004H .CRT$XPZ DATA
0003:0000001c 00000004H .CRT$XTA DATA
0003:00000020 00000004H .CRT$XTZ DATA
0003:00000030 00002490H .data DATA
0003:000024c0 000005fcH .bss DATA
酒刺
0004:00000000 00000250H .rsrc$01 DATA
0004:00000250 00000720H .rsrc$02 DATA
在ction信息之后,你看到的时公共函数信息。注意下边的"public"部分,如果你有静态C函数,那它不会在"public"部分出现。幸运的是,在line numbers部分仍会反映静态函数的信息。"public"函数信息的最重要的部分是函数名称和Rva+Ba栏的信息,Rva+Ba信息是函数的起始地址。
Address Publics by Value Rva+Ba Lib:Object
0001:00000000 _WinMain@16 00401000 f MAPFILE.obj
0001:000000c0 ?MyRegisterClass@@YAGPAUHINSTANCE__@@@Z 004010c0 f MAPFILE.obj
0001:00000150 ?InitInstance@@YAHPAUHINSTANCE__@@H@Z 00401150 f MAPFILE.obj
餐厅周
0001:000001b0 ?WndProc@@YGJPAUHWND__@@IIJ@Z 004011b0 f MAPFILE.obj
0001:00000310 ?About@@YGJPAUHWND__@@IIJ@Z 00401310 f MAPFILE.obj
0001:00000350 _WinMainCRTStartup 00401350 f LIBC:wincrt0.obj
0001:00000446 __amsg_exit 00401446 f LIBC:wincrt0.obj
0001:0000048f __cinit 0040148f f LIBC:crt0dat.obj
考研机构费用
0001:000004bc _exit 004014bc f LIBC:crt0dat.obj
0001:000004cd __exit 004014cd f LIBC:crt0dat.obj
yangquan0001:00000591 __XcptFilter 00401591 f LIBC:winxfltr.obj
0001:00000715 __wincmdln 00401715 f LIBC:wincmdln.obj
//SNIPPED FOR BETTER READING
0003:00002ab4 __FPinit 00408ab4 <common>
0003:00002ab8 __acmdln 00408ab8 <common>
entry point at 0001:00000350
Static symbols
0001:000035d0 LeadUp1 004045d0 f LIBC:memmove.obj
0001:000035fc LeadUp2 004045fc f LIBC:memmove.obj
//SNIPPED FOR BETTER READING
santa claus
0001:00000577 __initterm 00401577 f LIBC:crt0dat.obj