.Net CLR异常和windows C++ 异常调用栈简析

楔子

前面一篇研究了下C++异常的,这篇来看下,CLR的异常内存模型,实际上都是一个模型,承继自windows异常处理机制。不同的是,有VC编译器(vcruntime.dll)接管的部分,被CLR里面的函数ProcessCLRException接管了。
注意:这里面省略了一部分不必要赘述的细节问题,版本号分别为(CLR PreView 7和vcruntime 14.0)

C++异常栈

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

CLR异常栈

>	coreclr.dll!ProcessCLRException	C++ntdll.dll!RtlpExecuteHandlerForExceptionntdll.dll!RtlDispatchExceptionntdll.dll!KiUserExceptionDispatchKernelBase.dll!RaiseExceptioncoreclr.dll!`RaiseTheExceptionInternalOnly'::`53'::__Body::Runcoreclr.dll!RaiseTheExceptionInternalOnlycoreclr.dll!IL_Throw00007ffa6faf040c()

对比

CLR异常栈的地址00007ffa6faf040c()实际上就是C#的main函数入口。对比的是C异常栈的函数入口main。
其它的一一对应(C# ----C++):
1.IL_Throw-》_CxxThrowException
2.RaiseTheExceptionInternalOnly和RaiseTheExceptionInternalOnly以及RaiseException-》RaiseException()
3.KiUserExceptionDispatch-》KiUserExceptionDispatch()
4.RtlDispatchException-》RtlDispatchException
5.RtlpExecuteHandlerForException》RtlpExecuteHandlerForException
6.ProcessCLRException -》__CxxFrameHandler4

注意粗体部分对应的,这个地方开始,VC和CLR分道扬镳了。各自实现了后面函数异常处理的实现。

下面是windows 异常


楔子

以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里面。
285a99d8354bfcc2efe7eba2c121af3c.png
_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经历什么,我们跟踪下汇编
b030419fb333edb8d4b118e70e44ff09.png
__CxxFrameHandler4调用了RtlGuardRestoreContext,继续单步F11,RtlGuardRestoreContext里面调用了函数RtlRestoreContext
fa80c3c9f2051f3f846fb81b73a9ecbf.png
RtlRestoreContext里面有个跳转指令jmp rdx。看下图:
2bd8bffb1af34d9425ba7342b411b921.png
jmp指令调到了如下
4912104d52070b73e5a6e6d7e16ce332.png
而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)
这段的原型是:

12c812844b4bef3e925a6e218b5e8a47.png

59282f088c15d6ea650999ffcfdf14cf.png

注意的点:

CxxCallCatchBlock函数不会返回,直接跳转到catch大括号下面的代码里面继续执行后面的代码段。

void * RENAME_EH_EXTERN(__FrameHandler4)::CxxCallCatchBlock(EXCEPTION_RECORD *pExcept)
{//为了便于观察, 此处省略一万字,return continuationAddress;
}

4046c99d2cc3f6d5725aef26ae60c2e7.png

总结下:

堆栈的调用如下:

vcruntime140_1d.dll!__FrameHandler4::CxxCallCatchBlock(jmp rdx)ntdll.dll!RcConsolidateFramesntdll.dll!RtlRestoreContextntdll.dll!RtlGuardRestoreContext	ntdll.dll!RtlUnwindEx	vcruntime140_1d.dll!__FrameHandler4::UnwindNestedFramesvcruntime140_1d.dll!CatchItvcruntime140_1d.dll!FindHandlervcruntime140_1d.dll!__InternalCxxFrameHandlervcruntime140_1d.dll!__CxxFrameHandler4	ntdll.dll!RtlpExecuteHandlerForException()	ntdll.dll!RtlDispatchExceptionntdll.dll!KiUserExceptionDispatch()KernelBase.dll!RaiseException()vcruntime140d.dll!_CxxThrowExceptionConsoleApplication2.exe!main

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Codeforces936C. Lock Puzzle

