原文地址:https://monoinfinito.wordpress.com/2013/03/19/c-exceptions-under-the-hood-7-a-nice-personality/
在我们学习异常的旅程中,目前我们已经了解到如何完成抛出,称为“调用帧信息”的东西辅助称为Unwind的库执行栈回滚,编译器写入称为LSDA的、语言特定数据区,了解一个方法可以处理哪些异常。现在我们知道许多魔术在personality函数上完成;不过我们还未看过它的实际作用。让我们更详细地回顾一下异常如何抛出与捕捉(或者,更准确地,目前为止我们知道它是如何被抛出、捕捉):
- 编译器将我们的throw语句翻译为一对__cxa_allocate_exception/__cxa_throw
- __cxa_allocate_exception将在内存里创建异常
- __cxa_throw将初始化一堆东西,通过调用_Unwind_RaiseException把这个异常转发底层的unwind库
- Unwind将使用CFI来了解哪些函数在栈上(即知道如何开始栈回滚)
- 每个函数将有一个LSDA(语言特定数据区)部分,加上.gcc_except_table
- Unwind将以当前栈帧以及LSDA调用personality函数;这个函数将回复unwind这个栈是否能处理这个异常
了解到这,是时候实现我们自己的personality方法了。在抛出异常时,我们的ABI过去输出这:
|
|
让我们回到mycppabi并添加像这样的内容(链接到完整的mycppabi.cpp文件):
|
|
备注:你可以从我的github repo下载完整的源代码。
的确,在我们运行它时,我们将看到我们的personality函数被调用。我们知道我们在正确的道路上,现在我们已经知道我们希望什么样的personality函数;让我们开始使用对这个函数合适的定义:
|
|
如果我们把这放入我们的mycppabi.cpp文件,我们得到:
|
|
备注:你可以从我的github repo下载完整的源代码。
让我们编译及链接,然后运行它,在gdb的辅助下分析这个函数的每个参数:
Breakpoint 1, __gxx_personality_v0 (version=1, actions=1, exceptionClass=134514792, unwind_exception=0x804a060, context=0xbffff0f0)
- 版本以及exceptionClass与语言/ABI/编译器工具链/原生或非原生异常等相关。我们无需为我们的小ABI担心这些,我们将处理所有的异常。
- 行动:这是_Unwind_用来告诉personality函数它应该做什么的(后面详细论述)
- Unwind_exception:由__cxa_allocate_exception分配的异常(有点儿……进行了许多指针算术,不过该指针可用于访问我们最初的异常)
- Context:这保存了当前栈帧的所有信息,例如语言特定数据区(LSDA)。这是我们将用来检测栈是否可以处理抛出异常的(也用于检测我们是否需要运行任何析构函数)。
好了,一个可工作的(额,可链接的)personality函数。不过做不了什么,因此下一次我们将从添加某个真实行为开始,尝试使它处理一个异常。