【C++软件调试技术】什么是pdb文件?如何使用pdb文件?哪些工具需要使用pdb文件?

目录

1、什么是pdb文件?

2、如何配置生成pdb文件?

3、pdb文件的时间戳和文件名称

3.1、pdb文件的时间戳

3.2、pdb文件的文件名称

4、有pdb文件才能在Visual Studio中调试代码

5、在Windbg中使用pdb文件

5.1、使用lm命令查看二进制文件的时间戳,去查找对应版本的pdb文件

5.2、在Windbg中配置pdb文件路径

5.3、如何确定pdb是否加载成功?如果加载失败,可以尝试去强制加载

5.4、pdb文件加载失败的可能原因有哪些?

5.5、有时需要使用到系统库的pdb文件

5.6、关于微软系统库在线pdb下载服务器的说明

5.7、有时需要在Windbg中查看相关变量的值

6、使用Process Explorer、Process Monitor等工具查看函数调用堆栈时需要用到pdb文件

6.1、使用Process Explorer中查看函数调用堆栈时需要使用pdb文件

6.2、使用Process Monitor中查看函数调用堆栈时需要使用pdb文件

7、在反汇编工具IDA中查看汇编代码也需要用到pdb文件

7.1、使用反汇编工具IDA查看汇编代码上下文

7.2、编译器优化代码对我们查看汇编上下文的影响

7.3、查看汇编上下文去辅助定位软件异常问题的实例

7.4、排查软件异常需要掌握哪些基础汇编知识?

7.5、通过查看C++代码对应的汇编代码去学习汇编,将C++源码与汇编代码对照着学

7.6、学习汇编有哪些好处?

8、最后


C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931C/C++基础与进阶(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_11931267.htmlVC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/124272585C++软件分析工具从入门到精通案例集锦(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/131405795开源组件及数据库技术(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_12458859.html网络编程与网络问题分享(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_2276111.html       我们在排查C++软件异常问题时,需要借助多个软件工具去分析(比如SPY++、Dependency Walker、GDIView、Process Explorer、Process Monitor、API Monitor、Windbg和IDA等),很多时候都需要去查看异常发生时的函数调用堆栈及变量信息。要查看到详细的函数调用的堆栈(看到具体的函数)及变量信息,需要使用到pdb符号文件。本文结合多年来排查C++软件异常的实践,详细介绍什么是pdb文件,哪些工具需要使用到pdb文件,以及如何去使用pdb文件,以供大家借鉴或参考。

1、什么是pdb文件?

       pdb(Program Database)文件是Windows平台用于存储程序调试信息的文件格式,pdb文件中包含了编译后的可执行文件或动态链接库的符号表、源代码文件路径、局部变量和全局变量的信息等。pdb文件通常与对应的可执行文件(如.exe或.dll)一起使用,以支持程序的调试和分析。

       pdb文件主要包含如下的信息:

1)符号表信息:pdb文件包含了编译后的程序的符号表信息,包括函数名、类名、结构体名、变量名等。这些符号信息可以帮助调试器在调试过程中准确地定位和查看变量的值、函数的调用关系等。
2)源代码文件路径:pdb文件记录了源代码文件的路径信息,这使得调试器能够在需要时自动加载对应的源代码文件,以便开发人员在调试过程中查看和修改源代码。
3)局部变量和全局变量信息:pdb文件还包含了局部变量和全局变量的信息,包括变量名、类型和内存地址等。这些信息对于调试器来说很重要,它们使得开发人员可以在调试过程中查看和修改变量的值,有助于分析程序中的问题。
4)回溯栈信息:pdb文件中还包含了堆栈帧和函数调用关系的信息,这对于调试器来说非常重要。通过pdb文件,调试器可以在堆栈回溯时准确地还原函数调用的顺序,帮助开发人员理解程序的执行流程。

       pdb文件是Windows平台开发工具链(比如Visual Studio、QT Creator等)生成的包含函数变量符号及调试信息的符号文件。在进行程序的调试和分析时,通常需要将pdb文件与对应的可执行文件一起使用。调试器(比如Visual Studio、Windbg等)会根据pdb文件中存放的函数符号及变量符号等信息,来展示变量值、源代码行号、函数调用堆栈等相关信息,方便调试分析。

有了pdb,函数调用堆栈中才能看到具体的函数名及代码的行号,才能看到变量的信息,查看到变量的值。无论是在Visual Studio中调试,还是在Windbg中调试,都亦如此!

