仅通过崩溃地址找出源代码的出错行

Delphi之通过报错地址或崩溃地址找出源代码的出错行 一 什么是MAP文件  什么是 MAP 文件?简单地讲, MAP 文件是程序的全局符号、源文件和代码行号信息的唯一的文本表示方法,它可以在任何地方、任何时候使用,不需要有额外的程序进行支持。而且,这是唯一能找出程序崩溃的地方的救星。  如果要查找代码行号,需要使用下面的公式做一些十六进制的减法运算:  崩溃行偏移 = 崩溃地址(Crash Address) - 基地址(ImageBase A 阅读详情

        作为程序员,我们平时最担心见到的事情是什么?是内存泄漏?是界面不好看?……错啦!我相信我的看法是不会有人反对的——那就是,程序发生了崩溃!

“该程序执行了非法操作,即将关闭。请与你的软件供应商联系。”,呵呵,这句 M$ 的“名言”,恐怕就是程序员最担心见到的东西了。有的时候,自己的程序在自己的机器上运行得好好的,但是到了别人的机器上就崩溃了;有时自己在编写和测试的过程中就莫名其妙地遇到了非法操作,但是却无法确定到底是源代码中的哪行引起的……是不是很痛苦呢?不要紧,本文可以帮助你走出这种困境,甚至你从此之后可以自豪地要求用户把崩溃地址告诉你,然后你就可以精确地定位到源代码中出错的那行了。(很神奇吧?呵呵。)

首先我必须强调的是,本方法可以在目前市面上任意一款编译器上面使用。但是我只熟悉 M$ 的 VC 和 MASM ,因此后面的部分只介绍如何在这两个编译器中实现,请读者自行融会贯通,掌握在别的编译器上使用的方法。

Well,废话说完了,让我们开始! :)

首先必须生成程序的 MAP 文件。什么是 MAP 文件?简单地讲, MAP 文件是程序的全局符号、源文件和代码行号信息的唯一的文本表示方法,它可以在任何地方、任何时候使用,不需要有额外的程序进行支持。而且,这是唯一能找出程序崩溃的地方的救星。

好吧,既然 MAP 文件如此神奇,那么我们应该如何生成它呢?在 VC 中,我们可以按下 Alt+F7 ,打开“Project Settings”选项页,选择 CC++ 选项卡,并在最下面的 Project Options 里面输入:Zd ,然后要选择 Link 选项卡,在最下面的 Project Options 里面输入: mapinfolines 和 mapPROJECT_NAME.map 。最后按下 F7 来编译生成 EXE 可执行文件和 MAP 文件。

在 MASM 中,我们要设置编译和连接参数,我通常是这样做的:

rc %1.rc
ml c coff Zd %1.asm
link subsystemwindows mapinfoexports mapinfolines map%1.map %1.obj %1.res

把它保存成 makem.bat ,就可以在命令行输入 makem filename 来编译生成 EXE 可执行文件和 MAP 文件了。

在此我先解释一下加入的参数的含义:

Zd              表示在编译的时候生成行信息
map[filename]  表示生成 MAP 文件的路径和文件名
mapinfolines   表示生成 MAP 文件时,加入行信息
mapinfoexports 表示生成 MAP 文件时,加入 exported functions (如果生成的是 DLL 文件,这个选项就要加上)

OK,通过上面的步骤,我们已经得到了 MAP 文件,那么我们该如何利用它呢?

让我们从简单的实例入手,请打开你的 VC ,新建这样一个文件:

01 
02  程序名称:演示如何通过崩溃地址找出源代码的出错行
03  作者:罗聪
04  日期:2003-2-7
05  出处:httpwww.luocong.com(老罗的缤纷天地)
06  本程序会产生“除0错误”,以至于会弹出“非法操作”对话框。
07  “除0错误”只会在 Debug 版本下产生,本程序为了演示而尽量简化。
08  注意事项:如欲转载,请保持本程序的完整,并注明:
09  转载自“老罗的缤纷天地”(httpwww.luocong.com)
10 
11 
12  void Crash(void)
13  {
14      int i = 1;
15      int j = 0;
16      i = j;
17  }
18 
19  void main(void)
20  {
21      Crash();
22  }