给个串&#xff0c;只能用操作shift x表示把后面x个字符翻转后放到串的前面。问s串怎么操作能变t串。n<2000&#xff0c;操作次数<6100。 打VP时这转来转去的有点晕。。。 可以想一种逐步构造的方法&#xff0c;即从一个小的完成构造的部分通过一顿操作&#xff0c;在不影…

公共服务领域英文译写规范_公共领域日:对版权和公共领域重要性的思考

公共服务领域英文译写规范The first of the year is Public Domain Day, a day intended to call attention to copyright issues and the public domain. At the Center for the Study of the Public Domain they have an interesting (and sobering) review of works that wo…

Elasticsearch 实战经验总结

Centos7下es 7.7.0安装配置 怎么安装使用elasticsearch-head插件 用logstash同步Mysql数据到ES Springboot使用ES官方推荐方式REST Client整合ES实现关键词高亮 ELK-Elasticsearch&#xff0c;Logstash&#xff0c;kibana搭建基于日志文件的日志分析系统 设置elasticsearc…

.Net 7 的 AOT 和 CLR有什么区别?

楔子&#xff1a;AOT和 CLR的区别是什么呢&#xff1f;大部分人肯定会说&#xff0c;一个编译成本地机器码&#xff08;Native Code&#xff09;&#xff0c;一个是JIT即时编译的结果。这么说&#xff0c;其实也对&#xff0c;但是不具体。具体应该怎么看呢&#xff1f;AOTAOT实…

接入amazon avs_每日新闻综述:亚马逊将互联网接入推向全球的宏伟计划

接入amazon avsPlus Snap’s big push to stay relevant, Amazon’s Alexa-powered AirPods alternatives, more Android Q news, and a lot more. It’s time to talk about the biggest, coolest, or generally most interesting stories from the last 24 hours. 加上Snap保…

计算的未来

我自己倒是后来也是觉得我自己可以想象一个未来的技术&#xff0c;就是以后的编程的语言和库可以抽象现在的一些高级语言的关键字。比如要写一个编辑器的时候&#xff0c;只要给点这些东西的数据结构和数据流向&#xff0c;而一些什么很繁琐的一些底层编码都是可以用高级语言来…

nginx 实用配置问题总结

配置 tomcat&#xff0c;nginx&#xff0c;解决 post 请求超时问题nginx 跨域问题 CORS policy: No Access-Control-Allow-Originnginx 配置静态验证文件&#xff0c;报 404&#xff0c;解决方案nginx 获取用户真实 IPcentos 部署 php 网站方法-使用 nginx ssl https

零部件分类属性

离散制造业的研发、生产跟产品零部件紧密联系在一起&#xff0c;从企业业务流程来说零部件涉及研发、采购、仓储、生产、质量、售后和配件等多个部门&#xff0c;为了更好地管理零部件&#xff0c;下面我们一起来看看零部件概念及分类。1、按行业属性分类&#xff08;1&#xf…

键盘忍者:使用单个热键弹出Vista日历

We’ve covered how to access the Windows Vista Calendar using the keyboard, but what if you wanted to assign a single keystroke to pop up the calendar? Yeah, sure, you can just click it with the mouse, but where’s the geek fun in that? 我们已经介绍了如何…

Linux下全局安装composer方法

# 下载composer [vagrantlocalhost ~]$ curl -sS https://getcomposer.org/installer | php# 将composer.phar文件移动到bin目录以便全局使用composer命令 [vagrantlocalhost ~]$ mv composer.phar /usr/local/bin/composer# 切换国内源 [vagrantlocalhost ~]$ composer config…

如何使用必应地图 WPF 控件

如何使用必应地图 WPF 控件如何使用必应地图 WPF 控件作者&#xff1a;WPFDevelopersOrg - 驚鏵原文链接&#xff1a;https://github.com/WPFDevelopersOrg/WPFDevelopers框架使用.NET40&#xff1b;Visual Studio 2019;Bing Maps WPF 控件需要 .NET Framework 4.0和 Windows S…