2、如何配置生成pdb文件?

       以Visual Studio为例,在Visual Studio中,不管是Debug还是Release下,默认都会生成pdb文件。在链接器->调试->生成程序数据库配置项中,如下所示:

QT Creator中也支持配置生成pdb文件。 

       本地程序之所以能调试,是因为其二进制文件在本地编译生成时会自动写入对应的pdb文件的绝对路径:

pdb文件中存放着用于调试的各种调试信息。启动调试时调试器会根据二进制文件中记录的pdb文件路径去尝试加载pdb文件,加载成功后,就能获取pdb文件中的调试信息,这样就可以调试了。

在Linux系统中,符号信息是内置到二进制文件中的,不是单独出来的符号文件。

3、pdb文件的时间戳和文件名称

       在使用pdb文件时,有两点需要注意一下,一个是pdb文件的时间戳,一个是pdb文件的名称。

3.1、pdb文件的时间戳

       加载pdb文件时会严格校验pdb文件的时间戳(就是生成pdb文件的时间),必须要和对应的二进制文件生成时间完全一致。即使是同样的代码,即没有修改任何代码,在不同时间点编译生成的pdb文件,也是不能和二进制文件交叉使用的。

3.2、pdb文件的文件名称

       在默认情况下,Visual Studio车工程pdb文件是和工程名称一样的:

也可以在工程配置中指定名称。可以在工程配置中修改pdb文件的名称,但一旦pdb文件生成之后,是不能修改名称的!如果修改了,调试器是无法加载的。

       这点是在我们排查某个项目的崩溃问题时发现的。比如某个工程名称为videocodec_hp.vcxproj,生成的pdb文件的名称默认应为videocodec_hp.pdb(不区分大小写),但在编译脚本中执行文件拷贝时将pdb文件的名称改成videocodec.pdb(去掉了_hp的后缀),这种情况下Windbg是无法加载的。必须手动将pdb文件名改成生成时的名称,才能正常加载使用。

所以还是要多动手实践,在实践的过程中,才能发现更多的细节和问题!

4、有pdb文件才能在Visual Studio中调试代码

       二进制文件中会默认写入pdb文件的完整路径,比如:

调试时会通过完整路径去寻找pdb文件,加载文件中的变量函数等符号信息及其他调试信息,去调试代码。并在调试器中显示完整的函数调用堆栈和变量值信息。

       在Visual Studio中可以进行Debug调试、Release调试和附加到进程调试。对于Debug调试和Release调试,编译生成的二进制文件和pdb文件在同一个路径下,调试是没问题的。

       对于附加到进程调试,一般用于程序底层模块的调试。负责维护底层模块代码的同事,事先将程序安装到其开发机器上,或者直接拷贝包含exe主程序的程序包到其机器上。然后在其机器上用Visual Studio打开要调试的工程,然后将工程代码编译一下,将生成的dll文件拷贝到exe主程序的路径中,然后将exe主程序运行起来,此时exe主程序使用的就是新编译生成的dll文件。此时可以将打开dll文件工程的Visual Studio附加到exe主程序进程上,用Visual Studio调试dll工程的源码。具体附加方法是,点击菜单栏中的调试->附加到进程,在弹出的窗口中找到exe进程:

点击附加按钮即可,就可以打断点调试了。

       关于Visual Studio常用调试方式说明,可以查看我的文章:

Visual Studio调试方式详解icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125587329      关于Visual Studio的常用调试技巧与方法,可以查看我的文章:

Visual Studio调试技巧与实用方法总结(实战经验分享)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/124884225       曾经有人问我,将dll库拷贝到exe主程序的路径中,为啥附加调试时还可以调试该dll库的源码呢?dll文件和对应的pdb文件已经不在一个地方了(同一个路径中)。其实之所以能调试,是因为编译时生成的调试信息都保存到pdb文件中了,虽然调试的dll二进制文件与其对应的pdb文件不在一个路径中了,但pdb文件的完整路径已经被写到dll二进制文件中,Visual Studio在开始调试时,会到dll二进制文件中将pdb文件的完整路径读出来,然后通过这个路径找到pdb文件,并将pdb文件加载到Visual Studio中,所以就可以调试了。因为dll文件与其对应的pdb文件在同一个电脑上,通过dll文件中写入的pdb文件完整路径可以找到pdb文件。如果将dll文件拿到另一个电脑上,这样从dll中读取的pdb路径去找pdb文件肯定是找不到的,因为另一个电脑上对应的路径中根本没有这个pdb文件。


 在这里,给大家重点推荐一下我的几个热门畅销专栏,欢迎订阅:(博客主页还有其他专栏,可以去查看)

