blob: b0d93b8a110c32a17596e1d76110a42327acf362 [file] [log] [blame]
Edward O'Callaghan4856eef2009-08-05 04:02:56 +00001/* ===-- 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 */
Daniel Dunbarfd089992009-06-26 16:47:03 +000011
12#include <stdint.h>
13#include <stdio.h>
14#include <stdlib.h>
15
Edward O'Callaghan4856eef2009-08-05 04:02:56 +000016/*
17 * _Unwind_* stuff based on C++ ABI public documentation
18 * http://refspecs.freestandards.org/abi-eh-1.21.html
19 */
20
Daniel Dunbarfd089992009-06-26 16:47:03 +000021typedef 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;
Nick Kledzikac02b5a2010-01-20 06:13:20 +000045 void (*exception_cleanup)(_Unwind_Reason_Code reason,
46 struct _Unwind_Exception* exc);
Daniel Dunbarfd089992009-06-26 16:47:03 +000047 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
Edward O'Callaghan4856eef2009-08-05 04:02:56 +000058/*
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 */
Daniel Dunbarfd089992009-06-26 16:47:03 +000064
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
Edward O'Callaghan4856eef2009-08-05 04:02:56 +000080#define DW_EH_PE_indirect 0x80 /* gcc extension */
Daniel Dunbarfd089992009-06-26 16:47:03 +000081
82
83
Edward O'Callaghan4856eef2009-08-05 04:02:56 +000084/* read a uleb128 encoded value and advance pointer */
Daniel Dunbarfd089992009-06-26 16:47:03 +000085static 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
Edward O'Callaghan4856eef2009-08-05 04:02:56 +0000100/* read a pointer encoded value and advance pointer */
Daniel Dunbarfd089992009-06-26 16:47:03 +0000101static 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
Edward O'Callaghan4856eef2009-08-05 04:02:56 +0000109 /* first get value */
Daniel Dunbarfd089992009-06-26 16:47:03 +0000110 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:
Edward O'Callaghan4856eef2009-08-05 04:02:56 +0000144 /* not supported */
Daniel Dunbarfd089992009-06-26 16:47:03 +0000145 abort();
146 break;
147 }
148
Edward O'Callaghan4856eef2009-08-05 04:02:56 +0000149 /* then add relative offset */
Daniel Dunbarfd089992009-06-26 16:47:03 +0000150 switch ( encoding & 0x70 ) {
151 case DW_EH_PE_absptr:
Edward O'Callaghan4856eef2009-08-05 04:02:56 +0000152 /* do nothing */
Daniel Dunbarfd089992009-06-26 16:47:03 +0000153 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:
Edward O'Callaghan4856eef2009-08-05 04:02:56 +0000162 /* not supported */
Daniel Dunbarfd089992009-06-26 16:47:03 +0000163 abort();
164 break;
165 }
166
Edward O'Callaghan4856eef2009-08-05 04:02:56 +0000167 /* then apply indirection */
Daniel Dunbarfd089992009-06-26 16:47:03 +0000168 if (encoding & DW_EH_PE_indirect) {
169 result = *((uintptr_t*)result);
170 }
171
172 *data = p;
173 return result;
174}
175
176
Edward O'Callaghan4856eef2009-08-05 04:02:56 +0000177/*
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
Daniel Dunbarfd089992009-06-26 16:47:03 +0000186_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{
Edward O'Callaghan4856eef2009-08-05 04:02:56 +0000190 /* Since C does not have catch clauses, there is nothing to do during */
191 /* phase 1 (the search phase). */
Daniel Dunbarfd089992009-06-26 16:47:03 +0000192 if ( actions & _UA_SEARCH_PHASE )
193 return _URC_CONTINUE_UNWIND;
194
Edward O'Callaghan4856eef2009-08-05 04:02:56 +0000195 /* There is nothing to do if there is no LSDA for this frame. */
Daniel Dunbarfd089992009-06-26 16:47:03 +0000196 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
Edward O'Callaghan4856eef2009-08-05 04:02:56 +0000204 /* Parse LSDA header. */
Daniel Dunbarfd089992009-06-26 16:47:03 +0000205 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 }
Edward O'Callaghan4856eef2009-08-05 04:02:56 +0000213 /* Walk call-site table looking for range that includes current PC. */
Daniel Dunbarfd089992009-06-26 16:47:03 +0000214 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);
Edward O'Callaghan4856eef2009-08-05 04:02:56 +0000223 readULEB128(&p); /* action value not used for C code */
Daniel Dunbarfd089992009-06-26 16:47:03 +0000224 if ( landingPad == 0 )
Edward O'Callaghan4856eef2009-08-05 04:02:56 +0000225 continue; /* no landing pad for this entry */
Daniel Dunbarfd089992009-06-26 16:47:03 +0000226 if ( (start <= pcOffset) && (pcOffset < (start+length)) ) {
Edward O'Callaghan4856eef2009-08-05 04:02:56 +0000227 /* 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 */
Daniel Dunbarfd089992009-06-26 16:47:03 +0000232 _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
Edward O'Callaghan4856eef2009-08-05 04:02:56 +0000240 /* No landing pad found, continue unwinding. */
Daniel Dunbarfd089992009-06-26 16:47:03 +0000241 return _URC_CONTINUE_UNWIND;
242}
243