很显然本程序有“除0错误”,在 Debug 方式下编译的话,运行时肯定会产生“非法操作”。好,让我们运行它,果然,“非法操作”对话框出现了,这时我们点击“详细信息”按钮,记录下产生崩溃的地址——在我的机器上是 0x0040104a 。

再看看它的 MAP 文件:(由于文件内容太长,中间没用的部分我进行了省略)

CrashDemo

Timestamp is 3e430a76 (Fri Feb 07 092302 2003)

Preferred load address is 00400000

Start         Length     Name                   Class
000100000000 0000de04H .text                   CODE
00010000de04 0001000cH .textbss                CODE
000200000000 00001346H .rdata                  DATA
000200001346 00000000H .edata                  DATA
000300000000 00000104H .CRT$XCA                DATA
000300000104 00000104H .CRT$XCZ                DATA
000300000208 00000104H .CRT$XIA                DATA
00030000030c 00000109H .CRT$XIC                DATA
000300000418 00000104H .CRT$XIZ                DATA
00030000051c 00000104H .CRT$XPA                DATA
000300000620 00000104H .CRT$XPX                DATA
000300000724 00000104H .CRT$XPZ                DATA
000300000828 00000104H .CRT$XTA                DATA
00030000092c 00000104H .CRT$XTZ                DATA
000300000a30 00000b93H .data                   DATA
0003000015c4 00001974H .bss                    DATA
000400000000 00000014H .idata$2                DATA
000400000014 00000014H .idata$3                DATA
000400000028 00000110H .idata$4                DATA
000400000138 00000110H .idata$5                DATA
000400000248 000004afH .idata$6                DATA

  Address         Publics by Value              Rva+Base     LibObject

