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