通过map文件找程序崩溃的代码行
一,配置vs
二,程序崩溃界面
// ConsoleApplication1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // #include <iostream> void fun(bool flag) { if (flag) { printf("hello world\n"); } else { printf("sorry.\n"); } int a = 0, b = 4; int c = b / a; } int main() { bool m = false; fun(m); //std::cout << "Hello World!\n"; system("pause"); return 0; } // 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单 // 调试程序: F5 或调试 >“开始调试”菜单 // 入门使用技巧: // 1. 使用解决方案资源管理器窗口添加/管理文件 // 2. 使用团队资源管理器窗口连接到源代码管理 // 3. 使用输出窗口查看生成输出和其他消息 // 4. 使用错误列表窗口查看错误 // 5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目 // 6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件
三,map文件
0003:00000030 00000388H .bss DATA 0004:00000000 0000002dH .msvcjmc DATA 0005:00000000 00000060H .rsrc$01 DATA 0005:00000060 00000180H .rsrc$02 DATA Address Publics by Value Rva+Base Lib:Object 0000:00000000 ___hybrid_code_map_count 00000000 <absolute> 0000:00000000 ___AbsoluteZero 00000000 <absolute> 0000:00000000 ___hybrid_code_map 00000000 <absolute> 0000:00000000 ___volatile_metadata 00000000 <absolute> 0000:00000000 ___hybrid_auxiliary_iat 00000000 <absolute> 0000:00000000 ___guard_longjmp_count 00000000 <absolute> 0000:00000000 ___dynamic_value_reloc_table 00000000 <absolute> 0000:00000000 ___guard_iat_count 00000000 <absolute> 0000:00000000 ___enclave_config 00000000 <absolute> 0000:00000000 ___guard_fids_count 00000000 <absolute> 0000:00000000 ___guard_longjmp_table 00000000 <absolute> 0000:00000000 ___guard_iat_table 00000000 <absolute> 0000:00000000 ___guard_fids_table 00000000 <absolute> 0000:00000002 ___safe_se_handler_count 00000002 <absolute> 0000:00000100 ___guard_flags 00000100 <absolute> 0000:00000000 ___ImageBase 00400000 <linker-defined> 0001:00000000 ?__empty_global_delete@@YAXPAX@Z 00401000 f i ConsoleApplication1.obj 0001:00000040 ?__empty_global_delete@@YAXPAXI@Z 00401040 f i ConsoleApplication1.obj 0001:00000080 ?fun@@YAX_N@Z 00401080 f ConsoleApplication1.obj 0001:00000100 __JustMyCode_Default 00401100 f i ConsoleApplication1.obj 0001:00000110 ___local_stdio_printf_options 00401110 f i ConsoleApplication1.obj 0001:00000160 __vfprintf_l 00401160 f i ConsoleApplication1.obj 0001:000001d0 _main 004011d0 f ConsoleApplication1.obj 0001:00000240 _printf 00401240 f i ConsoleApplication1.obj 0001:000002c0 @__CheckForDebuggerJustMyCode@4 004012c0 f MSVCRTD:debugger_jmc.obj 0001:00000300 @_RTC_AllocaHelper@12 00401300 f MSVCRTD:stack.obj 0001:00000340 @_RTC_CheckStackVars2@12 00401340 f MSVCRTD:stack.obj 0001:00000420 @_RTC_CheckStackVars@8 00401420 f MSVCRTD:stack.obj 0001:00000490 __RTC_CheckEsp 00401490 f MSVCRTD:stack.obj
如果仔细浏览 Rva+Base 这栏,你会发现第一个比崩溃地址大的地址0x00401080,所以在 这个地址之前的那个入口就是产生崩溃的函数
因此,发生崩溃的函数就是 ?Crash@@YAXXZ ,所有以问号开头的函数名称都是 C++ 修饰的名称
什么是 MAP 文件?简单地讲, MAP 文件是程序的全局符号、源文件和代码行号信息的唯一的文本表示方法,它可以在任何地方、任何时候使用,不需要有额外的程序进行支持。而且,这是唯一能找出程序崩溃的地方的救星。
好吧,既然 MAP 文件如此神奇,那么我们应该如何生成它呢?在 VC 中,我们可以按下 Alt+F7 ,打开“Project Settings”选项页,选择 C/C++ 选项卡,并在最下面的 Project Options 里面输入:/Zd ,然后要选择 Link 选项卡,在最下面的 Project Options 里面输入: /mapinfo:lines 和 /map:PROJECT_NAME.map 。最后按下 F7 来编译生成 EXE 可执行文件和 MAP 文件。
在 MASM 中,我们要设置编译和连接参数,我通常是这样做的:
rc %1.rc
ml /c /coff /Zd %1.asm
link /subsystem:windows /mapinfo:exports /mapinfo:lines /map:%1.map %1.obj %1.res
把它保存成 makem.bat ,就可以在命令行输入 makem filename 来编译生成 EXE 可执行文件和 MAP 文件了。
在此我先解释一下加入的参数的含义:
/Zd 表示在编译的时候生成行信息
/map[:filename] 表示生成 MAP 文件的路径和文件名
/mapinfo:lines 表示生成 MAP 文件时,加入行信息
/mapinfo:exports 表示生成 MAP 文件时,加入 exported functions (如果生成的是 DLL 文件,这个选项就要加上)
参考:
https://blog.csdn.net/anddy926/article/details/7695476/
https://blogs.msdn.microsoft.com/hopperx/2006/09/14/using-map-files-part-1/
https://blog.csdn.net/u012138730/article/details/90611728