专栏1:(该精品技术专栏的订阅量已达到430多个,专栏中包含大量项目实战分析案例,有很强的实战参考价值,广受好评!专栏文章持续更新中,预计更新到200篇以上!欢迎订阅!)

C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931

本专栏根据多年C++软件异常排查的项目实践,系统地总结了引发C++软件异常的常见原因以及排查C++软件异常的常用思路与方法,详细讲述了C++软件的调试方法与手段,以图文并茂的方式给出具体的项目问题实战分析实例(很有实战参考价值),带领大家逐步掌握C++软件调试与异常排查的相关技术,适合基础进阶和想做技术提升的相关C++开发人员!

考察一个开发人员的水平,一是看其编码及设计能力,二是要看其软件调试能力!所以软件调试能力(排查软件异常的能力)很重要,必须重视起来!能解决一般人解决不了的问题,既能提升个人能力及价值,也能体现对团队及公司的贡献!

专栏中的文章都是通过项目实战总结出来的,包含大量项目问题实战分析案例,有很强的实战参考价值!专栏文章还在持续更新中,预计文章篇数能更新到200篇以上!

专栏2: 

C/C++基础与进阶(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_11931267.html

以多年的开发实战经验为基础,总结并讲解一些的C/C++基础与进阶内容,以图文并茂的方式对C++相关知识点进行详细地展开与剖析!专栏涉及了C/C++开发领域多个方面的内容,同时给出C/C++及网络方面的常见笔试面试题,并详细讲述Visual Studio常用调试手段与技巧!

专栏3: 

VC++常用功能开发汇总icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/124272585

专栏将10多年C++开发实践中常用的功能,以高质量的代码展现出来,并对相关功能的实现细节进行了详细的说明。这些常用的代码,其质量与稳定性是有保证的,可以直接拿过去使用,可以有效地解决C++软件开发过程中遇到的问题。


5、在Windbg中使用pdb文件

       我们在使用Windbg分析软件问题时,无论是静态分析dump文件,还是动态调试目标进程,都需要查看异常发生时的函数调用堆栈。要在函数调用堆栈中看到具体的函数名和代码的行号,必须要加载堆栈中相关模块的pdb文件。因为函数名称及代码行号等符号信息都保存在pdb文件中的。

5.1、使用lm命令查看二进制文件的时间戳,去查找对应版本的pdb文件

       根据函数调用堆栈中显示的那些模块,确定需要去找哪些模块的pdb文件。一般不需要找堆栈中所有模块的pdb文件,一般只要找离崩溃点较近的一个或多个模块。可以使用lm命令查看模块的时间戳:

然后通过这个时间戳去找对应时间点的pdb文件。要事先将所有版本的pdb文件统一保存维护起来,当某个版本出问题时,根据时间戳查找对应时间点的pdb文件即可。

5.2、在Windbg中配置pdb文件路径

       点击Windbg菜单栏中的File -> Symbol File Path ...,打开设置pdb文件路径的窗口,如下所示:

一般我们设置如下格式的pdb组合文件路径:

C:\Users\Administrator\Desktop\pdbdir;srv*f:\mss0616*http://msdl.microsoft.com/download/symbols

       这么个一长串组合路径主要由下面两个路径构成:(路径之间使用分号隔开)

1)应用程序库pdb文件路径(非系统库)
C:\Users\Administrator\Desktop\pdbdir。我们开发的模块的pdb文件集中拷贝到该路径中,路径名称可以随意选择。
2)Windows系统库在线下载路径
srv*f:\mss0616*http://msdl.microsoft.com/download/symbols,其中http://msdl.microsoft.com/download/symbols,是微软提供的在线系统pdb文件下载服务器,如果设置了该地址,Windbg会自动连接该服务器,去自动下载与当前dump文件中用到的系统库版本一致的pdb文件。另外,f:\mss0616路径是从微软pdb文件服务器上下载下来的pdb文件的存放路径。

       一般情况下,我们不用一上来就设置Windows系统库的pdb路径(微软在线系统库pdb文件下载地址),我们只需要设置软件业务库(非系统库)的pdb文件路径即可!至于什么时候需要用到系统库的pdb文件,我们下面会讲到。

       关于Windows系统库的pdb文件及路径说明,可以查看微软官网的说明:

