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块
相关推荐
Cinema KI6 小时前
C++11(下) 入门三部曲终章(基础篇):夯实语法,解锁基础编程能力
开发语言·c++
燃于AC之乐6 小时前
深入解剖STL List:从源码剖析到相关接口实现
c++·stl·list·源码剖析·底层实现
汉克老师6 小时前
GESP2025年6月认证C++二级( 第一部分选择题(9-15))
c++·循环结构·求余·gesp二级·gesp2级·整除、
不想睡觉_6 小时前
优先队列priority_queue
c++·算法
rainbow688916 小时前
EffectiveC++入门:四大习惯提升代码质量
c++
秋邱17 小时前
用 Python 写出 C++ 的性能?用CANN中PyPTO 算子开发硬核上手指南
开发语言·c++·python
我在人间贩卖青春17 小时前
C++之析构函数
c++·析构函数
我在人间贩卖青春17 小时前
C++之数据类型的扩展
c++·字符串·数据类型
wangjialelele18 小时前
平衡二叉搜索树:AVL树和红黑树
java·c语言·开发语言·数据结构·c++·算法·深度优先