C++异常的幕后13:为着陆垫设置上下文

原文地址:https://monoinfinito.wordpress.com/2013/04/25/c-exceptions-under-the-hood-13-setting-the-context-for-a-landing-pad/

作者:nicolasbrailo

上次我们终于写出了几乎能工作的personality函数。我们可以检测每个有着陆垫可用的栈帧,然后告诉_Unwind_我们希望运行一个特定的着陆垫。我们遇到了一个小问题:虽然我们对_Unwind_设置了上下文以继续在正确的着陆垫上执行,我们没有在寄存器上设置正确的异常。结果,这意味着着陆垫将不知道应该处理哪个异常,因此它将说“我不能处理这个”。_Unwind_将说“请尝试下一个着陆垫”,但我们的ABI是如此简单,它不知道怎么找到下一个,只是尝试同一个。一次又一次。我们可能已经发明了一段时间以来最做作的例子(真的)!

让我们为着陆垫设置正确的上下文,并稍微整理一下我们的ABI:

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

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

#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");

}

 

 

 

 

/***********************************************************************/

 

/**

 * The LSDA is a read only place in memory; we'll create a typedef for

 * this to avoid a const mess later on; LSDA_ptr refers to readonly and

 * &LSDA_ptr will be a non-const pointer to a const place in memory

 */

typedef const uint8_t* LSDA_ptr;

 

struct LSDA_Header {

    /**

     * Read the LSDA table into a struct; advances the lsda pointer

     * as many bytes as read

     */

    LSDA_Header(LSDA_ptr *lsda) {

        LSDA_ptr read_ptr = *lsda;

 

        // Copy the LSDA fields

        start_encoding = read_ptr[0];

        type_encoding = read_ptr[1];

        ttype = read_ptr[2];

 

        // Advance the lsda pointer

        *lsda = read_ptr + sizeof(LSDA_Header);

    }

 

    uint8_t start_encoding;

    uint8_t type_encoding;

    uint8_t ttype;

};

 

struct LSDA_CS_Header {

    // Same as other LSDA constructors

    LSDA_CS_Header(LSDA_ptr *lsda) {

        LSDA_ptr read_ptr = *lsda;

        encoding = read_ptr[0];

        length = read_ptr[1];

        *lsda = read_ptr + sizeof(LSDA_CS_Header);

    }

 

    uint8_t encoding;

    uint8_t length;

};

 

struct LSDA_CS {

    // Same as other LSDA constructors

    LSDA_CS(LSDA_ptr *lsda) {

        LSDA_ptr read_ptr = *lsda;

        start = read_ptr[0];

        len = read_ptr[1];

        lp = read_ptr[2];

        action = read_ptr[3];

        *lsda = read_ptr + sizeof(LSDA_CS);

    }

 

    // Note start, len and lp would be void*'s, but they are actually relative

    // addresses: start and lp are relative to the start of the function, len

    // is relative to start

  

    // Offset into function from which we could handle a throw

    uint8_t start;

    // Length of the block that might throw

    uint8_t len;

    // Landing pad

    uint8_t lp;

    // Offset into action table + 1 (0 means no action)

    // Used to run destructors

    uint8_t action;

};

 

/*********************************************************************/

 

 

_Unwind_Reason_Code __gxx_personality_v0 (

                             int version,

                             _Unwind_Action actions,

                             uint64_t exceptionClass,

                             _Unwind_Exception* unwind_exception,

                             _Unwind_Context* context)

{

    if (actions & _UA_SEARCH_PHASE)

    {

        printf("Personality function, lookup phase\n");

        return _URC_HANDLER_FOUND;

    } else if (actions & _UA_CLEANUP_PHASE) {

        printf("Personality function, cleanup\n");

 

        // Pointer to the beginning of the raw LSDA

        LSDA_ptr lsda = (uint8_t*)_Unwind_GetLanguageSpecificData(context);

 

        // Read LSDA headerfor the LSDA

        LSDA_Header header(&lsda);

 

        // Read the LSDA CS header

        LSDA_CS_Header cs_header(&lsda);

 

        // Calculate where the end of the LSDA CS table is

        const LSDA_ptr lsda_cs_table_end = lsda + cs_header.length;

 

        // Loop through each entry in the CS table

        while (lsda < lsda_cs_table_end)

        {

            LSDA_CS cs(&lsda);

 

            if (cs.lp)

            {

                int r0 = __builtin_eh_return_data_regno(0);

                int r1 = __builtin_eh_return_data_regno(1);

 

                _Unwind_SetGR(context, r0, (uintptr_t)(unwind_exception));

                // Note the following code hardcodes the exception type;

                // we'll fix that later on

                _Unwind_SetGR(context, r1, (uintptr_t)(1));

 

                uintptr_t func_start = _Unwind_GetRegionStart(context);

                _Unwind_SetIP(context, func_start + cs.lp);

                break;

            }

        }

 

        return _URC_INSTALL_CONTEXT;

    } else {

        printf("Personality function, error\n");

        return _URC_FATAL_PHASE1_ERROR;

    }

}

 

 

}

备注:LSDA更细致的模式戳这里,完整代码在我的github repo

最后,它工作了。如果运行它你应该看到像这样的东西:

1

2

3

4

5

6

7

8

9

10

./app

alloc ex 1

__cxa_throw called

Personality function, lookup phase

Personality function, cleanup

begin FTW

Caught a Fake_Exception!

end FTW

try_but_dont_catch handled the exception

catchit handled the exception

当然我们有点依赖_Unwind_:这里我们说我们将处理每个异常,不管是什么。这把我们的catch(Exception&)转换为catch(…),如果调用帧里的第一个函数没有catch语句,就要天下大乱了。不过,仍然,对一个非常简单的ABI,我们到达了第一个里程碑。

现在我们能改进它,使得它在正确的帧上处理正确的异常吗?也许下次。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值