楔子

以win11 + vs2022运行VC++ 编译观察的结果。

如果安装了Visual Studio 2022,比如安装在D盘,则路径:

D:\Visual Studio\IDE\VC\Tools\MSVC\14.33.31629

下面包含了vcruntime.dll的源码,主要VC编译器和ntdll.dll 以及KernelBase.dll交互。

注:本篇不叙述正常的windows用户态和内核态异常处理,仅看用户态下偏角的运作方式。

代码

void main()
{
char* pStr = NULL;
try
{
throw pStr;
}
catch (char* s)
{
printf("Hello S");
}
getchar();
}

try里面抛出一个异常,异常调用堆栈如下

分析

红色箭头,throw抛出异常之后,调用了_CxxThrowException函数,这个函数刚好在vcruntime.dll里面。



_CxxThrowException函数源码在VS路径:

D:\Visual Studio\IDE\VC\Tools\MSVC\14.33.31629\crt\src\vcruntime\throw.cpp
extern "C" __declspec(noreturn) void __stdcall _CxxThrowException(
void *pExceptionObject, // The object thrown
_ThrowInfo *pThrowInfo // Everything we need to know about it
) {
//为了方便观看,此处省略一万字
RaiseException(EH_EXCEPTION_NUMBER, EXCEPTION_NONCONTINUABLE, _countof(parameters), parameters);
}

_CxxThrowException又调用了RaiseException函数。RaiseException函数会进入到内核里面分别调用如下:

ntdll.dll!KiUserExceptionDispatch-》
ntdll.dll!RtlDispatchException-》
ntdll.dll!RtlpExecuteHandlerForException-》

windows异常分为内核态和用户态处理过程,RtlpExecuteHandlerForException则刚好是用户态处理过程。这些过程过于复杂,此处为了避免无端枝节,不赘述。

RtlpExecuteHandlerForException是调用异常处理的函数,通俗点就是跳转到catch地址,然后执行catch后面的代码。

在VS2022里面,异常处理函数是__CxxFrameHandler4(此函数在vcruntime.dll里面)

源码在路径:

D:\Visual Studio\IDE\VC\Tools\MSVC\14.33.31629\crt\src\vcruntime\risctrnsctrl.cpp

__CxxFrameHandler4后面的调用函数是:

__CxxFrameHandler4-》
vcruntime140_1d.dll!__InternalCxxFrameHandler-》
vcruntime140_1d.dll!FindHandler-》
vcruntime140_1d.dll!CatchIt-》
vcruntime140_1d.dll!__FrameHandler4::UnwindNestedFrames-》
ntdll.dll!RtlUnwindEx-》
ntdll.dll!RtlGuardRestoreContext-》
ntdll.dll!RtlRestoreContext-》
ntdll.dll!RtlpExecuteHandlerForUnwind-》
vcruntime140_1d.dll!__CxxFrameHandler4-》

到了这里实际上已经接近完成了,但是实际上还远不止如此。如果再继续调用,会直接跳到函数

ntdll.dll!RcConsolidateFrames -》
vcruntime140_1d.dll!__FrameHandler4::CxxCallCatchBlock

从__CxxFrameHandler4到RcConsolidateFrames经历什么?会发现跟上面的对不上。堆栈也没有显示。

为此,还需要继续跟踪

汇编

为了能看到从__CxxFrameHandler4到RcConsolidateFrames经历什么,我们跟踪下汇编



__CxxFrameHandler4调用了RtlGuardRestoreContext,继续单步F11,RtlGuardRestoreContext里面调用了函数RtlGuardRestoreContext



RtlGuardRestoreContext里面有个跳转指令jmp rdx。看下图:



jmp指令调到了如下



而call rax的rax就是CxxCallCatchBlock函数的地址。

因为RcConsolidateFrames函数是在ntdll.dll里面没有被开源,所以两次跳转(jmp 和 call 应该是这个函数里面所做的动作)

如此一来就对上上面的那个函数调用顺序(从上到下),但是还有一个问题,这个try里面抛出了异常,那么catch是何时被执行的呢?

Catch

理顺了RcConsolidateFrames函数调用顺序,RcConsolidateFrames自己则调用了函数CxxCallCatchBlock。这个函数里面调用了catch处理异常。

CxxCallCatchBlock函数源码地址:

D:\Visual Studio\IDE\VC\Tools\MSVC\14.33.31629\crt\src\vcruntime\frame.cpp(1344行)

源码:

void * RENAME_EH_EXTERN(__FrameHandler4)::CxxCallCatchBlock(
EXCEPTION_RECORD *pExcept
)
{
//为了方便观看,此处省略一万行
continuationAddress = RENAME_EH_EXTERN(_CallSettingFrame_LookupContinuationIndex)
}
RENAME_EH_EXTERN(_CallSettingFrame_LookupContinuationIndex)
这段的原型是:

总结下:

堆栈的调用如下:

	vcruntime140_1d.dll!__FrameHandler4::CxxCallCatchBlock
(jmp rdx)ntdll.dll!RcConsolidateFrames
ntdll.dll!RtlRestoreContext
ntdll.dll!RtlGuardRestoreContext
ntdll.dll!RtlUnwindEx
vcruntime140_1d.dll!__FrameHandler4::UnwindNestedFrames
vcruntime140_1d.dll!CatchIt
vcruntime140_1d.dll!FindHandler
vcruntime140_1d.dll!__InternalCxxFrameHandler
vcruntime140_1d.dll!__CxxFrameHandler4
ntdll.dll!RtlpExecuteHandlerForException()
ntdll.dll!RtlDispatchException
ntdll.dll!KiUserExceptionDispatch()
KernelBase.dll!RaiseException()
vcruntime140d.dll!_CxxThrowException
ConsoleApplication2.exe!main

