1 |
|
|
/* ===-- gcc_personality_v0.c - Implement __gcc_personality_v0 -------------=== |
2 |
|
|
* |
3 |
|
|
* The LLVM Compiler Infrastructure |
4 |
|
|
* |
5 |
|
|
* This file is dual licensed under the MIT and the University of Illinois Open |
6 |
|
|
* Source Licenses. See LICENSE.TXT for details. |
7 |
|
|
* |
8 |
|
|
* ===----------------------------------------------------------------------=== |
9 |
|
|
* |
10 |
|
|
*/ |
11 |
|
|
|
12 |
|
|
#include "int_lib.h" |
13 |
|
|
|
14 |
|
|
#include <unwind.h> |
15 |
|
|
#if defined(__arm__) && !defined(__ARM_DWARF_EH__) && !defined(__USING_SJLJ_EXCEPTIONS__) |
16 |
|
|
/* |
17 |
|
|
* When building with older compilers (e.g. clang <3.9), it is possible that we |
18 |
|
|
* have a version of unwind.h which does not provide the EHABI declarations |
19 |
|
|
* which are quired for the C personality to conform to the specification. In |
20 |
|
|
* order to provide forward compatibility for such compilers, we re-declare the |
21 |
|
|
* necessary interfaces in the helper to permit a standalone compilation of the |
22 |
|
|
* builtins (which contains the C unwinding personality for historical reasons). |
23 |
|
|
*/ |
24 |
|
|
#include "unwind-ehabi-helpers.h" |
25 |
|
|
#endif |
26 |
|
|
|
27 |
|
|
/* |
28 |
|
|
* Pointer encodings documented at: |
29 |
|
|
* http://refspecs.freestandards.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html |
30 |
|
|
*/ |
31 |
|
|
|
32 |
|
|
#define DW_EH_PE_omit 0xff /* no data follows */ |
33 |
|
|
|
34 |
|
|
#define DW_EH_PE_absptr 0x00 |
35 |
|
|
#define DW_EH_PE_uleb128 0x01 |
36 |
|
|
#define DW_EH_PE_udata2 0x02 |
37 |
|
|
#define DW_EH_PE_udata4 0x03 |
38 |
|
|
#define DW_EH_PE_udata8 0x04 |
39 |
|
|
#define DW_EH_PE_sleb128 0x09 |
40 |
|
|
#define DW_EH_PE_sdata2 0x0A |
41 |
|
|
#define DW_EH_PE_sdata4 0x0B |
42 |
|
|
#define DW_EH_PE_sdata8 0x0C |
43 |
|
|
|
44 |
|
|
#define DW_EH_PE_pcrel 0x10 |
45 |
|
|
#define DW_EH_PE_textrel 0x20 |
46 |
|
|
#define DW_EH_PE_datarel 0x30 |
47 |
|
|
#define DW_EH_PE_funcrel 0x40 |
48 |
|
|
#define DW_EH_PE_aligned 0x50 |
49 |
|
|
#define DW_EH_PE_indirect 0x80 /* gcc extension */ |
50 |
|
|
|
51 |
|
|
|
52 |
|
|
|
53 |
|
|
/* read a uleb128 encoded value and advance pointer */ |
54 |
|
|
static uintptr_t readULEB128(const uint8_t** data) |
55 |
|
|
{ |
56 |
|
|
uintptr_t result = 0; |
57 |
|
|
uintptr_t shift = 0; |
58 |
|
|
unsigned char byte; |
59 |
|
|
const uint8_t* p = *data; |
60 |
|
|
do { |
61 |
|
|
byte = *p++; |
62 |
|
|
result |= (byte & 0x7f) << shift; |
63 |
|
|
shift += 7; |
64 |
|
|
} while (byte & 0x80); |
65 |
|
|
*data = p; |
66 |
|
|
return result; |
67 |
|
|
} |
68 |
|
|
|
69 |
|
|
/* read a pointer encoded value and advance pointer */ |
70 |
|
|
static uintptr_t readEncodedPointer(const uint8_t** data, uint8_t encoding) |
71 |
|
|
{ |
72 |
|
|
const uint8_t* p = *data; |
73 |
|
|
uintptr_t result = 0; |
74 |
|
|
|
75 |
|
|
if ( encoding == DW_EH_PE_omit ) |
76 |
|
|
return 0; |
77 |
|
|
|
78 |
|
|
/* first get value */ |
79 |
|
|
switch (encoding & 0x0F) { |
80 |
|
|
case DW_EH_PE_absptr: |
81 |
|
|
result = *((const uintptr_t*)p); |
82 |
|
|
p += sizeof(uintptr_t); |
83 |
|
|
break; |
84 |
|
|
case DW_EH_PE_uleb128: |
85 |
|
|
result = readULEB128(&p); |
86 |
|
|
break; |
87 |
|
|
case DW_EH_PE_udata2: |
88 |
|
|
result = *((const uint16_t*)p); |
89 |
|
|
p += sizeof(uint16_t); |
90 |
|
|
break; |
91 |
|
|
case DW_EH_PE_udata4: |
92 |
|
|
result = *((const uint32_t*)p); |
93 |
|
|
p += sizeof(uint32_t); |
94 |
|
|
break; |
95 |
|
|
case DW_EH_PE_udata8: |
96 |
|
|
result = *((const uint64_t*)p); |
97 |
|
|
p += sizeof(uint64_t); |
98 |
|
|
break; |
99 |
|
|
case DW_EH_PE_sdata2: |
100 |
|
|
result = *((const int16_t*)p); |
101 |
|
|
p += sizeof(int16_t); |
102 |
|
|
break; |
103 |
|
|
case DW_EH_PE_sdata4: |
104 |
|
|
result = *((const int32_t*)p); |
105 |
|
|
p += sizeof(int32_t); |
106 |
|
|
break; |
107 |
|
|
case DW_EH_PE_sdata8: |
108 |
|
|
result = *((const int64_t*)p); |
109 |
|
|
p += sizeof(int64_t); |
110 |
|
|
break; |
111 |
|
|
case DW_EH_PE_sleb128: |
112 |
|
|
default: |
113 |
|
|
/* not supported */ |
114 |
|
|
compilerrt_abort(); |
115 |
|
|
break; |
116 |
|
|
} |
117 |
|
|
|
118 |
|
|
/* then add relative offset */ |
119 |
|
|
switch ( encoding & 0x70 ) { |
120 |
|
|
case DW_EH_PE_absptr: |
121 |
|
|
/* do nothing */ |
122 |
|
|
break; |
123 |
|
|
case DW_EH_PE_pcrel: |
124 |
|
|
result += (uintptr_t)(*data); |
125 |
|
|
break; |
126 |
|
|
case DW_EH_PE_textrel: |
127 |
|
|
case DW_EH_PE_datarel: |
128 |
|
|
case DW_EH_PE_funcrel: |
129 |
|
|
case DW_EH_PE_aligned: |
130 |
|
|
default: |
131 |
|
|
/* not supported */ |
132 |
|
|
compilerrt_abort(); |
133 |
|
|
break; |
134 |
|
|
} |
135 |
|
|
|
136 |
|
|
/* then apply indirection */ |
137 |
|
|
if (encoding & DW_EH_PE_indirect) { |
138 |
|
|
result = *((const uintptr_t*)result); |
139 |
|
|
} |
140 |
|
|
|
141 |
|
|
*data = p; |
142 |
|
|
return result; |
143 |
|
|
} |
144 |
|
|
|
145 |
|
|
#if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && \ |
146 |
|
|
!defined(__ARM_DWARF_EH__) |
147 |
|
|
#define USING_ARM_EHABI 1 |
148 |
|
|
_Unwind_Reason_Code __gnu_unwind_frame(struct _Unwind_Exception *, |
149 |
|
|
struct _Unwind_Context *); |
150 |
|
|
#endif |
151 |
|
|
|
152 |
|
|
static inline _Unwind_Reason_Code |
153 |
|
|
continueUnwind(struct _Unwind_Exception *exceptionObject, |
154 |
|
|
struct _Unwind_Context *context) { |
155 |
|
|
#if USING_ARM_EHABI |
156 |
|
|
/* |
157 |
|
|
* On ARM EHABI the personality routine is responsible for actually |
158 |
|
|
* unwinding a single stack frame before returning (ARM EHABI Sec. 6.1). |
159 |
|
|
*/ |
160 |
|
|
if (__gnu_unwind_frame(exceptionObject, context) != _URC_OK) |
161 |
|
|
return _URC_FAILURE; |
162 |
|
|
#endif |
163 |
|
|
return _URC_CONTINUE_UNWIND; |
164 |
|
|
} |
165 |
|
|
|
166 |
|
|
/* |
167 |
|
|
* The C compiler makes references to __gcc_personality_v0 in |
168 |
|
|
* the dwarf unwind information for translation units that use |
169 |
|
|
* __attribute__((cleanup(xx))) on local variables. |
170 |
|
|
* This personality routine is called by the system unwinder |
171 |
|
|
* on each frame as the stack is unwound during a C++ exception |
172 |
|
|
* throw through a C function compiled with -fexceptions. |
173 |
|
|
*/ |
174 |
|
|
#if __USING_SJLJ_EXCEPTIONS__ |
175 |
|
|
/* the setjump-longjump based exceptions personality routine has a |
176 |
|
|
* different name */ |
177 |
|
|
COMPILER_RT_ABI _Unwind_Reason_Code |
178 |
|
|
__gcc_personality_sj0(int version, _Unwind_Action actions, |
179 |
|
|
uint64_t exceptionClass, struct _Unwind_Exception* exceptionObject, |
180 |
|
|
struct _Unwind_Context *context) |
181 |
|
|
#elif USING_ARM_EHABI |
182 |
|
|
/* The ARM EHABI personality routine has a different signature. */ |
183 |
|
|
COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0( |
184 |
|
|
_Unwind_State state, struct _Unwind_Exception *exceptionObject, |
185 |
|
|
struct _Unwind_Context *context) |
186 |
|
|
#else |
187 |
|
|
COMPILER_RT_ABI _Unwind_Reason_Code |
188 |
|
|
__gcc_personality_v0(int version, _Unwind_Action actions, |
189 |
|
|
uint64_t exceptionClass, struct _Unwind_Exception* exceptionObject, |
190 |
|
|
struct _Unwind_Context *context) |
191 |
|
|
#endif |
192 |
|
|
{ |
193 |
|
|
/* Since C does not have catch clauses, there is nothing to do during */ |
194 |
|
|
/* phase 1 (the search phase). */ |
195 |
|
|
#if USING_ARM_EHABI |
196 |
|
|
/* After resuming from a cleanup we should also continue on to the next |
197 |
|
|
* frame straight away. */ |
198 |
|
|
if ((state & _US_ACTION_MASK) != _US_UNWIND_FRAME_STARTING) |
199 |
|
|
#else |
200 |
|
|
if ( actions & _UA_SEARCH_PHASE ) |
201 |
|
|
#endif |
202 |
|
|
return continueUnwind(exceptionObject, context); |
203 |
|
|
|
204 |
|
|
/* There is nothing to do if there is no LSDA for this frame. */ |
205 |
|
|
const uint8_t* lsda = (uint8_t*)_Unwind_GetLanguageSpecificData(context); |
206 |
|
|
if ( lsda == (uint8_t*) 0 ) |
207 |
|
|
return continueUnwind(exceptionObject, context); |
208 |
|
|
|
209 |
|
|
uintptr_t pc = _Unwind_GetIP(context)-1; |
210 |
|
|
uintptr_t funcStart = _Unwind_GetRegionStart(context); |
211 |
|
|
uintptr_t pcOffset = pc - funcStart; |
212 |
|
|
|
213 |
|
|
/* Parse LSDA header. */ |
214 |
|
|
uint8_t lpStartEncoding = *lsda++; |
215 |
|
|
if (lpStartEncoding != DW_EH_PE_omit) { |
216 |
|
|
readEncodedPointer(&lsda, lpStartEncoding); |
217 |
|
|
} |
218 |
|
|
uint8_t ttypeEncoding = *lsda++; |
219 |
|
|
if (ttypeEncoding != DW_EH_PE_omit) { |
220 |
|
|
readULEB128(&lsda); |
221 |
|
|
} |
222 |
|
|
/* Walk call-site table looking for range that includes current PC. */ |
223 |
|
|
uint8_t callSiteEncoding = *lsda++; |
224 |
|
|
uint32_t callSiteTableLength = readULEB128(&lsda); |
225 |
|
|
const uint8_t* callSiteTableStart = lsda; |
226 |
|
|
const uint8_t* callSiteTableEnd = callSiteTableStart + callSiteTableLength; |
227 |
|
|
const uint8_t* p=callSiteTableStart; |
228 |
|
|
while (p < callSiteTableEnd) { |
229 |
|
|
uintptr_t start = readEncodedPointer(&p, callSiteEncoding); |
230 |
|
|
uintptr_t length = readEncodedPointer(&p, callSiteEncoding); |
231 |
|
|
uintptr_t landingPad = readEncodedPointer(&p, callSiteEncoding); |
232 |
|
|
readULEB128(&p); /* action value not used for C code */ |
233 |
|
|
if ( landingPad == 0 ) |
234 |
|
|
continue; /* no landing pad for this entry */ |
235 |
|
|
if ( (start <= pcOffset) && (pcOffset < (start+length)) ) { |
236 |
|
|
/* Found landing pad for the PC. |
237 |
|
|
* Set Instruction Pointer to so we re-enter function |
238 |
|
|
* at landing pad. The landing pad is created by the compiler |
239 |
|
|
* to take two parameters in registers. |
240 |
|
|
*/ |
241 |
|
|
_Unwind_SetGR(context, __builtin_eh_return_data_regno(0), |
242 |
|
|
(uintptr_t)exceptionObject); |
243 |
|
|
_Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0); |
244 |
|
|
_Unwind_SetIP(context, (funcStart + landingPad)); |
245 |
|
|
return _URC_INSTALL_CONTEXT; |
246 |
|
|
} |
247 |
|
|
} |
248 |
|
|
|
249 |
|
|
/* No landing pad found, continue unwinding. */ |
250 |
|
|
return continueUnwind(exceptionObject, context); |
251 |
|
|
} |
252 |
|
|
|