1/* ===-- gcc_personality_v0.c - Implement __gcc_personality_v0 -------------===
2 *
3 *      	       The LLVM Compiler Infrastructure
4 *
5 * This file is distributed under the University of Illinois Open Source
6 * License. See LICENSE.TXT for details.
7 *
8 * ===----------------------------------------------------------------------===
9 *
10 */
11
12#include <stdint.h>
13#include <stdio.h>
14#include <stdlib.h>
15
16/*
17 * _Unwind_* stuff based on C++ ABI public documentation
18 * http://refspecs.freestandards.org/abi-eh-1.21.html
19 */
20
21typedef enum {
22    _URC_NO_REASON = 0,
23    _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
24    _URC_FATAL_PHASE2_ERROR = 2,
25    _URC_FATAL_PHASE1_ERROR = 3,
26    _URC_NORMAL_STOP = 4,
27    _URC_END_OF_STACK = 5,
28    _URC_HANDLER_FOUND = 6,
29    _URC_INSTALL_CONTEXT = 7,
30    _URC_CONTINUE_UNWIND = 8
31} _Unwind_Reason_Code;
32
33typedef enum {
34    _UA_SEARCH_PHASE = 1,
35    _UA_CLEANUP_PHASE = 2,
36    _UA_HANDLER_FRAME = 4,
37    _UA_FORCE_UNWIND = 8,
38    _UA_END_OF_STACK = 16
39} _Unwind_Action;
40
41typedef struct _Unwind_Context* _Unwind_Context_t;
42
43struct _Unwind_Exception {
44    uint64_t                exception_class;
45    void                    (*exception_cleanup)(_Unwind_Reason_Code reason,
46                                                 struct _Unwind_Exception* exc);
47    uintptr_t                private_1;
48    uintptr_t                private_2;
49};
50
51extern const uint8_t*    _Unwind_GetLanguageSpecificData(_Unwind_Context_t c);
52extern void              _Unwind_SetGR(_Unwind_Context_t c, int i, uintptr_t n);
53extern void              _Unwind_SetIP(_Unwind_Context_t, uintptr_t new_value);
54extern uintptr_t         _Unwind_GetIP(_Unwind_Context_t context);
55extern uintptr_t         _Unwind_GetRegionStart(_Unwind_Context_t context);
56
57
58/*
59 * Pointer encodings documented at:
60 *   http://refspecs.freestandards.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html
61 */
62
63#define DW_EH_PE_omit      0xff  /* no data follows */
64
65#define DW_EH_PE_absptr    0x00
66#define DW_EH_PE_uleb128   0x01
67#define DW_EH_PE_udata2    0x02
68#define DW_EH_PE_udata4    0x03
69#define DW_EH_PE_udata8    0x04
70#define DW_EH_PE_sleb128   0x09
71#define DW_EH_PE_sdata2    0x0A
72#define DW_EH_PE_sdata4    0x0B
73#define DW_EH_PE_sdata8    0x0C
74
75#define DW_EH_PE_pcrel     0x10
76#define DW_EH_PE_textrel   0x20
77#define DW_EH_PE_datarel   0x30
78#define DW_EH_PE_funcrel   0x40
79#define DW_EH_PE_aligned   0x50
80#define DW_EH_PE_indirect  0x80 /* gcc extension */
81
82
83
84/* read a uleb128 encoded value and advance pointer */
85static uintptr_t readULEB128(const uint8_t** data)
86{
87    uintptr_t result = 0;
88    uintptr_t shift = 0;
89    unsigned char byte;
90    const uint8_t* p = *data;
91    do {
92        byte = *p++;
93        result |= (byte & 0x7f) << shift;
94        shift += 7;
95    } while (byte & 0x80);
96    *data = p;
97    return result;
98}
99
100/* read a pointer encoded value and advance pointer */
101static uintptr_t readEncodedPointer(const uint8_t** data, uint8_t encoding)
102{
103    const uint8_t* p = *data;
104    uintptr_t result = 0;
105
106    if ( encoding == DW_EH_PE_omit )
107        return 0;
108
109    /* first get value */
110    switch (encoding & 0x0F) {
111        case DW_EH_PE_absptr:
112            result = *((uintptr_t*)p);
113            p += sizeof(uintptr_t);
114            break;
115        case DW_EH_PE_uleb128:
116            result = readULEB128(&p);
117            break;
118        case DW_EH_PE_udata2:
119            result = *((uint16_t*)p);
120            p += sizeof(uint16_t);
121            break;
122        case DW_EH_PE_udata4:
123            result = *((uint32_t*)p);
124            p += sizeof(uint32_t);
125            break;
126        case DW_EH_PE_udata8:
127            result = *((uint64_t*)p);
128            p += sizeof(uint64_t);
129            break;
130        case DW_EH_PE_sdata2:
131            result = *((int16_t*)p);
132            p += sizeof(int16_t);
133            break;
134        case DW_EH_PE_sdata4:
135            result = *((int32_t*)p);
136            p += sizeof(int32_t);
137            break;
138        case DW_EH_PE_sdata8:
139            result = *((int64_t*)p);
140            p += sizeof(int64_t);
141            break;
142        case DW_EH_PE_sleb128:
143        default:
144            /* not supported */
145            abort();
146            break;
147    }
148
149    /* then add relative offset */
150    switch ( encoding & 0x70 ) {
151        case DW_EH_PE_absptr:
152            /* do nothing */
153            break;
154        case DW_EH_PE_pcrel:
155            result += (uintptr_t)(*data);
156            break;
157        case DW_EH_PE_textrel:
158        case DW_EH_PE_datarel:
159        case DW_EH_PE_funcrel:
160        case DW_EH_PE_aligned:
161        default:
162            /* not supported */
163            abort();
164            break;
165    }
166
167    /* then apply indirection */
168    if (encoding & DW_EH_PE_indirect) {
169        result = *((uintptr_t*)result);
170    }
171
172    *data = p;
173    return result;
174}
175
176
177/*
178 * The C compiler makes references to __gcc_personality_v0 in
179 * the dwarf unwind information for translation units that use
180 * __attribute__((cleanup(xx))) on local variables.
181 * This personality routine is called by the system unwinder
182 * on each frame as the stack is unwound during a C++ exception
183 * throw through a C function compiled with -fexceptions.
184 */
185
186_Unwind_Reason_Code __gcc_personality_v0(int version, _Unwind_Action actions,
187         uint64_t exceptionClass, struct _Unwind_Exception* exceptionObject,
188         _Unwind_Context_t context)
189{
190    /* Since C does not have catch clauses, there is nothing to do during */
191    /* phase 1 (the search phase). */
192    if ( actions & _UA_SEARCH_PHASE )
193        return _URC_CONTINUE_UNWIND;
194
195    /* There is nothing to do if there is no LSDA for this frame. */
196    const uint8_t* lsda = _Unwind_GetLanguageSpecificData(context);
197    if ( lsda == NULL )
198        return _URC_CONTINUE_UNWIND;
199
200    uintptr_t pc = _Unwind_GetIP(context)-1;
201    uintptr_t funcStart = _Unwind_GetRegionStart(context);
202    uintptr_t pcOffset = pc - funcStart;
203
204    /* Parse LSDA header. */
205    uint8_t lpStartEncoding = *lsda++;
206    if (lpStartEncoding != DW_EH_PE_omit) {
207        readEncodedPointer(&lsda, lpStartEncoding);
208    }
209    uint8_t ttypeEncoding = *lsda++;
210    if (ttypeEncoding != DW_EH_PE_omit) {
211        readULEB128(&lsda);
212    }
213    /* Walk call-site table looking for range that includes current PC. */
214    uint8_t         callSiteEncoding = *lsda++;
215    uint32_t        callSiteTableLength = readULEB128(&lsda);
216    const uint8_t*  callSiteTableStart = lsda;
217    const uint8_t*  callSiteTableEnd = callSiteTableStart + callSiteTableLength;
218    const uint8_t* p=callSiteTableStart;
219    while (p < callSiteTableEnd) {
220        uintptr_t start = readEncodedPointer(&p, callSiteEncoding);
221        uintptr_t length = readEncodedPointer(&p, callSiteEncoding);
222        uintptr_t landingPad = readEncodedPointer(&p, callSiteEncoding);
223        readULEB128(&p); /* action value not used for C code */
224        if ( landingPad == 0 )
225            continue; /* no landing pad for this entry */
226        if ( (start <= pcOffset) && (pcOffset < (start+length)) ) {
227            /* Found landing pad for the PC.
228             * Set Instruction Pointer to so we re-enter function
229             * at landing pad. The landing pad is created by the compiler
230             * to take two parameters in registers.
231	     */
232            _Unwind_SetGR(context, __builtin_eh_return_data_regno(0),
233                                                (uintptr_t)exceptionObject);
234            _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0);
235            _Unwind_SetIP(context, funcStart+landingPad);
236            return _URC_INSTALL_CONTEXT;
237        }
238    }
239
240    /* No landing pad found, continue unwinding. */
241    return _URC_CONTINUE_UNWIND;
242}
243
244