Symbol path for Windows debuggersicon-default.png?t=N7T8https://learn.microsoft.com/zh-cn/windows-hardware/drivers/debugger/symbol-path

5.3、如何确定pdb是否加载成功?如果加载失败,可以尝试去强制加载

       有时即使在Windbg中已经设置了正确的pdb路径,勾选了reload选项,查看函数调用堆栈,可能还是看不到部分模块中的具体函数名及代码的行号,比如:

只能看到模块名TestDlg,这是exe主程序,但看不到具体的函数,应该是Windbg没加载到TestDlg模块对应的pdb文件导致的。

       可以使用 lm vm TestDlg* 命令查看TestDlg模块的详细信息,如果看不到pdb路径:

说明Windbg没加载到当前模块的pdb文件。如果加载到pdb文件,则会显示pdb文件的完整路径:

        这里需要额外注意一下,即使在Windbg中设置了pdb所在的路径,并且勾选了reload选项,如下:

可能Windbg也不会自动将pdb加载起来。此时,可以尝试用.reload命令强制加载一下,命令如下:

.reload  /f  TestDlg.exe

说不定可以加载起来,这点我们在项目中遇到过很多次了!

注意,使用.reload命令去强制加载,不代表可以去强制加载不对应的pdb文件。如果pdb文件的时间戳和二进制文件不一致,即不是一个时间点生成的,即便强制加载,也加载不了的。

5.4、pdb文件加载失败的可能原因有哪些?

       pdb文件加载失败可能是以下某个原因导致的:

1)没有设置正确的pdb文件路径

没有将pdb文件路径设置到Windbg中,或者设置了错误的pdb文件路径。

2)pdb文件的时间戳与二进制文件不一致

pdb文件的时间戳和对应的二进制文件(dll或exe文件)不一致,即pdb文件和二进制文件不是同一个时间点生成的。加载pdb文件时,会严格校验pdb文件的时间戳,必须要和对应的二进制文件完全一致。即便代码没修改过,不同时间点编译的pdb文件,也是不能交叉使用的。

3)pdb文件生成后名称不能修改

       在Visual Studio的工程属性中,默认配置了生成pdb文件的,pdb文件的名称默认就是工程名称。在QT Creator中编译的程序,也可以配置生成pdb文件。这些pdb生成时,会自动将pdb文件的名称写到对应的二进制文件中:

后面Windbg去根据写入的pdb名称去加载pdb文件的。一旦pdb文件生成了,其名称是不能人为修改的,人为修改后会导致pdb文件无法加载的。

        对于pdb文件生成后被修改名称导致Windbg加载失败的实战案例,我前几天正好遇到过一次,可以查看我的文章:

pdb文件名称被修改导致pdb文件加载失败的实战排查案例分享icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/139393894

5.5、有时需要使用到系统库的pdb文件

       有时从函数调用堆栈看,程序崩溃在系统库中,但基本都不是系统库有问题,问题最终还是出在上层的业务模块中,比如上层业务模块在调用系统库的接口(比如memcpy等)时,传入了不合法的参数,或者传入了错误的内存地址或不可访问的内存地址,导致系统库的接口内部发生异常。

       我们在设置业务模块的pdb文件后无法最终确定问题时,可能会需要加载系统库的pdb文件。加载系统库pdb文件后,我们可以看到调用那些系统库的接口,这样可能便于问题的分析。此外,加载系统库pdb文件之后,可能能看到更多行的函数调用堆栈,我们在实际的项目问题中遇到多次了。

5.6、关于微软系统库在线pdb下载服务器的说明

       Windbg会根据函数调用堆栈中涉及的系统库的模块,去自动下载这些系统库的pdb文件。软件可能在不同版本的Windows系统中崩溃,比如可能是Win7、Win8、Win10、Win11等,即便是系统名称是一样的,可能对应着不同的子版本。不同版本的系统,对应的系统dll库的版本也是不同的。微软系统库在线pdb下载服务器中存放了所有版本的系统库的pdb文件,Windbg会根据要下载的系统dll库的特征值(比如md5)到服务器上去下载对应版本的pdb文件,然后自动将pdb加载到Windbg中。

