C++ EH Exception(0xe06d7363)----抛出过程
C++ EH Exception是Windows系统VC++里对c++语言的throw的分类和定义,它的代码就是0xe06d7363。在VC++里其本质也是SEH结构化异常机制。在我们分析用户崩溃的例子中经常会遇到它。一般情况下,遇到它,是我们代码里用throw抛出异常后没有处理导致程序崩溃。下面分析一下它的原理。
我们借助一段代码来跟踪和分析
class MyException
{
public:
int nErr;
char *szMessage;
public:
MyException(void)
:nErr()
, szMessage(NULL)
{ } MyException(int nerr,char *szMess)
:nErr(nerr)
, szMessage(szMess)
{ } ~MyException(void)
{ }
};
int _tmain(int argc, _TCHAR* argv[])
{
try
{
MyException me(1, "test exception");
throw me;
}
catch (MyException me1)
{
printf("err=%s\n",me1.szMessage);
}
}
将上述代码在VS2013里编译调试,转到汇编
: try
: {
mov dword ptr [ebp-],
: MyException me(, "test exception");
0136179E push 1367858h
013617A3 push
013617A5 lea ecx,[ebp-1Ch]
013617A8 call MyException::MyException (013610D2h)
013617AD mov dword ptr [ebp-104h],eax
013617B3 mov byte ptr [ebp-],
: throw me;
013617B7 mov eax,dword ptr [ebp-1Ch]
013617BA mov dword ptr [ebp-0FCh],eax
013617C0 mov ecx,dword ptr [ebp-18h]
013617C3 mov dword ptr [ebp-0F8h],ecx
013617C9 push 1369084h
013617CE lea edx,[ebp-0FCh]
013617D4 push edx
013617D5 call __CxxThrowException@8 (0136111Dh)
: }
我们可以看到,throw 首先通过下面的代码
013617B7 mov eax,dword ptr [ebp-1Ch]
013617BA mov dword ptr [ebp-0FCh],eax
013617C0 mov ecx,dword ptr [ebp-18h]
013617C3 mov dword ptr [ebp-0F8h],ecx
复制了一份异常对象MyException me,然后取了这份拷贝的地址作为参数传给了__CxxThrowException函数,同时将异常类型信息也传递了过去
013617CE lea edx,[ebp-0FCh]
013617D4 push edx
接着调用了__CxxThrowException函数,我们进入看看
下面是改函数的代码
/////////////////////////////////////////////////////////////////////////////
//
// _CxxThrowException - implementation of 'throw'
//
// Description:
// Builds the NT Exception record, and calls the NT runtime to initiate
// exception processing.
//
// Why is pThrowInfo defined as _ThrowInfo? Because _ThrowInfo is secretly
// snuck into the compiler, as is the prototype for _CxxThrowException, so
// we have to use the same type to keep the compiler happy.
//
// Another result of this is that _CRTIMP can't be used here. Instead, we
// synthesisze the -export directive below.
//
// Returns:
// NEVER. (until we implement resumable exceptions, that is)
// // We want double underscore for CxxThrowException for ARM CE only
__declspec(noreturn) extern "C" void __stdcall
#if !defined(_M_ARM) || defined(_M_ARM_NT)
_CxxThrowException(
#else
__CxxThrowException(
#endif
void* pExceptionObject, // The object thrown
_ThrowInfo* pThrowInfo // Everything we need to know about it
) {
EHTRACE_ENTER_FMT1("Throwing object @ 0x%p", pExceptionObject); static const EHExceptionRecord ExceptionTemplate = { // A generic exception record
EH_EXCEPTION_NUMBER, // Exception number
EXCEPTION_NONCONTINUABLE, // Exception flags (we don't do resume)
NULL, // Additional record (none)
NULL, // Address of exception (OS fills in)
EH_EXCEPTION_PARAMETERS, // Number of parameters
{ EH_MAGIC_NUMBER1, // Our version control magic number
NULL, // pExceptionObject
NULL,
#if _EH_RELATIVE_OFFSETS
NULL // Image base of thrown object
#endif
} // pThrowInfo
};
EHExceptionRecord ThisException = ExceptionTemplate; // This exception ThrowInfo* pTI = (ThrowInfo*)pThrowInfo;
if (pTI && (THROW_ISWINRT( (*pTI) ) ) )
{
ULONG_PTR *exceptionInfoPointer = *reinterpret_cast<ULONG_PTR**>(pExceptionObject);
exceptionInfoPointer--; // The pointer to the ExceptionInfo structure is stored sizeof(void*) infront of each WinRT Exception Info. WINRTEXCEPTIONINFO** ppWei = reinterpret_cast<WINRTEXCEPTIONINFO**>(exceptionInfoPointer);
pTI = (*ppWei)->throwInfo; (*ppWei)->PrepareThrow( ppWei );
} //
// Fill in the blanks:
//
ThisException.params.pExceptionObject = pExceptionObject;
ThisException.params.pThrowInfo = pTI;
#if _EH_RELATIVE_OFFSETS
PVOID ThrowImageBase = RtlPcToFileHeader((PVOID)pTI, &ThrowImageBase);
ThisException.params.pThrowImageBase = ThrowImageBase;
#endif //
// If the throw info indicates this throw is from a pure region,
// set the magic number to the Pure one, so only a pure-region
// catch will see it.
//
// Also use the Pure magic number on Win64 if we were unable to
// determine an image base, since that was the old way to determine
// a pure throw, before the TI_IsPure bit was added to the FuncInfo
// attributes field.
//
if (pTI != NULL)
{
if (THROW_ISPURE(*pTI))
{
ThisException.params.magicNumber = EH_PURE_MAGIC_NUMBER1;
}
#if _EH_RELATIVE_OFFSETS
else if (ThrowImageBase == NULL)
{
ThisException.params.magicNumber = EH_PURE_MAGIC_NUMBER1;
}
#endif
} //
// Hand it off to the OS:
// EHTRACE_EXIT;
#if defined(_M_X64) && defined(_NTSUBSET_)
RtlRaiseException( (PEXCEPTION_RECORD) &ThisException );
#else
RaiseException( ThisException.ExceptionCode,
ThisException.ExceptionFlags,
ThisException.NumberParameters,
(PULONG_PTR)&ThisException.params );
#endif
}
可以看到,这个函数首先是创建了一个EHExceptionRecord 对象,其实对应的就是 SEH里的结构EXCEPTION_RECORD,并且给这个结构成员赋值。
在这里通过如下代码,0xe06d7363就赋值给ThisException.ExceptionCode

还有就是将ThrowInfo赋值给EHExceptionRecordThisException.params.pThrowInfo。_ThrowInfo 结构体定义如下:
typedef const struct _s__ThrowInfo
{
unsigned int attributes;
_PMFN pmfnUnwind;
int (__cdecl*pForwardCompat)(...);
_CatchableTypeArray *pCatachableTypeArray;
} _ThrowInfo;
结构体中重要的成员是_CatchableTypeArray。它包含了程序运行时抛出对象的类新信息(RTTI).
如果你的程序运行时抛出一个my_exception类型的对象,那么抛出的数据参数pCatchableTypeArray包含了两个重要子数据信息。一个是typeid(my_exception),另外一个是typeid(std::exception)。
在我们的例子里

紧接着就调用RaiseException函数进入了异常的分发过程。
综上,在C++ EH Exception 的异常里,EXCEPTION_RECORD结构填充如下:
ExceptionAddress: 异常地址 76d018a2 (KERNELBASE!RaiseException+0x00000062)
ExceptionCode: 异常代码 e06d7363 (C++ EH exception)
ExceptionFlags: 标志 00000001
NumberParameters: 3 or 4 64位时是4
Parameter[0]: 19930520//魔数
Parameter[1]: 00d5f690// 抛出的异常对象指针
Parameter[2]: 00989084// 异常对象类型信息
Parameter[3]: 0000000010000000 // 64位下模块句柄HINSTANCE
C++ EH Exception(0xe06d7363)----抛出过程的更多相关文章
- 异常CLRDBG_NOTIFICATION_EXCEPTION_CODE( 0x04242420)的抛出过程
新建一个c#控制工程,就用自动生成的代码,不用补任何代码,如下: using System; using System.Collections.Generic; using System.Linq; ...
- 异常0xc000041d的抛出过程
为了说明这个过程,我们必须写一个示例程序,如下: #include "stdafx.h" #include <tchar.h> #include <stdio.h ...
- EXCEPTION_HIJACK(0xe0434f4e)异常的抛出过程
样例工程 在VS2013里新建一个C#控制台工程,写下如下代码: using System; using System.Collections.Generic; using System.Linq; ...
- C++ EH Exception(0xe06d7363)---捕获过程
书接上文<C++ EH Exception(0xe06d7363)----抛出过程>,下面我们讲下,VC++是如何catch到异常且处理的. 我们知道,在VC++里,C++异常实现的底层机 ...
- springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑。为了区别不同的异常通常根据异常类型自定义异常类,这里我们创建一个自定义系统异常,如果controller、service、dao抛出此类异常说明是系统预期处理的异常信息。
springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑. 1.1 异常处理思路 系统中异常包括两类:预期异常和运行时异常RuntimeEx ...
- (转)spring异常抛出触发事务回滚策略
背景:在面试时候问到事务方法在调用过程中出现异常,是否会传递的问题,平时接触的比较少,有些懵逼. spring异常抛出触发事务回滚策略 Spring.EJB的声明式事务默认情况下都是在抛出unchec ...
- Spring异常抛出触发事务回滚
Spring.EJB的声明式事务默认情况下都是在抛出unchecked exception后才会触发事务的回滚 /** * 如果在spring事务配置中不为切入点(如这里的切入点可以定义成test*) ...
- JAVA异常的捕获与抛出原则
在可能会出现exception的地方,要使用try-catch或者throws或者两者都要.我的判断依据是:如果对可能出现的exception不想被外部(方法的调用者)知道,就在方法内部try-cat ...
- 【开发技术】java异常的捕获与抛出原则
在可能会出现exception的地方,要使用try-catch或者throws或者两者都要.我的判断依据是:如果对可能出现的exception不想被外部(方法的调用者)知道,就在方法内部try-cat ...
随机推荐
- 关于ElasticSearch的堆内存设置与优化
1.什么是堆内存?Java 中的堆是 JVM 所管理的最大的一块内存空间,主要用于存放各种类的实例对象.在 Java 中,堆被划分成两个不同的区域:- 新生代 ( Young ).- 老年代 ( Ol ...
- .net core获取数据库连接 抛出The type initializer to throw an exception
原文:https://www.cnblogs.com/pudefu/p/7580722.html 在.NET Framework框架时代我们的应用配置内容一般都是写在Web.config或者App.c ...
- kafka使用SASL_PLAINTEXT做用户认证
使用SASL/PLAIN认证 server端1.配置brokerkafka_server_jaas.conf内容KafkaServer {org.apache.kafka.common.securit ...
- Angular复习笔记5-指令
Angular复习笔记5-指令 在Angular中,指令是一个重要的概念,它作用在特定的DOM元素上,可以扩展这个元素的功能,为元素增加新的行为.本质上,组件可以被理解为一种带有视图的指令.组件继承自 ...
- FORM表单 onclick()与onsubmit()
FORM表单中onclick().submit()与onsubmit()的问题 最近遇到一次处理form数据的过滤,采用了button的onclick事件来检查,发现return false后表单仍然 ...
- 记录一次git回滚代码
老大临时让更新一版代码到本地,熟练的git fetch/git merge 之后,出来了一批改动的文件,但是并不是我改动的. 我以为是版本迭代出来的其他同事改的,我就直接给add commit到我的版 ...
- Newtonsoft.Json.Linq.JObject 遍历验证每个属性内容
业务需求,拦截器验证每个请求inputstream(实际是application/json流)的数据,但是json反序列化实体格式不同. var req = filterContext.Request ...
- Xcode调试打印方法
1 NSLog 在调试的过程中,最常用的查看变量值的方法是NSLog 整数 int a = 1; NSLog("%d", a); 浮点数 float b = 1.11; NSLog ...
- Spring的核心机制:依赖注入
依赖注入的概念 当一个对象要调用另一个对象时,一般是new一个被调用的对象,示例: class A{ private B b=new B(); public void test(){ b.say ...
- 使用Prometheus监控Linux系统各项指标
首先在Linux系统上安装一个探测器node explorer, 下载地址https://prometheus.io/docs/guides/node-exporter/ 这个探测器会定期将linux ...