exception handling based on dwarf2, part III
[quarnos.git] / manes / exception.cpp
blobad40e219e060edd9656080993c0715315317ee02
1 /* Exception Support
2 * Based on DWARF2
3 */
5 /* Copyright (C) 2009 Pawel Dziepak */
7 #include "typeinfo"
8 #include "libs/setjmp.h"
9 #include "libs/list.h"
10 #include "manes/error.h"
12 typedef char uint64[8];
14 typedef enum {
15 _URC_NO_REASON = 0,
16 _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
17 _URC_FATAL_PHASE2_ERROR = 2,
18 _URC_FATAL_PHASE1_ERROR = 3,
19 _URC_NORMAL_STOP = 4,
20 _URC_END_OF_STACK = 5,
21 _URC_HANDLER_FOUND = 6,
22 _URC_INSTALL_CONTEXT = 7,
23 _URC_CONTINUE_UNWIND = 8
24 } _Unwind_Reason_Code;
27 struct _Unwind_Context {
28 unsigned int eip;
29 unsigned int ebp;
30 unsigned int lsda;
31 unsigned int proc;
35 typedef void (*_Unwind_Exception_Cleanup_Fn)(_Unwind_Reason_Code reason, struct _Unwind_Exception *exc);
37 struct _Unwind_Exception {
38 uint64 exception_class;
39 _Unwind_Exception_Cleanup_Fn exception_cleanup;
40 uint64 private_1;
41 uint64 private_2;
45 /* Personality Routine Actions */
46 typedef int _Unwind_Action;
47 static const _Unwind_Action _UA_SEARCH_PHASE = 1;
48 static const _Unwind_Action _UA_CLEANUP_PHASE = 2;
49 static const _Unwind_Action _UA_HANDLER_FRAME = 4;
50 static const _Unwind_Action _UA_FORCE_UNWIND = 8;
53 typedef _Unwind_Reason_Code (*personality_fn)(int version, _Unwind_Action actions, uint64 exception_class,_Unwind_Exception *ue_header, _Unwind_Context *context);
54 /* Based on DWARF3 Specification */
56 /* Read unsigned little endain base 128 value */
57 unsigned int read_uleb128(char *data, int &index) {
58 unsigned int value = 0;
59 unsigned char shift = 0;
60 do {
61 value |= ((data[index] & 0x7f) << shift);
62 shift += 7;
63 index++;
64 } while(data[index - 1] & 0x80);
66 return value;
69 /* Read signed little endain base 128 value */
70 signed int read_sleb128(char *data, int &index) {
71 signed int value = 0;
72 unsigned char shift = 0;
73 do {
74 value |= ((data[index] & 0x7f) << shift);
75 shift += 7;
76 index++;
77 } while(data[index - 1] & 0x80);
79 unsigned int minus = 0xffffffff & ~((1 << (shift - 1)) - 1);
80 if (value & (1 << (shift - 1)))
81 value |= minus;
82 return value;
85 struct fde {
86 unsigned int length;
87 fde *cie_pointer;
88 unsigned int initial_location;
89 unsigned int address_range;
91 struct return_fde {
92 fde *found_fde;
93 unsigned int personality;
94 unsigned int lsda;
97 unsigned int get_personality(unsigned int cie) {
98 int begin = cie;
99 cie += sizeof(unsigned int) * 2; // length and cie_id
100 for (; *(char*)cie; cie++); // augmentation string
101 int i = 1;
102 read_uleb128((char*)cie, i); // code align
103 read_uleb128((char*)cie, i); // data align
104 unsigned int ret_index = read_uleb128((char*)cie, i); // return address index
105 cie += i;
106 cie++; // augmentation size
108 if (! *(char*)cie) {
109 cie++;
110 return *(unsigned int*)cie;
111 } else
112 return 0;
115 unsigned int get_lsda(fde *current) {
116 unsigned int pointer = (unsigned int)current;
117 pointer += sizeof(fde);
118 if (*(char*)pointer == 0 || *(char*)pointer == 4) {
119 pointer++;
120 return *(unsigned int*)pointer;
121 } else
122 return 0;
125 return_fde get_fde(unsigned int eip) {
126 extern void *_eh_frame;
127 extern void *_eh_frame_end;
129 fde *current = (fde*)&_eh_frame;
130 unsigned int last_cie = (unsigned int)current;
132 while ((unsigned int)current < (unsigned int)&_eh_frame_end) {
133 /* do not want CIE */
134 if (current->cie_pointer != (fde*)0) {
136 if (eip >= current->initial_location
137 && eip <= current->initial_location + current->address_range) {
138 return_fde ret;
139 ret.found_fde = current;
140 ret.personality = get_personality(last_cie);
141 ret.lsda = get_lsda(current);
143 return ret;
145 } else {
147 last_cie = (unsigned int)current;
150 current = (fde*)((unsigned int)current + current->length + sizeof(unsigned int));
153 // return 0;
156 /* Based on IA-64 ABI Specification */
159 struct __cxa_exception {
160 std::type_info *exceptionType;
161 void (*exceptionDestructor) (void *);
162 /* unexpected_handler unexpectedHandler;
163 terminate_handler terminateHandler;*/
164 __cxa_exception * nextException;
166 int handlerCount;
167 int handlerSwitchValue;
168 const char *actionRecord;
169 const char *languageSpecificData;
170 void *catchTemp;
171 void *adjustedPtr;
173 _Unwind_Exception unwindHeader;
176 struct __cxa_eh_globals {
177 __cxa_exception *caughtExceptions;
178 unsigned int uncaughtExceptions;
181 __cxa_eh_globals eh_globals;
183 __cxa_eh_globals *__cxa_get_globals(void) {
184 return &eh_globals;
187 extern "C" void _Unwind_Resume(_Unwind_Exception ex) {
190 struct frame_pointer {
191 frame_pointer *next;
192 unsigned int eip;
195 unsigned int _Unwind_GetIP(struct _Unwind_Context *context) {
196 return context->eip;
199 unsigned int _Unwind_GetCFA(struct _Unwind_Context *context) {
200 return context->ebp;
203 unsigned int _Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) {
204 return context->lsda;
207 unsigned int _Unwind_GetRegionStart(struct _Unwind_Context *context) {
208 return context->proc;
211 void *get_eh_frame();
213 extern "C" _Unwind_Reason_Code _Unwind_RaiseException(_Unwind_Exception *ex) {
215 /* Phase 1
216 * Ask personality at each eip if there is an exception handler.
218 get_eh_frame();
219 frame_pointer *current_fp;
220 personality_fn personality;
221 _Unwind_Context context;
222 __asm__("movl %%ebp, %%eax" : "=a" (current_fp));
223 current_fp = current_fp->next;
224 do {
225 if (!current_fp)
226 return _URC_END_OF_STACK;
228 unsigned int return_point = current_fp->eip;
230 return_fde ret = get_fde(return_point);
231 context.eip = return_point;
232 context.ebp = (unsigned int)current_fp->next;
233 context.proc = ret.found_fde->initial_location;
234 context.lsda = ret.lsda;
236 union {
237 unsigned int val;
238 personality_fn personality;
239 } cast;
240 /* UD and what? All this code is implementation-specific */
241 cast.val = ret.personality;
242 personality = cast.personality;
244 current_fp = current_fp->next;
246 } while(personality(1, _UA_SEARCH_PHASE, ex->exception_class, ex, &context) != _URC_HANDLER_FOUND);
248 /* Phase 2
249 * Ask personality to perform cleanup at each eip and eventually jump to the handler.
252 _Unwind_Context handler = context;
253 __asm__("movl %%ebp, %%eax" : "=a" (current_fp));
254 do {
255 unsigned int return_point = current_fp->eip;
257 return_fde ret = get_fde(return_point);
258 context.eip = return_point;
259 context.ebp = (unsigned int)current_fp->next;
260 context.proc = ret.found_fde->initial_location;
261 context.lsda = ret.lsda;
263 union {
264 unsigned int val;
265 personality_fn personality;
266 } cast;
267 /* UD and what? All this code is implementation-specific */
268 cast.val = ret.personality;
269 personality = cast.personality;
271 context.eip = return_point;
272 context.ebp = (unsigned int)current_fp->next;
274 current_fp = current_fp->next;
275 } while (context.ebp != handler.ebp && personality(1, _UA_CLEANUP_PHASE, ex->exception_class, ex, &context) != _URC_FATAL_PHASE2_ERROR);
277 /* Call handler */
278 personality(1, _UA_HANDLER_FRAME, ex->exception_class, ex, &handler);
283 struct lsda_header_info
285 _Unwind_Ptr Start;
286 _Unwind_Ptr LPStart;
287 const unsigned char *TType;
288 const unsigned char *action_table;
289 unsigned char ttype_encoding;
290 unsigned char call_site_encoding;
294 struct lsda_header {
295 unsigned int lpstart;
296 char *ttype;
297 unsigned int call_site_encoding;
298 char *action_table;
299 char *call_site;
302 #define DW_EH_PE_omit 0xff
304 lsda_header read_lsda_header(char *lsda, _Unwind_Context *context) {
305 lsda_header info;
306 int start_lsda = (int)lsda;
307 char encoding = *lsda++;
309 /* Read LPStart */
310 /* if (encoding != 0xff) {
311 // while(1);
312 } else {*/
313 info.lpstart = _Unwind_GetRegionStart(context);
314 // }
316 /* Read TType */
317 encoding = *lsda++;
318 if (encoding != 0xff) {
319 int i = 0;
320 unsigned int ttype = read_uleb128(lsda, i);
321 info.ttype = (char*)(ttype + (int)lsda + i);
322 lsda += i;
325 /* Read Call-site entries encoding */
326 info.call_site_encoding = *lsda++;
328 /* Read Action table */
329 int i = 0;
330 unsigned int action = read_uleb128(lsda, i);
331 info.action_table = (char*)(action + (int)lsda + i);
332 lsda += i;
334 /* Call-site */
335 info.call_site = lsda;
337 return info;
340 extern "C" _Unwind_Reason_Code __gxx_personality_v0(int version, _Unwind_Action actions, uint64 exception_class, _Unwind_Exception *ue_header, _Unwind_Context *context) {
341 if (version != 1)
342 return _URC_FATAL_PHASE1_ERROR;
344 if (actions == _UA_SEARCH_PHASE) {
345 unsigned int lsda = _Unwind_GetLanguageSpecificData(context);
347 if (!lsda)
348 return _URC_CONTINUE_UNWIND;
350 lsda_header hdr = read_lsda_header((char*)lsda, context);
351 unsigned int ip = _Unwind_GetIP(context) - 5; // sizeof(call addr32) == 5
353 // we assume that call_site encoding == uleb128
354 int i = 0;
355 int cs_ip = 0, cs_len = 0, cs_lp = 0, cs_action = 0;
356 do {
357 /* Go through call site table (look for ip) */
358 cs_ip = read_uleb128(hdr.call_site, i) + hdr.lpstart;
359 cs_len = read_uleb128(hdr.call_site, i);
360 cs_lp = read_uleb128(hdr.call_site, i);
361 cs_action = read_uleb128(hdr.call_site, i) - 1;
363 if (cs_ip == ip) {
364 int next;
365 __cxa_exception *full_exception = (__cxa_exception*)((unsigned int)ue_header - sizeof(__cxa_exception) + sizeof(_Unwind_Exception));
367 /* Go through action table (look for type to catch) */
368 do {
369 int position = cs_action;
371 int action_id = read_uleb128(hdr.action_table, position);
372 if (action_id == 0)
373 break;
375 cs_action = position;
376 next = read_sleb128(hdr.action_table, position);
378 std::type_info *type = (std::type_info*)*(unsigned int*)((unsigned int)hdr.ttype - action_id * sizeof(unsigned int));
380 if (*type == *full_exception->exceptionType) {
381 /* cache collected data */
383 return _URC_HANDLER_FOUND;
386 cs_action += next;
387 } while (next);
390 } while((unsigned int)&hdr.call_site[i] <= (unsigned int)hdr.action_table);
392 return _URC_CONTINUE_UNWIND;
394 } else if (actions == _UA_CLEANUP_PHASE) {
396 } else if (actions == _UA_HANDLER_FRAME) {
402 extern "C" void *__cxa_allocate_exception(unsigned int thrown_size){
403 char *excp = new char[thrown_size + sizeof(__cxa_exception)];
404 memset(excp, 0, sizeof(__cxa_exception));
405 return (void*)((unsigned int)excp + sizeof(__cxa_exception));
408 extern "C" void __cxa_throw(void *obj, std::type_info *tinfo, void (*dest) (void *)) {
409 __cxa_exception *header = ((__cxa_exception*)obj - 1);
411 header->exceptionDestructor = dest;
412 header->exceptionType = tinfo;
413 strncpy(header->unwindHeader.exception_class, "QUARC++\0", 8);
415 __cxa_get_globals()->uncaughtExceptions++;
417 _Unwind_RaiseException(&header->unwindHeader);
419 critical("unhandled exception thrown");
422 extern "C" void __cxa_begin_catch() {
425 extern "C" void __cxa_end_catch(){}