5.7、有时需要在Windbg中查看相关变量的值

       有时不仅要看函数调用堆栈中具体的函数名和代码的行号,还要看函数中相关变量在内存中的值,在有些场景下,相关变量的值可能是分析问题的关键线索

       我之前写过两篇通过变量的值去快速定位问题的案例,可以查看文章:

通过查看Windbg中变量值去定位C++软件异常问题icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125731044通过查看Windbg中变量值去定位C++软件异常的又一典型案例分享icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125793532

6、使用Process Explorer、Process Monitor等工具查看函数调用堆栈时需要用到pdb文件

       有时我们在分析软件问题时,需要先借助一些软件工具去分析,比如使用Process Explorer排查CPU高占用问题、使用Process Monitor监测目标程序的文件活动(读写了哪些文件)和注册表活动(读写了哪些注册表)。

6.1、使用Process Explorer中查看函数调用堆栈时需要使用pdb文件

       我们在使用Process Explorer排查CPU高占用问题时,现在进程列表中找到目标进程,然后双击之,在弹出的进程属性窗口中,切换到Threads标签页中,找到CPU占用高的那个线程,双击之,查看线程的函数调用堆栈,如下所示:

如果要在堆栈中看到具体的函数名和行号,则需要加载pdb文件。只需要将pdb文件放在对应的二进制文件的同级目录中,Process Explorer就会去搜索加载的。

       关于使用Process Explorer排查程序高CPU占用的实战分析案例,可以查看我之前写的文章:

使用Process Explorer/Process Hacker和Windbg高效排查软件高CPU占用问题icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/135822428

6.2、使用Process Monitor中查看函数调用堆栈时需要使用pdb文件

       我们可以使用Process Monitor监测目标程序的注册表活动和文件活动。比如监测程序都读写了哪些注册表项,都读写了哪些文件,当监测到记录时双击可以查看执行这些操作时的函数调用堆栈:

这样就知道是哪一处的代码执行了这个操作。

       关于使用Process Monitor监测注册表活动和文件活动的实战分析案例,可以查看我之前写的文章:
使用Process Monitor探测Windows系统高DPI缩放设置的注册表项icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/130586460%C2%A0%C2%A0使用Process Monitor工具探测日志文件是程序哪个模块生成的icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/133339034

7、在反汇编工具IDA中查看汇编代码也需要用到pdb文件

       这部分内容涉及到汇编相关内容,我会进行详细的展开,比如如何学习汇编排查软件异常需要掌握哪些基础的汇编知识如何查看C++源码对应的汇编代码以及通过查看汇编上下文去辅助定位软件异常的实例

7.1、使用反汇编工具IDA查看汇编代码上下文

       程序一般是崩溃在某一条汇编指令上,但有时通过查看这条汇编指令及异常发生时的函数调用堆栈可能无法定位问题,这时候可能就需要借助查看汇编代码上下文去辅助定位问题了。

CPU中最终执行的二进制机器码,等同于汇编代码。二进制机器码可读性差,汇编指令作为二进制指令的助记符,可读性好,我们阅读汇编代码等同于阅读二进制机器码。汇编代码能直观地反映出问题的原因所在,比如汇编指令中访问了一个很小的内存地址,引发内存访问违例。

       一句C++源码可能对应几行汇编代码,在查看汇编代码上下文(发生崩溃的那条汇编指令附近的上下文)时,需要将C++源码与汇编代码对照着看,找到二者的对应关系,这样才好读懂汇编上下文。抛开C++源码,直接去阅读汇编代码上下文,很难读懂,因为这需要很深的汇编功底才能做到。

       可以直接在Windbg中查看汇编代码上下文,但Windbg中显示的汇编代码没有注解,很难和C++源码对应起来。通常我们使用反汇编工具IDA打开二进制文件去查看汇编代码上下文。为了读懂汇编代码上下文,我们需要在IDA显示的汇编代码中看到具体的函数名、变量符号及相关注释信息:

通过这些信息去确定C++源码与汇编代码的对应关系,这样更便于我们读懂汇编上下文。如果要看到具体的函数名、变量符号及相关注释信息,IDA要加载当前打开的二进制文件的pdb文件。IDA从pdb中读出函数及变量等符号信息,然后利用这些符号信息去展示汇编代码。将pdb文件放在二进制文件的当前路径下即可,IDA会从当前路径中去搜索加载pdb文件。

        关于如何使用反汇编工具IDA,可以查看我的文章:

