VC6.0 通过崩溃地址中找到异常代码行
来源:http://blog.csdn.net/mydeardingxiaoli/article/details/20371585
这是从“VC编程经验总结7”中转出来的借花献佛——如何通过崩溃地址找到出错的代码行作为程序员,我们平时最担心见到的事情是什么?是内存泄漏?是界面不好看?……错啦!我相信我的看法是不会有人反对的--那就是,程序发生了崩溃! “该程序执行了非法操作,即将关闭。请与你的软件供应商联系。”,呵呵,这句 M$ 的“名言”,恐怕就是程序员最担心见到的东西了。有的时候,自己的程序在自己的机器上运行得好好的,但是到了别人的机器上就崩溃了;有时自己在编写和测试的过程中就莫名其妙地遇到了非法操作,但是却无法确定到底是源代码中的哪行引起的……是不是很痛苦呢?不要紧,本文可以帮助你走出这种困境,甚至你从此之后可以自豪地要求用户把崩溃地址告诉你,然后你就可以精确地定位到源代码中出错的那行了。(很神奇吧?呵呵。首先我必须强调的是,本方法可以在目前市面上任意一款编译器上面使用。但是我只熟悉 M$ 的 VC 和 MASM ,因此后面的部分只介绍如何在这两个编译器中实现,请读者自行融会贯通,掌握在别的编译器上使用的方法。Well,废话说完了,让我们开始!:)
首先必须生成程序的 MAP 文件。什么是 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 文件,这个选项就要加上)
OK,通过上面的步骤,我们已经得到了 MAP 文件,那么我们该如何利用它呢?让我们从简单的实例入手,请打开你的 VC ,新建这样一个文件: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 文件:(由于文件内容太长,中间没用的部分我进行了省略)CrashDemoTimestamp is 3e430a76 (Fri Feb 07 09:23:02 2003)
Preferred load address is 00400000
Start Length Name Class
0001:00000000 0000de04H .text CODE
0001:0000de04 0001000cH .textbss CODE
0002:00000000 00001346H .rdata DATA
0002:00001346 00000000H .edata DATA
0003:00000000 00000104H .CRT$XCA DATA
0003:00000104 00000104H .CRT$XCZ DATA
0003:00000208 00000104H .CRT$XIA DATA
0003:0000030c 00000109H .CRT$XIC DATA
0003:00000418 00000104H .CRT$XIZ DATA
0003:0000051c 00000104H .CRT$XPA DATA
0003:00000620 00000104H .CRT$XPX DATA
0003:00000724 00000104H .CRT$XPZ DATA
0003:00000828 00000104H .CRT$XTA DATA
0003:0000092c 00000104H .CRT$XTZ DATA
0003:00000a30 00000b93H .data DATA
0003:000015c4 00001974H .bss DATA
0004:00000000 00000014H .idata$2 DATA
0004:00000014 00000014H .idata$3 DATA
0004:00000028 00000110H .idata$4 DATA
0004:00000138 00000110H .idata$5 DATA
0004:00000248 000004afH .idata$6 DATA
Address Publics by Value Rva+Base Lib:Object
0001:00000020 ?Crash@@YAXXZ 00401020 f CrashDemo.obj
0004:000001f0 __imp__FlushFileBuffers@4 004241f0 kernel32:KERNEL32.dll
0004:000001f4 __imp__CloseHandle@4 004241f4 kernel32:KERNEL32.dll
0004:000001f8 /177KERNEL32_NULL_THUNK_DATA 004241f8 kernel32:KERNEL32.dllentry point at 0001:000000f0
Line numbers for ./Debug/CrashDemo.obj(d:/msdev/myprojects/crashdemo/crashdemo.cpp) segment .text13 0001:00000020 14 0001:00000038 15 0001:0000003f 16 0001:00000046
17 0001:00000050 20 0001:00000070 21 0001:00000088 22 0001:0000008d 如果仔细浏览 Rva+Base 这栏,你会发现第一个比崩溃地址 0x0040104a 大的函数地址是 0x00401070 ,所以在 0x00401070 这个地址之前的那个入口就是产生崩溃的函数,也就是这行:
0001:00000020 ?Crash@@YAXXZ 00401020 f CrashDemo.obj
因此,发生崩溃的函数就是 ?Crash@@YAXXZ ,所有以问号开头的函数名称都是 C++ 修饰的名称。在我们的源程序中,也就是 Crash() 这个子函数。
OK,现在我们轻而易举地便知道了发生崩溃的函数名称,你是不是很兴奋呢?呵呵,先别忙,接下来,更厉害的招数要出场了。
请注意 MAP 文件的最后部分--代码行信息(Line numbers information),它是以这样的形式显示的:
13 0001:00000020
第一个数字代表在源代码中的代码行号,第二个数是该代码行在所属的代码段中的偏移量。
如果要查找代码行号,需要使用下面的公式做一些十六进制的减法运算:
崩溃行偏移 = 崩溃地址(Crash Address) - 基地址(ImageBase Address) - 0x1000
为什么要这样做呢?细心的朋友可能会留意到 Rva+Base 这栏了,我们得到的崩溃地址都是由 偏移地址(Rva)+基地址(Base) 得来的,所以在计算行号的时候要把基地址减去,一般情况下,基地址的值是 0x00400000 。另外,由于一般的 PE 文件的代码段都是从 0x1000 偏移开始的,所以也必须减去 0x1000 。
好了,明白了这点,我们就可以来进行小学减法计算了:
崩溃行偏移 = 0x0040104a - 0x00400000 - 0x1000 = 0x4a
如果浏览 MAP 文件的代码行信息,会看到不超过计算结果,但却最接近的数是 CrashDemo.cpp 文件中的:
16 0001:00000046 也就是在源代码中的第 16 行,让我们来看看源代码:16 i /= j;
哈!!!果然就是第 16 行啊!兴奋吗?我也一样! :)方法已经介绍完了,从今以后,我们就可以精确地定位到源代码中的崩溃行,而且只要编译器可以生成 MAP 文件(包括 VC、MASM、VB、BCB、Delphi……),本方法都是适用的。我们时常抱怨 M$ 的产品如何如何差,但其实 M$ 还是有意无意间提供了很多有价值的信息给我们的,只是我们往往不懂得怎么利用而已……相信这样一来,你就可以更为从容地面对“非法操作”提示了。你甚至可以要求用户提供崩溃的地址,然后就可以坐在家中舒舒服服地找到出错的那行,并进行修正。
VC6.0 通过崩溃地址中找到异常代码行的更多相关文章
- [VC6] 小谈如何解决VC6.0 open崩溃的问题(已解决)(转)
[昨天重装了系统,开始用VC6还是可以的,后来装了WPS,在用VC6里面的Open就崩溃了. 这个解决方法就这么几步:1.把FileTool.dll放到指定的AddIns目录.2. 注册这个dll到注 ...
- vc6.0运用mysql数据库中的编码所导致的乱码问题(接收和输出的编码必须要一致)
[编译中遇见的问题] ①在用vc 6.0去调用MySQL中的数据时,出现中文乱码 ②不明白mysql中的码制 [开始解决问题] ①打开mysql控制台 ...
- Python计算一个项目中含有的代码行数
最近想要知道以前做过的project有多少行代码,因为文件太多,直接手工数效率太低,于是编写一个python程序用来计算一个project有多少代码行. 首先,在一个项目中,有很多子文件夹,子文件夹中 ...
- 在.NET中怎样取得代码行数
文章目的 介绍在.NET中取得代码行数的方法 代码 [STAThread] static void Main(string[] args) { ReportError("Yay!" ...
- java简单统计.java文件中的有效代码行,空行,注释行
package regxdemo; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundExc ...
- 怎么使用jstack精确找到异常代码
1.代码demo //一个CPU密集型线程的demo: package chapter1; public class FindJavaThreadInTaskManager { public stat ...
- 利用jstack 找到异常代码
1.top找出耗时pid进程或ps -ef |grep xxx 找出pid 2.ps p 3036 -L -o pcpu,pid,tid,time,tname,cmd 3036为pid 3.prin ...
- 使用jstack精确找到异常代码的
https://blog.csdn.net/mr__fang/article/details/68496248
- Eclipse中如何显示代码行
方法一 快捷键方式: 按住 Ctrl + F10 选择 show Line Numbers 方法二 手动操作: Window -- Prefences -- General -- Editors - ...
随机推荐
- WP8开发札记(一)WP8应用生命周期管理
在介绍生命周期前,我们先了解两个相关的概念. 1.墓碑机制:WP8与Android采用的真后台机制不同,WP8采用的是墓碑机制.一旦从当前应用程序离开(非退出),该应用会被墓碑化,这样可以更好的管理( ...
- img onerror事件
怪自己知道的太少,img标签有onerror这个事件,我是才刚知道,恕我愚昧,既然是第一次遇到,而且又是一个自己从没有涉及过得的东西,所以我希望通过这个随笔来是自己印象深刻,此文仅仅只是让自己印象深刻 ...
- openstack中的floating ip与阿里云的公网ip
项目组因业务需求使用openstack搭建了一个私有云,本想在vm上搭建一个ftp.源是vsftpd.所有配置都完成了,在远程登录的时候却出现了 这个问题. 初一看以为是文件夹权限的问题,可上上下下全 ...
- XAF-列表视图编辑模式
下面来看看XAF中列表有哪些编辑模式: 一.inline编辑 下图说明了WinForms和ASP.NET应用程序中的可编辑列表视图. 在win中,这个很友好,就像excel中编辑一样.5星功能^_^. ...
- 二度云抢先成为首批中国工信部(.vip/.xyz/.club)域名注册管理机构
今天,工信部官网的公示文件显示,新通用顶级域名.vip..xyz以及.club域名注册局已正式获得工信部审批,成为中国境内合法的顶级域名注册管理机构,这标志着.vip..xyz以及.club域名成为首 ...
- android浏览器 源码共享
浏览器源码共享 [天天浏览器]拥有极为精简的内核,手机App大小在2MB以内,是市场上极省用户手机内存,极少占用手机资源,速度极快的浏览器,本身功能完整强大,是极速上网必备的利器. 源代码下载地址:h ...
- Andrew Ng机器学习入门——线性回归
本人从2017年起,开始涉猎机器学习.作为入门,首先学习的是斯坦福大学Andrew Ng(吴恩达)教授的Coursera课程 2 单变量线性回归 线性回归属于监督学习(Supervise Learni ...
- Kattis -Bus Numbers
Bus Numbers Your favourite public transport company LS (we cannot use their real name here, so we pe ...
- tomcat6.0的安装
这里我比较推荐用安装版,因为解压版需要配置很多环境变量,没那么方便. 双击安装包 选择安装目录,next 这里选择之前安装好的jdk的jre目录 安装完后,检查是否安装完成 打开安装程序 进入bin目 ...
- Python学习笔记进阶篇——总览
Python学习笔记——进阶篇[第八周]———进程.线程.协程篇(Socket编程进阶&多线程.多进程) Python学习笔记——进阶篇[第八周]———进程.线程.协程篇(异常处理) Pyth ...