C++异常的幕后7:好的personality

原文地址:https://monoinfinito.wordpress.com/2013/03/19/c-exceptions-under-the-hood-7-a-nice-personality/

作者:nicolasbrailo 

在我们学习异常的旅程中,目前我们已经了解到如何完成抛出,称为“调用帧信息”的东西辅助称为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过去输出这:

1

2

3

alloc ex 1

__cxa_throw called

no one handled __cxa_throw, terminate!

让我们回到mycppabi并添加像这样的内容(链接到完整的mycppabi.cpp文件):

1

2

3

4

void __gxx_personality_v0()

{

    printf("Personality function FTW\n");

}

备注:你可以从我的github repo下载完整的源代码。

的确,在我们运行它时,我们将看到我们的personality函数被调用。我们知道我们在正确的道路上,现在我们已经知道我们希望什么样的personality函数;让我们开始使用对这个函数合适的定义:

1

2

3

_Unwind_Reason_Code __gxx_personality_v0 (

                     int version, _Unwind_Action actions, uint64_t exceptionClass,

                     _Unwind_Exception* unwind_exception, _Unwind_Context* context);

如果我们把这放入我们的mycppabi.cpp文件,我们得到:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

#include <stdint.h>

 

namespace __cxxabiv1 {

    struct __class_type_info {

        virtual void foo() {}

    } ti;

}

 

#define EXCEPTION_BUFF_SIZE 255

char exception_buff[EXCEPTION_BUFF_SIZE];

 

extern "C" {

 

void* __cxa_allocate_exception(size_t thrown_size)

{

    printf("alloc ex %i\n", thrown_size);

    if (thrown_size > EXCEPTION_BUFF_SIZE) printf("Exception too big");

    return &exception_buff;

}

 

void __cxa_free_exception(void *thrown_exception);

 

 

#include <unwind.h>

 

typedef void (*unexpected_handler)(void);

typedef void (*terminate_handler)(void);

 

struct __cxa_exception {

    std::type_info *    exceptionType;

    void (*exceptionDestructor) (void *);

    unexpected_handler  unexpectedHandler;

    terminate_handler   terminateHandler;

    __cxa_exception *   nextException;

 

    int         handlerCount;

    int         handlerSwitchValue;

    const char *        actionRecord;

    const char *        languageSpecificData;

    void *          catchTemp;

    void *          adjustedPtr;

 

    _Unwind_Exception   unwindHeader;

};

 

void __cxa_throw(void* thrown_exception, struct type_info *tinfo, void (*dest)(void*))

{

    printf("__cxa_throw called\n");

 

    __cxa_exception *header = ((__cxa_exception *) thrown_exception - 1);

    _Unwind_RaiseException(&header->unwindHeader);

 

    // __cxa_throw never returns

    printf("no one handled __cxa_throw, terminate!\n");

    exit(0);

}

 

 

void __cxa_begin_catch()

{

    printf("begin FTW\n");

}

 

void __cxa_end_catch()

{

    printf("end FTW\n");

}

 

_Unwind_Reason_Code __gxx_personality_v0 (

                     int version, _Unwind_Action actions, uint64_t exceptionClass,

                     _Unwind_Exception* unwind_exception, _Unwind_Context* context)

{

    printf("Personality function FTW!\n");

}

 

 

}

备注:你可以从我的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函数。不过做不了什么,因此下一次我们将从添加某个真实行为开始,尝试使它处理一个异常。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值