反汇编工具IDA使用详解icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/120635120

7.2、编译器优化代码对我们查看汇编上下文的影响

       有一点需要注意一下,C++源码在release下编译时编译器会对源码进行优化:(比如下图中的Visual Studio工程编译选项,默认使用最大速度优化)

生成的汇编代码是优化后的,这对我们去找C++源码与汇编代码的对应关系会产生一定的障碍。比如在C++源码中一个函数调用了另一个函数,如果编译器没对代码进行优化,则我们会看到call被调用函数指令。编译器可能会对代码进行优化,直接把函数调用给优化掉了,即用几句汇编代码就把函数调用给替代了。

       编译器之所以对代码进行优化,就是为了提高代码执行的效率。以优化函数调用为例,如果不优化,在发生函数调用时,需要将参数压到栈上传递给被调用函数,进入被调用函数后要保护主调函数的现场(保护现场,即保存相关主调函数相关寄存器的值),然后在即将退出函数时恢复主调函数的现场(恢复现场),最后将传递参数占用的栈空间给清理掉,这些都是函数调用的开销。

       将函数调用给优化掉,可以避免函数调用的开销,从而提高代码的执行速度。举一个最简单的例子,代码中调用了C函数memcpy执行内存拷贝,优化后的汇编代码中是看不到call memcpy这个函数的操作,几句汇编代码就代替了函数调用。

7.3、查看汇编上下文去辅助定位软件异常问题的实例

       关于如何通过查看汇编上下文去辅助定位问题,举一个简单的例子。在C++中,一个虚函数的调用(用类指针去调用一个虚函数),会对应三句汇编代码。这三句汇编代码涉及到两次寻址,虚函数的寻址过程如下所示:

虚函数调用的实例汇编代码如下:

.text:005103EB                 call    ds:__imp__GetContactPtr
.text:005103F1                 mov     [ebp+var_2AF4], eax
.text:005103F7                 mov     ecx, [ebp+var_2AF4]  // 1、ecx存放要调用的虚函数所属类对象的首地址
.text:005103FD                 mov     edx, [ecx]    // 2、【第一次寻址】类对象的首地址就是类中隐藏的成员变量虚函数表指针的首地址,对该地址寻址,从该地址读出的内容就是虚函数表的首地址
.text:005103FF                 mov     ecx, [ebp+var_2AF4] // 3、调用类的成员函数前,要将类对象首地址同ecx寄存器传给被调用的成员函数
.text:00510405                 mov     eax, [edx+78h]  // 4、【第二次寻址】根据目标虚函数在虚函数表中的偏移,找到目标虚函数在虚函数表中的位置,然后将该位置中的内容读出来,就是目标虚函数代码段地址
.text:00510408                 call    eax  // 5、直接call目标虚函数代码段地址,完成虚函数的调用

1)第一次寻址,是从类对象的虚函数表指针变量(内存)中读出变量值,该值就是虚函数的首地址;

2)第二次寻址,根据要调用的虚函数在虚函数表中的偏移位置,到虚函数表对应位置的内存中读出要调用的虚函数首地址(代码段的地址),然后去call这个虚函数。

       如果通过ecx寄存器传入的C++类对象的首地址是个空指针,则会导致三句汇编代码中的第一句出现崩溃。我们再反过来看,我们看出问题的这句汇编是虚函数调用的汇编(通过将源码与汇编代码对照着看来确定),如果崩溃的汇编指令中访问了一个很小的内存地址,则说明传入的C++类对象的首地址有问题,比如传入了空指针。

       如果要查看虚函数调用的汇编代码实现的详细说明,可以查看我之前写的文章:(文章中有具体的汇编实例)

几秒读懂C++虚函数调用的汇编代码实现icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/121046234       有次通过查看C++业务模块的汇编代码上下文,结合安卓系统生成的Tombstone文件,去快速定位安卓app底层的C++业务模块的崩溃问题。这一案例也写成了文章,感兴趣可以去查看:

使用IDA查看汇编代码上下文,结合安卓系统生成的Tombstone文件,排查安卓app程序底层库崩溃问题icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/135484104