作者:江湖评谈

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

windows C++ 异常调用栈简析的更多相关文章

  1. cocos2d-x安卓应用启动调用过程简析

    调用org.cocos2dx.cpp.AppActivity AppActivity是位于proj.android/src下的开发者类(即开发者自定义的类),它继承自org.cocos2dx.lib. ...

  2. Windows下获取Dump文件以及进程下各线程调用栈的方法总结(转)

    1. Dump文件的用途 Dump文件, 主要用于诊断一个进程的运行状态,尤其是碰到崩溃(Crash)或者挂起(hang)不响应时,需要分析它的工作状态.  除了平时常见的attach到这个进程, 分 ...

  3. 编写高质量代码改善C#程序的157个建议——建议70:避免在调用栈较低的位置记录异常

    建议70:避免在调用栈较低的位置记录异常 并不是所有的异常都要被记录到日志,一类情况是异常发生的场景需要被记录,还有一类就是未被捕获的异常.未被捕获的异常通常被视为一个Bug,所以,对于它的记录,应该 ...

  4. 简析 .NET Core 构成体系

    简析 .NET Core 构成体系 Roslyn 编译器 RyuJIT 编译器 CoreCLR & CoreRT CoreFX(.NET Core Libraries) .NET Core 代 ...

  5. [转载] Thrift原理简析(JAVA)

    转载自http://shift-alt-ctrl.iteye.com/blog/1987416 Apache Thrift是一个跨语言的服务框架,本质上为RPC,同时具有序列化.发序列化机制:当我们开 ...

  6. JDK框架简析--java.lang包中的基础类库、基础数据类型

    题记 JDK.Java Development Kit. 我们必须先认识到,JDK不过,不过一套Java基础类库而已,是Sun公司开发的基础类库,仅此而已,JDK本身和我们自行书写总结的类库,从技术含 ...

  7. 简析.NET Core 以及与 .NET Framework的关系

    简析.NET Core 以及与 .NET Framework的关系 一 .NET 的 Framework 们 二 .NET Core的到来 1. Runtime 2. Unified BCL 3. W ...

  8. Java Annotation 及几个常用开源项目注解原理简析

    PDF 版: Java Annotation.pdf, PPT 版:Java Annotation.pptx, Keynote 版:Java Annotation.key 一.Annotation 示 ...

  9. SpringMVC源码情操陶冶-DispatcherServlet简析(二)

    承接前文SpringMVC源码情操陶冶-DispatcherServlet类简析(一),主要讲述初始化的操作,本文将简单介绍springmvc如何处理请求 DispatcherServlet#doDi ...

随机推荐

  1. 使用gulp助力前端自动化

    前言 随着前端诸如webpack,rollup,vite的发展,gulp感觉似乎好像被取代了.其实并没有,只不过它从台前退居到了幕后.我们仍然可以在很多项目中看到它的身影,比如elementplus. ...

  2. 3.0.0 alpha 重磅发布!九大新功能、全新 UI 解锁调度系统新能力

    2022 年 4 月 22 日,Apache DolphinScheduler 正式宣布 3.0.0 alpha 版本发布!此次版本升级迎来了自发版以来的最大变化,众多全新功能和特性为用户带来新的体验 ...

  3. Fiddler抓包工具下载安装及使用

    一.Fiddler简介 简介: Fiddler是一款强大的Web调试工具,他能记录所有客户端和服务器的HTTP/HTTPS请求 工作原理: Fiddler是以代理web服务器的形式工作的,它使用代理地 ...

  4. Apache HttpClient 5 使用详细教程

    点赞再看,动力无限. 微信搜「程序猿阿朗 」. 本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章. 超文本传输协议(HTTP)可能是当今 ...

  5. 【java】学习路径40-Buffer缓冲区输入流

    @Testpublic void testBufferInputStream(){ BufferedInputStream bfis = null; try { bfis = new Buffered ...

  6. React报错之Property 'value' does not exist on type EventTarget

    正文从这开始~ 总览 当event参数的类型不正确时,会产生"Property 'value' does not exist on type EventTarget"错误.为了解决 ...

  7. Session认证机制与JWT认证机制

    一.什么是身份认证? 身份认证(Authentication)又称"身份验证"."鉴权",是指通过一定的手段,完成对用户身份的确认.日常生活中的身份认证随处可见 ...

  8. KingbaseES 时间类型数据和oracle时间类型的区别

    Oracle日期时间类型有两类,一类是日期时间类型,包括Date, Timestamp with time zone, Timestamp with local time zone.另一类是Inter ...

  9. KingbaseES 开启事务提交跟踪

    KingbaseESV8R6有个参数 track_commit_timestamp,用来开启跟踪事务提交的时间戳. 配置 编辑kingbase.conf,添加配置如下: track_commit_ti ...

  10. 记一次 Sedona(GeoSpark) 空间计算优化

    项目需求需要空间计算能力,开始选型Sedona(GeoSpark)来完成, 需求需要每一条数据在满足某条件的情况下,去查找某张表进行空间匹配,找到离这个点(point)最近的一条道路(lineStri ...