000100000020       Crash@@YAXXZ              00401020 f   CrashDemo.obj
000100000070       _main                      00401070 f   CrashDemo.obj
000400000000       __IMPORT_DESCRIPTOR_KERNEL32 00424000     kernel32KERNEL32.dll
000400000014       __NULL_IMPORT_DESCRIPTOR   00424014     kernel32KERNEL32.dll
000400000138       __imp__GetCommandLineA@0   00424138     kernel32KERNEL32.dll
00040000013c       __imp__GetVersion@0        0042413c     kernel32KERNEL32.dll
000400000140       __imp__ExitProcess@4       00424140     kernel32KERNEL32.dll
000400000144       __imp__DebugBreak@0        00424144     kernel32KERNEL32.dll
000400000148       __imp__GetStdHandle@4      00424148     kernel32KERNEL32.dll
00040000014c       __imp__WriteFile@20        0042414c     kernel32KERNEL32.dll
000400000150       __imp__InterlockedDecrement@4 00424150     kernel32KERNEL32.dll
000400000154       __imp__OutputDebugStringA@4 00424154     kernel32KERNEL32.dll
000400000158       __imp__GetProcAddress@8    00424158     kernel32KERNEL32.dll
00040000015c       __imp__LoadLibraryA@4      0042415c     kernel32KERNEL32.dll
000400000160       __imp__InterlockedIncrement@4 00424160     kernel32KERNEL32.dll
000400000164       __imp__GetModuleFileNameA@12 00424164     kernel32KERNEL32.dll
000400000168       __imp__TerminateProcess@8  00424168     kernel32KERNEL32.dll
00040000016c       __imp__GetCurrentProcess@0 0042416c     kernel32KERNEL32.dll
000400000170       __imp__UnhandledExceptionFilter@4 00424170     kernel32KERNEL32.dll
000400000174       __imp__FreeEnvironmentStringsA@4 00424174     kernel32KERNEL32.dll
000400000178       __imp__FreeEnvironmentStringsW@4 00424178     kernel32KERNEL32.dll
00040000017c       __imp__WideCharToMultiByte@32 0042417c     kernel32KERNEL32.dll
000400000180       __imp__GetEnvironmentStrings@0 00424180     kernel32KERNEL32.dll
000400000184       __imp__GetEnvironmentStringsW@0 00424184     kernel32KERNEL32.dll
000400000188       __imp__SetHandleCount@4    00424188     kernel32KERNEL32.dll
00040000018c       __imp__GetFileType@4       0042418c     kernel32KERNEL32.dll
000400000190       __imp__GetStartupInfoA@4   00424190     kernel32KERNEL32.dll
000400000194       __imp__HeapDestroy@4       00424194     kernel32KERNEL32.dll
000400000198       __imp__HeapCreate@12       00424198     kernel32KERNEL32.dll
00040000019c       __imp__HeapFree@12         0042419c     kernel32KERNEL32.dll
0004000001a0       __imp__VirtualFree@12      004241a0     kernel32KERNEL32.dll
0004000001a4       __imp__RtlUnwind@16        004241a4     kernel32KERNEL32.dll
0004000001a8       __imp__GetLastError@0      004241a8     kernel32KERNEL32.dll
0004000001ac       __imp__SetConsoleCtrlHandler@8 004241ac     kernel32KERNEL32.dll
0004000001b0       __imp__IsBadWritePtr@8     004241b0     kernel32KERNEL32.dll
0004000001b4       __imp__IsBadReadPtr@8      004241b4     kernel32KERNEL32.dll
0004000001b8       __imp__HeapValidate@12     004241b8     kernel32KERNEL32.dll
0004000001bc       __imp__GetCPInfo@8         004241bc     kernel32KERNEL32.dll
0004000001c0       __imp__GetACP@0            004241c0     kernel32KERNEL32.dll
0004000001c4       __imp__GetOEMCP@0          004241c4     kernel32KERNEL32.dll
0004000001c8       __imp__HeapAlloc@12        004241c8     kernel32KERNEL32.dll
0004000001cc       __imp__VirtualAlloc@16     004241cc     kernel32KERNEL32.dll
0004000001d0       __imp__HeapReAlloc@16      004241d0     kernel32KERNEL32.dll
0004000001d4       __imp__MultiByteToWideChar@24 004241d4     kernel32KERNEL32.dll
0004000001d8       __imp__LCMapStringA@24     004241d8     kernel32KERNEL32.dll
0004000001dc       __imp__LCMapStringW@24     004241dc     kernel32KERNEL32.dll
0004000001e0       __imp__GetStringTypeA@20   004241e0     kernel32KERNEL32.dll
0004000001e4       __imp__GetStringTypeW@16   004241e4     kernel32KERNEL32.dll
0004000001e8       __imp__SetFilePointer@16   004241e8     kernel32KERNEL32.dll
0004000001ec       __imp__SetStdHandle@8      004241ec     kernel32KERNEL32.dll
0004000001f0       __imp__FlushFileBuffers@4  004241f0     kernel32KERNEL32.dll
0004000001f4       __imp__CloseHandle@4       004241f4     kernel32KERNEL32.dll
0004000001f8       177KERNEL32_NULL_THUNK_DATA 004241f8     kernel32KERNEL32.dll

entry point at        0001000000f0


Line numbers for .DebugCrashDemo.obj(dmsdevmyprojectscrashdemocrashdemo.cpp) segment .text

    13 000100000020    14 000100000038    15 00010000003f    16 000100000046
    17 000100000050    20 000100000070    21 000100000088    22 00010000008d

如果仔细浏览 Rva+Base 这栏,你会发现第一个比崩溃地址 0x0040104a 大的函数地址是 0x00401070 ,所以在 0x00401070 这个地址之前的那个入口就是产生崩溃的函数,也就是这行:

000100000020       Crash@@YAXXZ              00401020 f   CrashDemo.obj

因此,发生崩溃的函数就是 Crash@@YAXXZ ,所有以问号开头的函数名称都是 C++ 修饰的名称。在我们的源程序中,也就是 Crash() 这个子函数。

OK,现在我们轻而易举地便知道了发生崩溃的函数名称,你是不是很兴奋呢?呵呵,先别忙,接下来,更厉害的招数要出场了。