7.4、排查软件异常需要掌握哪些基础汇编知识?

        有朋友可能会问,既然要用到汇编,要去阅读汇编代码上下文,那我们应该如何学习汇编?需要掌握哪些汇编相关的基础知识呢?之前我总结过排查C++软件异常所需要掌握的基础汇编知识,可以查看我的文章:

分析C++软件异常需要掌握的汇编知识汇总icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/124758670

7.5、通过查看C++代码对应的汇编代码去学习汇编,将C++源码与汇编代码对照着学

       大家在上大学时很多人都学习汇编(比如8086微机原理),只是孤立地学习汇编,没能和实际应用结合起来(这是大学教育中一个普遍存在的痛点问题),很难将汇编与C++等高级语言的源代码对应起来。不仅学着枯燥,而且感觉没什么实际应用价值。现在要解决项目中的实际问题,需要将汇编代码与C++源代码对应起来看,学起来会有意思、有价值很多。

       查看C++源代码去查看对应的汇编代码是一个学习汇编的途径,特别是看一些典型的C++语句对应的汇编代码实现,比如C++虚函数调用的汇编代码实现、函数调用时参数是如何压到栈上传递给被调用函数的。

       可以在正在调试C++代码的Visual Studio中查看C++代码片对应的汇编代码,也可以使用反汇编工具IDA打开二进制文件去查看汇编代码(Release版本二进制文件中是经过编译器优化后的汇编代码)。

7.6、学习汇编有哪些好处?

       其实,通过项目实践发现,熟悉汇编是很有用的,不仅可以辅助排查软件异常问题,还可以从汇编代码的角度理解很多高级语言不好理解的代码执行细节(比如多线程问题)。对于C++程序,在CPU中执行的是二进制文件中的一条条汇编指令,汇编代码能最直观地反映出程序的执行路径和细节。

       之前针对学习汇编的诸多好处进行了说明和总结,可以查看我的文章:

为什么要学习汇编?学习汇编有哪些好处?icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/130935428

8、最后

       本文是在多年的C++软件异常排查实践的基础上总结出来的,希望能给大家提供一定的借鉴或参考。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/678030.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

第九篇 有限状态机

