C++异常处理机制反汇编(三):32位下的异常结构分析

C++异常处理机制反汇编(三):更深入地了解32位下异常处理机制

前面两篇文章只是大致介绍了32位环境下异常处理的基本结构和基本流程,省略了很多内容。这篇文章就把各种结构以及详细的异常处理流程串起来讲一遍,加深对异常处理机制底层原理的理解,为学习64位环境下异常处理机制做好准备。

**异常处理是编译器和操作系统合作完成的。**下面我们以一个示例来分析:

cpp 复制代码
#include <stdio.h>

int main()
{
	try
	{
		printf("try block\r\n");
		throw 1;
	}
	catch (int e)
	{
		printf("catch int exception\r\n");
	}
	catch (float e)
	{
		printf("catch float exception\r\n");
	}

	return 0;
}

从ExceptionList说起

ExceptionList是NT_TIB(TEB的一部分)中的一个字段,它存储了当前线程的结构化异常处理链(SEH链)的头部指针。

那么,SEH链是怎么形成的呢?这其实是编译器的功劳。在编译阶段,编译器每进入一个包含try-catch块的函数时,就会将一个EXCEPTION_REGISTRATION结构体压入SEH链头部

asm 复制代码
push offset __except_handler3  ; handler
push fs:[0]                    ; 保存上一个SEH节点
mov fs:[0], esp                ; 将新节点设为链头

对于上面的示例,压入EXCEPTION_REGISTRATION结构体的代码如下:

编译器生成的其它结构

发生异常时

对于示例中的throw语句,实际上是调用了:

可以看到,该函数有两个参数。实际上,这个函数原型如下:

cpp 复制代码
extern "C" void __stdcall _CxxThrowException(
   void* pExceptionObject,
   _ThrowInfo* pThrowInfo
);

这个函数用于:

Builds the exception record and calls the runtime environment to start processing the exception.

它有两个参数:

  1. 异常对象的地址
  2. ThrowInfo结构体

下图是栈中这两个参数存储的内容:

我们在内存窗口中查看这两个地址实际存储的内容:

关于ThrowInfo结构,可以看第一篇文章:

关于这个函数的更多内容可以参考官方文档

在_CxxThrowException函数内部,又调用了RasieException函数:

在调用RasieException之后,控制权转到Windows内核,也就是执行了KiDispathException函数。

异常捕获

当异常发生时,操作系统会介入:Windows异常分发器(KiDispathException)会捕获异常,遍历线程的SEH链,链中的节点(在具有异常处理功能的函数序言部分压入SEH链)都有一个handler字段,在遍历该链的时候会调用handler函数,handler函数会调用_CxxFrameHandler

handler函数如下:

其中eax是FuncInfo的地址。在讲解_CxxFrameHandler之前,我们先来了解一下FuncInfo结构体。该结构体由编译器生成,编译器为每个包含异常处理的函数自动生成FuncInfo数据,它是连接C++异常处理源代码与Windows SEH的桥梁。它的作用可以看第一篇文章。_CxxFrameHandler中就使用了这个结构体。

下面我们来看看_CxxFrameHandler函数:

cpp 复制代码
EXCEPTION_DISPOSITION __CxxFrameHandler(
      EHExceptionRecord  *pExcept,
      EHRegistrationNode *pRN,
      void               *pContext,
      DispatcherContext  *pDC
   );

这个函数的作用如下:

Internal CRT function. Used by the CRT to handle structured exception frames.

关于这个函数的更多内容,可以查看官方文档

异常处理

在_CxxFrameHandler获取了异常的相关信息,并调用了InternalCxxFrameHandler函数对异常进行分类处理:

总结调用链

下面对上面的调用链进行总结:

txt 复制代码
throw语句
    ↓
_CxxThrowException(异常对象, ThrowInfo)
    ↓
RaiseException(0xE06D7363, 4个参数)
    ↓
Windows内核异常分发器
    ↓
读取NT_TIB.ExceptionList(SEH链头)
    ↓
遍历SEH链,调用每个节点的handler
    ↓
    ↓→ 到达函数注册的__CxxFrameHandler
    ↓       ↓
    ↓   使用FuncInfo定位try块
    ↓       ↓
    ↓   使用TryBlockMapEntry查找catch范围
    ↓       ↓
    ↓   使用msRttiDscr匹配catch类型
    ↓       ↓
    ↓   使用ThrowInfo和CatchTableTypeArray进行RTTI匹配
    ↓       ↓
    ↓   使用PMD调整this指针(如果需要)
    ↓       ↓
    ↓   使用UnwindMapEntry进行栈展开
    ↓       ↓
    ↓   设置EIP跳转到CatchProc
    ↓       ↓
    ↓← ExceptionContinueExecution
    ↓
恢复执行到catch块
相关推荐
love530love3 分钟前
ComfyUI MediaPipe 终极填坑:解决 incompatible function arguments 报错,基于代理模式的猴子补丁升级版
人工智能·windows·comfyui·mediapipe·猴子补丁·monkey patch·python 3.12
今夕资源网29 分钟前
Windows Terminal更舒适的命令行环境 仅11MB 支持并行运行WSLLinux子系统 github开源项目
windows·github·命令行·cmd·terminal
java_logo2 小时前
SiYuan 思源笔记 Docker 部署终极指南:Windows+Linux 双平台
windows·笔记·docker·思源笔记·思源笔记部署·docker部署思源笔记·思源笔记文档
charlie1145141912 小时前
通用GUI编程技术——图形渲染实战(三十八)——顶点缓冲与输入布局:GPU的第一个三角形
开发语言·c++·学习·图形渲染·win32
用户805533698033 小时前
现代Qt开发教程(新手篇)1.10——进程
c++·qt
海参崴-3 小时前
C++ STL篇 AVL树的模拟实现
开发语言·c++
汉克老师3 小时前
GESP2025年6月认证C++五级( 第二部分判断题(1-10))
c++·贪心算法·分治算法·线性筛法·gesp5级·gesp五级
6Hzlia3 小时前
【Hot 100 刷题计划】 LeetCode 15. 三数之和 | C++ 排序+双指针
c++·算法·leetcode
vegetablesssss3 小时前
VTK切割图
c++·qt·vtk
CN-Dust3 小时前
【C++】for循环例题专题
java·c++·算法