请注意 MAP 文件的最后部分——代码行信息(Line numbers information),它是以这样的形式显示的:

13 000100000020

第一个数字代表在源代码中的代码行号,第二个数是该代码行在所属的代码段中的偏移量。

如果要查找代码行号,需要使用下面的公式做一些十六进制的减法运算:

崩溃行偏移 = 崩溃地址(Crash Address) - 基地址(ImageBase Address) - 0x1000

为什么要这样做呢?细心的朋友可能会留意到 Rva+Base 这栏了,我们得到的崩溃地址都是由 偏移地址(Rva)+ 基地址(Base) 得来的,所以在计算行号的时候要把基地址减去,一般情况下,基地址的值是 0x00400000 。另外,由于一般的 PE 文件的代码段都是从 0x1000 偏移开始的,所以也必须减去 0x1000 。

好了,明白了这点,我们就可以来进行小学减法计算了:

崩溃行偏移 = 0x0040104a - 0x00400000 - 0x1000 = 0x4a

如果浏览 MAP 文件的代码行信息,会看到不超过计算结果,但却最接近的数是 CrashDemo.cpp 文件中的:

16 000100000046

也就是在源代码中的第 16 行,让我们来看看源代码

16      i = j;

哈!!!果然就是第 16 行啊!

兴奋吗?我也一样! :)

方法已经介绍完了,从今以后,我们就可以精确地定位到源代码中的崩溃行,而且只要编译器可以生成 MAP 文件(包括 VC、MASM、VB、BCB、Delphi……),本方法都是适用的。我们时常抱怨 M$ 的产品如何如何差,但其实 M$ 还是有意无意间提供了很多有价值的信息给我们的,只是我们往往不懂得怎么利用而已……相信这样一来,你就可以更为从容地面对“非法操作”提示了。你甚至可以要求用户提供崩溃的地址,然后就可以坐在家中舒舒服服地找到出错的那行,并进行修正。
 

vs2010利用map,cod文件定位崩溃代码行 利用map,cod文件定位崩溃代码行 利用vs2010 新建一个空的控制台项目,添加文件gtg.cpp,内容如下 void crash() {          inti=1;          intj=0;          i/=j; } int main() {          crash();          return0; } 配置生成map及cod文件 阅读详情

相关推荐

MS-VC-使用MAP文件快速定位程序崩溃代码行-VC++

作为程序员,平时最担心见到的事情就是程序发生了崩溃,无论是指针越界还是非法操作,都将给我们的应用系统造成巨大的损失。但在一个大型系统的测试过程中,初期出现程序崩溃似乎成了不可避免的事。其实测试中出现程序崩溃并不可怕,反而是测试的成功。我们更为关心的是程序中的哪一行导致了系统崩溃,这样我们才能有针对性的进行改正。

如何通过崩溃地址找到出错的代码行(ZT)

这是从“VC编程经验总结7”中转出来的借花献佛——如何通过崩溃地址找到出错的代码行作为程序员,我们平时最担心见到的事情是什么?是内存泄漏?是界面不好看?……错啦!我相信我的看法是不会有人反对的--那就是,程序发生了崩溃! “该程序执行了非法操作,即将关闭。请与你的软件供应商联系。”,呵呵,这句 M$ 的“名言”,恐怕就是程序员最担心见到的东西了。有的时候,自己的程序在自己的机器上运行得

发芽的石头 1892

Tesla V100 在 Windows 下的驱动选择与模式切换实战

本文详细解析了Tesla V100专业计算卡在Windows系统下的驱动安装与模式切换实战。针对设备管理器识别但任务管理器“隐身”的常见问题,文章指出其默认TCC模式为纯计算设计,并指导用户正确选择数据中心或GRID驱动。核心在于通过注册表修改或驱动选择,在高效的TCC模式与便于监控的WDDM模式间切换,同时提供了nvidia-smi命令行监控与AI框架测试等稳定使用方案。

weixin_29247219的博客 1806

Google Breakpad: 实战crash