实验九 有限状态机 9.1 实验目的 学习有限状态机的组成与类型; 掌握有限状态机的设计方式; 学习有限状态机的编码方式; 掌握使用有限状态机进行设计的方法。 9.2 原理介绍 9.2.1 有限状态机的基本概念 有限状态机(Finite …

【TPAMI-2024】EfficientTrain++帮你降低网络训练的成本

写在前面:本博客仅作记录学习之用,部分图片来自网络,如需引用请注明出处,同时如有侵犯您的权益,请联系删除! 文章目录 前言论文更容易学习的模式:频域易于学习的模式:空间域统一的训练课程 EFFICIENTTRAIN计…

计算机网络-BGP路由优选原则概述

前面我们已经学习了BGP的基础概念、对等体建立、报文类型等,也通过实践完成了IBGP和EBGP的实验配置,其实这些路由协议都是理论比较复杂,但是配置其实比较简单的,今天我们来学习BGP的路由优选原则。 一、IGP路由优选 前面我们学习了…

数据结构与算法-10_阻塞队列

文章目录 1.单锁实现2.双锁实现 1.单锁实现 Java 中防止代码段交错执行,有两种锁选择 synchronized 代码块,属于关键字级别提供锁保护,功能少ReentrantLock 类,功能丰富 以 ReentrantLock 为例 ReentrantLock lock new Reent…

tomcat-memcached会话共享配置

目录 1、安装memcache服务 2、把依赖的jar包移至tomcat/lib目录下 3、配置tomcat/conf/context.xml 4、重启tomcat服务 1、安装memcache服务 具体安装步骤此处不详细说明,自行根据实际情况安装即可 2、把依赖的jar包移至tomcat/lib目录下 3、配置tomcat/conf/c…

自定义类型:联合体和枚举

1. 联合体类型的声明 2. 联合体的特点 3. 联合体大小的计算 4. 枚举类型的声明 5. 枚举类型的优点 6. 枚举类型的使用 欢迎关注 熬夜学编程 创作不易,请多多支持 感谢大家的阅读、点赞、收藏和关注 如有问题,欢迎指正 1. 联合体 1.1 联合体类型的声…

java自学阶段一:基础知识学习

《项目案例—黑马tlias智能学习辅助系统》 目录: 异常 一:学习目标: 异常:能够看懂异常信息,了解异常体系结构和分类,掌握异常的两种处理方式,自定义异常。 二、异常: 1.异常的概…

yolo-v8window环境运行

源码https://github.com/ultralytics/ultralytics 1.用pycharm打开YOLOv8文件夹,下载依赖项,依赖项已经以作者的名字来封装好,所以直接在终端输入:pip install ultralytics,安装好之后会默认安装的cpu版本的torch&am…

WannaMine4.0病毒应急处置

一、前言 某日,通过流量监测设备和EDR发现挖矿请求告警,并存在长期445端口扫描。 二、病毒排查 上机排查,发现该服务器存在WannaMine4.0病毒,通过网上文章了解,如果请求挖矿域名遭安全设备拦截,会导致挖矿…

AI大模型页面

自己做的AI,模仿GPT。 访问地址:欢迎 请大家给点意见,需要追加哪些功能。

《企业应用架构模式》学习指南

导读:企业应用包括哪些?它们又分别有哪些架构模式? 世界著名软件开发大师Martin Fowler给你答案 01什么是企业应用 我的职业生涯专注于企业应用,因此,这里所谈及的模式也都是关于企业应用的。(企业应用还有…

做视频号小店什么类目最容易爆单?其实,弄懂这三点就会选品了

大家好,我是电商花花。 我们做视频号小店做什么类目最容易爆单? 其实任何类目都有属于自己的受众人群和客户,都非常容易爆单,我们想要爆单,就要选对类目,选对产品。 视频号上所有的类目基本上可以分为标…

塑料焊接机熔深对激光焊接质量有什么影响

塑料焊接机的熔深对焊接质量具有直接且显著的影响。以下是熔深对焊接质量影响的详细解释: 1. 焊接强度:熔深直接决定了焊缝的截面积,从而影响焊接接头的强度。较深的熔深意味着焊缝的截面积更大,可以提供更强的结合力,…

Apache DolphinScheduler 社区5月月报更新!

各位热爱 DolphinScheduler 的小伙伴们,社区5月份月报更新啦!这里将记录 DolphinScheduler 社区每月的重要更新,欢迎关注,期待下个月你也登上Merge Star月度榜单哦~ 月度Merge Star 感谢以下小伙伴5月份为 Apache DolphinSchedu…

SpringBoot发送Gmail邮件

1. 登录Gmail Gmail网址 点击右上角“小齿轮”,然后点击"查看所有设置" 点击“转发和 POP/IMAP”,按图中设置,然后点击保存: 2. 启用两步验证(https://myaccount.google.com/security) 登录上述网址,找…

【MyBatis-plus】saveBatch 性能调优和【MyBatis】的数据批量入库

总结最优的两种方法: 方法1: 使用了【MyBatis-plus】saveBatch 但是数据入库效率依旧很慢,那可能是是因为JDBC没有配置,saveBatch 批量写入并没有生效哦!!! 详细配置如下:批量数据入…

用 HTML+CSS 实现全屏爱心滴落的动画效果,中间可显示名字

需求 在页面上显示一行白色文字,同时有爱心滴落的动画效果。 效果 HTML 和 CSS 代码 <!DOCTYPE html> <html lang="en"> <head

Kerberoasting攻击

一. Kerberoasting攻击原理 1. 原理 Kerberoasting 是域渗透中经常使用的一项技术&#xff0c;是Tim Medin 在 DerbyCon 2014 上发布的一种域口令攻击方法&#xff0c;Tim Medin 同时发布了配套的攻击工具 kerberoast。此后&#xff0c;不少研究人员对 Kerberoasting 进行了改…

揭秘Facebook:数字时代的社交奥秘

前言 在当今的数字时代&#xff0c;社交网络已经深刻改变了人们的沟通方式、信息获取方式和社交方式。其中&#xff0c;Facebook作为全球最大的社交网络平台之一&#xff0c;扮演了至关重要的角色。从一个大学生项目发展成覆盖全球的社交巨头&#xff0c;Facebook不仅见证了互…

大尺寸图像分类检测分割统一模型:Resource Efficient Perception for Vision Systems

论文题目&#xff1a;Resource Efficient Perception for Vision Systems 论文链接&#xff1a;http://arxiv.org/abs/2405.07166 代码链接&#xff1a;https://github.com/Visual-Conception-Group/Localized-Perception-Constrained-Vision-Systems 作者设计了一个统一的模…