如何保存推特链接以供以后从台式机和手机阅读

Have you come across a lot of interesting links from Twitter, but you don’t have the time to read all of them? Today we’ll show you how to read these links later from your desktop and phone. 您是否遇到过Twitter上很多有趣的链接&#xff0c;但没有时间阅读所…

scrapy爬虫实战分享

自动登录脚本参考scrapy爬虫启示录-小伙子老夫看你血气方刚这本《爬虫秘录》就传给你了Scrapy初章-Scrapy理论简介Scrapy次章-啥也不干就是爬图Scrapy第四章-设置代理IP偷偷爬图Scrapy第三章-图片存库MysqlScrapy第五章-多线程加速爬图Scrapy终章-1024福利Scrapy最最最终章-搂一…

【重大更新】DevExpress v17.2新版亮点—Bootstrap篇(二)

用户界面套包DevExpress v17.2日前终于正式发布&#xff0c;本站将以连载的形式为大家介绍各版本新增内容。本文将介绍了Bootstrap Controls v17.2 的CardView、Charts、Editors、GridView、Layout等新功能&#xff0c;快来下载试用新版本&#xff01; GridView Toolbar 在此版…

盘点 .NET 7 新功能

点击上方蓝字关注我们&#xff08;本文阅读时间&#xff1a;20分钟)本文翻译于 Jeremy Likness, Angelos Petropoulos 和 Jon Douglas 的博客.NET 7 为C# 11/F# 7、.NET MAUI、ASP.NET Core/Blazor、Web API、WinForms、WPF 等应用程序带来了更高的性能和新功能。使用 .NET 7&a…

onlyoffice采坑笔记

中文版onlyoffice/documentserver镜像制作onlyoffice 20并发限制处理&#xff0c;up to 20 maximumonlyoffice安装-Linuxwindows 10 下用docker安装onlyoffice服务 onlyoffice安装-Linux 0 点赞 ⋅ 0 回复 ⋅ 3月前onlyoffice相关命令记录 0 点赞 ⋅ 0 回复 ⋅ 3月前onlyoffice…

nb-iot链路层加密_Google为低端Android手机和IoT设备创建了更快的加密

nb-iot链路层加密Google谷歌Low-resource Android phones and IoT devices don’t have the processing power to use modern encryption services, which makes them vulnerable to hacking. That’s why Google is introducing Adiantum, a super-fast encryption standard f…

用offset调用文章

一。用offset偏移调用文章&#xff0c;这个我认为是比较好的&#xff0c;经常用。容易控制1. 第一个post显示5篇query_posts(showpost 5); 2.那么第二个post显示除上面显示过的6篇query_posts(showpost 6 & offset5); 二、用置顶文章1.第一个Post显示置顶文章query_posts(s…

MediatRPC - 基于MediatR和Quic通讯实现的RPC框架,比GRPC更简洁更低耦合,开源发布第一版...

大家好&#xff0c;我是失业在家&#xff0c;正在找工作的博主Jerry。作为一个.Net架构师&#xff0c;就要研究编程艺术&#xff0c;例如SOLID原则和各种设计模式。根据这些原则和实践&#xff0c;实现了一个更简洁更低耦合的RPC&#xff08;Remote Procedure Calls&#xff09…

新鲜抓取古文赏析五千篇

新鲜抓取的古文&#xff0c;有感兴趣的可以来看看。-IT源点-古文赏析 外科精義 黄景昌-古诗文选集 鼎镌陈眉公先生批评西廂记 世醫得效方 汪炎昶-古诗文选集 至正条格 乐郊私语 敖氏傷寒金鏡錄 十四經發揮 宋史 草泽狂歌 世医得效方 : 二十卷. 卫生宝鉴 辽史 陈深-古诗文选集 …