C/C++程序最棘手的时候就是一个字“挂”,总是经常和不经常的挂掉,尤其是那些线上的不经常挂的情况,光看日志定位问题真的很难。 为解决C挂挂的问题,有必要提供一个跨平台的crash处理系统,目前已知的支持平台有windows 、Linux、 OS X 、android、等待。 下面就来看看Google自己用的系统:Breakpad 最好的介绍莫过于谷歌自己的: 谷歌的介绍:http://code.google.com/p/google-breakpad/wiki/GettingStartedWith

jinchengzhou的专栏 1019

MAX30102心率血氧传感器:从原理到实践的可穿戴健康监测方案

本文深入解析MAX30102心率血氧传感器的工作原理及在可穿戴健康监测中的实践应用。通过反射式光电容积脉搏波(PPG)技术,该传感器能精准测量心率和血氧饱和度,具备低功耗、高抗干扰等优势。文章详细介绍了信号处理算法、硬件连接技巧及创新应用场景,为开发者提供全面的解决方案。

weixin_29184371的博客 282

⭐️白嫖的阿里云认证⭐️ 【真题&参考答案】:大模型Clouder认证:利用大模型提升内容生产能力

【摘要】真题「大模型Clouder认证:利用大模型提升内容生产能力」

davidkorhenn的博客 1921

对“仅通过崩溃地址找出源代码的出错行”一文的补充与改进

对“仅通过崩溃地址找出源代码的出错行”一文的补充与改进作者:上海伟功通信 roc  读了老罗的“仅通过崩溃地址找出源代码的出错行”(下称"罗文")一文后,感觉该文还是可以学到不少东西的。不过文中尚存在有些说法不妥,以及有些操作太繁琐的地方 。为此,本人在学习了此文后,在多次实验实践基础上,把该文中的一些内容进行补充与改进,希望对大家调试程序,尤其是release版本的程序有帮助 。欢迎各位朋友批评

simahao的专栏 1976

求助:有没有基于pscad的锂电池仿真模型

求助:有没有基于pscad的锂电池仿真模型 欢迎使用Markdown编辑器 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。 新的改变没有 我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写...

qq_38922523的博客 1222

历史经验之仅通过崩溃地址找出源代码的出错行

整理日期:2005年12月7日 作为程序员,我们平时最担心见到的事情是什么?是内存泄漏?是界面不好看?……错啦!我相信我的看法是不会有人反对的——那就是,程序发生了崩溃! “该程序执行了非法操作,即将关闭。请与你的软件供应商联系。”,呵呵,这句 M$ 的“名言”,恐怕就是程序员最担心见到的东西了。有的时候,自己的程序在自己的机器上运行得好好的,但是到了别人的机器上就崩溃了;有时自己在编写和测试的过程中就莫名其妙地遇到了非法操作,但是却无法确定到底是源代码中的哪行引起的……是不是很痛苦呢?不要紧,本文可以帮助

上善若水的博客 552

扫码器USB键盘报告描述符数据格式解析

Byte0用于判断Shift是否按下,当按下时Byte0的Bit1会置1,即0x02,若未按下,Bit为0,即0x00。0x00 0x00 0x00 0x00 0x00 0x00 0x00--->>未按下Shift。0x00 0x00 0x00 0x00 0x00 0x00 0x00--->>按下Shift。0x00 0x04 0x00 0x00 0x00 0x00 0x00--->>输出A。0x00 0x04 0x00 0x00 0x00 0x00 0x00--->>输出a。

m0_65548585的博客 1141

Ubuntu22.04+PX4+Gazebo:从零到一构建无人机仿真开发环境

本文详细介绍了在Ubuntu22.04系统下搭建PX4+Gazebo无人机仿真开发环境的完整流程。从基础环境配置、PX4固件安装到Gazebo仿真环境搭建,提供了实用命令和常见问题解决方案,帮助开发者快速构建高效的无人机开发环境。

weixin_29243279的博客 435
上一篇: 背水一战,不成功则成仁
下一篇: 不用防火墙,同样防病毒
ftkghost
博客等级 码龄22年 40粉丝 28原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值