usb: getting string descriptors, minor improvements
[quarnos.git] / manes / exception.cpp
blobfb4a904156070ad4ef484b22d613a2c7abd0e265
1 /* Exception Support
2 * Based on DWARF2
3 */
5 /* Copyright (C) 2009 Pawel Dziepak */
7 #include "typeinfo"
8 //#include "manes/error.h"
10 namespace std {
11 void terminate() {
12 asm("cli\nhlt"::"a"(0xdeadbeef));
16 typedef char uint64[8];
17 typedef unsigned int cpu_word;
19 int strncpy(char*,const char*,int);
20 int memset(void*,int,int);
22 typedef enum {
23 _URC_NO_REASON = 0,
24 _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
25 _URC_FATAL_PHASE2_ERROR = 2,
26 _URC_FATAL_PHASE1_ERROR = 3,
27 _URC_NORMAL_STOP = 4,
28 _URC_END_OF_STACK = 5,
29 _URC_HANDLER_FOUND = 6,
30 _URC_INSTALL_CONTEXT = 7,
31 _URC_CONTINUE_UNWIND = 8
32 } _Unwind_Reason_Code;
35 struct _Unwind_Context {
36 cpu_word eip;
37 cpu_word ebp;
38 cpu_word lsda;
39 cpu_word proc;
41 /* Phase 2 */
42 cpu_word eax;
43 cpu_word edx;
44 cpu_word landing_pad;
48 typedef void (*_Unwind_Exception_Cleanup_Fn)(_Unwind_Reason_Code reason, struct _Unwind_Exception *exc);
50 struct _Unwind_Exception {
51 uint64 exception_class;
52 _Unwind_Exception_Cleanup_Fn exception_cleanup;
53 uint64 private_1;
54 uint64 private_2;
58 /* Personality Routine Actions */
59 typedef int _Unwind_Action;
60 static const _Unwind_Action _UA_SEARCH_PHASE = 1;
61 static const _Unwind_Action _UA_CLEANUP_PHASE = 2;
62 static const _Unwind_Action _UA_HANDLER_FRAME = 4;
63 static const _Unwind_Action _UA_FORCE_UNWIND = 8;
65 void jump_to(cpu_word eax, cpu_word edx, cpu_word ebp, cpu_word eip) {
66 /* TODO: This routine should also set old esp value! */
67 asm("movl %%ecx, %%ebp\n" \
68 "pushl %%esi\n" \
69 "ret" ::"a"(eax), "d"(edx), "c"(ebp), "S" (eip));
72 typedef _Unwind_Reason_Code (*personality_fn)(int version, _Unwind_Action actions, uint64 exception_class,_Unwind_Exception *ue_header, _Unwind_Context *context);
73 /* Based on DWARF3 Specification */
75 /* Read unsigned little endain base 128 value */
76 cpu_word read_uleb128(unsigned char *data, int &index) {
77 cpu_word value = 0;
78 unsigned char shift = 0;
79 do {
80 value |= ((data[index] & 0x7f) << shift);
81 shift += 7;
82 index++;
83 } while(data[index - 1] & 0x80);
85 return value;
88 /* Read signed little endain base 128 value */
89 signed int read_sleb128(unsigned char *data, int &index) {
90 signed int value = 0;
91 unsigned char shift = 0;
92 do {
93 value |= ((data[index] & 0x7f) << shift);
94 shift += 7;
95 index++;
96 } while(data[index - 1] & 0x80);
98 cpu_word minus = 0xffffffff & ~((1 << (shift - 1)) - 1);
99 if (value & (1 << (shift - 1)))
100 value |= minus;
101 return value;
104 struct fde {
105 cpu_word length;
106 fde *cie_pointer;
107 cpu_word initial_location;
108 cpu_word address_range;
110 struct return_fde {
111 fde *found_fde;
112 cpu_word personality;
113 cpu_word lsda;
116 cpu_word get_personality(cpu_word cie) {
117 cie += sizeof(cpu_word) * 2; // length and cie_id
118 for (; *reinterpret_cast<unsigned char*>(cie); cie++); // augmentation string
119 int i = 1;
120 read_uleb128(reinterpret_cast<unsigned char*>(cie), i); // code align
121 read_uleb128(reinterpret_cast<unsigned char*>(cie), i); // data align
122 read_uleb128(reinterpret_cast<unsigned char*>(cie), i); // return address index
123 cie += i;
124 cie++; // augmentation size
126 if (! *reinterpret_cast<char*>(cie)) {
127 cie++;
128 return *reinterpret_cast<cpu_word*>(cie);
129 } else
130 return 0;
133 cpu_word get_lsda(fde *current) {
134 cpu_word pointer = reinterpret_cast<cpu_word>(current);
135 pointer += sizeof(fde);
136 if (*reinterpret_cast<char*>(pointer) == 0 || *reinterpret_cast<char*>(pointer) == 4) {
137 pointer++;
138 return *reinterpret_cast<cpu_word*>(pointer);
139 } else
140 return 0;
143 return_fde get_fde(cpu_word eip) {
144 extern void *_eh_frame;
145 extern void *_eh_frame_end;
147 fde *current = reinterpret_cast<fde*>(&_eh_frame);
149 while ((cpu_word)current < (cpu_word)&_eh_frame_end) {
150 /* do not want CIE */
151 if (current->cie_pointer != (fde*)0) {
153 if (eip >= current->initial_location
154 && eip <= current->initial_location + current->address_range) {
155 return_fde ret;
156 ret.found_fde = current;
157 cpu_word cie = (cpu_word)current + sizeof(unsigned int) - (cpu_word)current->cie_pointer;
158 ret.personality = get_personality(cie);
159 ret.lsda = get_lsda(current);
161 return ret;
165 current = reinterpret_cast<fde*>(reinterpret_cast<cpu_word>(current) + current->length + sizeof(cpu_word));
169 /* Based on IA-64 ABI Specification */
172 struct __cxa_exception {
173 std::type_info *exceptionType;
174 void (*exceptionDestructor) (void *);
175 /* unexpected_handler unexpectedHandler;
176 terminate_handler terminateHandler;*/
177 __cxa_exception * nextException;
179 int handlerCount;
180 int handlerSwitchValue;
181 const char *actionRecord;
182 const char *languageSpecificData;
183 void *catchTemp;
184 void *adjustedPtr;
186 _Unwind_Exception unwindHeader;
189 struct __cxa_eh_globals {
190 __cxa_exception *caughtExceptions;
191 cpu_word uncaughtExceptions;
194 __cxa_eh_globals eh_globals;
196 __cxa_eh_globals *__cxa_get_globals(void) {
197 return &eh_globals;
200 extern "C" void _Unwind_Resume(_Unwind_Exception ex) {
203 struct frame_pointer {
204 frame_pointer *next;
205 cpu_word eip;
208 cpu_word _Unwind_GetIP(struct _Unwind_Context *context) {
209 return context->eip;
212 cpu_word _Unwind_GetCFA(struct _Unwind_Context *context) {
213 return context->ebp;
216 cpu_word _Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) {
217 return context->lsda;
220 cpu_word _Unwind_GetRegionStart(struct _Unwind_Context *context) {
221 return context->proc;
224 void _Unwind_SetGR(_Unwind_Context *context, int index, cpu_word new_value) {
225 if (index == 0)
226 context->eax = new_value;
227 else
228 context->edx = new_value;
231 void _Unwind_SetIP(_Unwind_Context *context, cpu_word new_value) {
232 context->landing_pad = new_value;
236 void *get_eh_frame();
238 void InstallContext(_Unwind_Context *context) {
239 /* TODO: Set old esp and register values (other than eax and edx)! */
240 jump_to(context->eax, context->edx, context->ebp, context->landing_pad);
243 extern "C" _Unwind_Reason_Code _Unwind_RaiseException(_Unwind_Exception *ex) {
245 /* Phase 1
246 * Ask personality at each eip if there is an exception handler.
248 get_eh_frame();
249 frame_pointer *current_fp;
250 personality_fn personality;
251 _Unwind_Context context;
252 __asm__("movl %%ebp, %%eax" : "=a" (current_fp));
253 current_fp = current_fp->next;
254 do {
255 if (!current_fp)
256 return _URC_END_OF_STACK;
258 cpu_word return_point = current_fp->eip;
260 return_fde ret = get_fde(return_point);
261 context.eip = return_point;
262 context.ebp = reinterpret_cast<cpu_word>(current_fp->next);
263 context.proc = ret.found_fde->initial_location;
264 context.lsda = ret.lsda;
266 union {
267 cpu_word val;
268 personality_fn personality;
269 } cast;
270 /* UD and what? All this code is implementation-specific */
271 cast.val = ret.personality;
272 personality = cast.personality;
274 current_fp = current_fp->next;
276 } while(personality(1, _UA_SEARCH_PHASE, ex->exception_class, ex, &context) != _URC_HANDLER_FOUND);
278 /* Phase 2
279 * Ask personality to perform cleanup at each eip and eventually jump to the handler.
282 _Unwind_Context handler = context;
283 __asm__("movl %%ebp, %%eax" : "=a" (current_fp));
284 do {
285 cpu_word return_point = current_fp->eip;
287 return_fde ret = get_fde(return_point);
288 context.eip = return_point;
289 context.ebp = reinterpret_cast<cpu_word>(current_fp->next);
290 context.proc = ret.found_fde->initial_location;
291 context.lsda = ret.lsda;
293 union {
294 cpu_word val;
295 personality_fn personality;
296 } cast;
297 /* UD and what? All this code is implementation-specific */
298 cast.val = ret.personality;
299 personality = cast.personality;
301 context.eip = return_point;
302 context.ebp = reinterpret_cast<cpu_word>(current_fp->next);
304 current_fp = current_fp->next;
305 } while (context.ebp != handler.ebp && personality(1, _UA_CLEANUP_PHASE, ex->exception_class, ex, &context) != _URC_FATAL_PHASE2_ERROR);
307 /* Call handler */
308 personality(1, _UA_HANDLER_FRAME, ex->exception_class, ex, &handler);
313 struct lsda_header_info
315 _Unwind_Ptr Start;
316 _Unwind_Ptr LPStart;
317 const unsigned char *TType;
318 const unsigned char *action_table;
319 unsigned char ttype_encoding;
320 unsigned char call_site_encoding;
324 struct lsda_header {
325 cpu_word lpstart;
326 unsigned char *ttype;
327 cpu_word call_site_encoding;
328 unsigned char *action_table;
329 unsigned char *call_site;
332 #define DW_EH_PE_omit 0xff
334 lsda_header read_lsda_header(unsigned char *lsda, _Unwind_Context *context) {
335 lsda_header info;
336 unsigned char encoding = *lsda++;
338 /* Read LPStart */
339 /* if (encoding != 0xff) {
340 // while(1);
341 } else {*/
342 info.lpstart = _Unwind_GetRegionStart(context);
343 // }
345 /* Read TType */
346 encoding = *lsda++;
347 if (encoding != 0xff) {
348 int i = 0;
349 cpu_word ttype = read_uleb128(lsda, i);
350 info.ttype = reinterpret_cast<unsigned char*>(ttype + reinterpret_cast<cpu_word>(lsda) + i);
351 lsda += i;
354 /* Read Call-site entries encoding */
355 info.call_site_encoding = *lsda++;
357 /* Read Action table */
358 int i = 0;
359 cpu_word action = read_uleb128(lsda, i);
360 info.action_table = reinterpret_cast<unsigned char*>(action + reinterpret_cast<cpu_word>(lsda) + i);
361 lsda += i;
363 /* Call-site */
364 info.call_site = lsda;
366 return info;
369 extern "C" _Unwind_Reason_Code __gxx_personality_v0(int version, _Unwind_Action actions, uint64 exception_class, _Unwind_Exception *ue_header, _Unwind_Context *context) {
370 if (version != 1)
371 return _URC_FATAL_PHASE1_ERROR;
373 if (actions == _UA_SEARCH_PHASE) {
374 cpu_word lsda = _Unwind_GetLanguageSpecificData(context);
376 if (!lsda)
377 return _URC_CONTINUE_UNWIND;
379 lsda_header hdr = read_lsda_header(reinterpret_cast<unsigned char*>(lsda), context);
380 cpu_word ip = _Unwind_GetIP(context) - 5; // sizeof(call addr32) == 5
382 // we assume that call_site encoding == uleb128
383 int i = 0;
384 cpu_word cs_ip = 0, cs_len = 0, cs_lp = 0, cs_action = 0;
385 do {
386 /* Go through call site table (look for ip) */
387 cs_ip = read_uleb128(hdr.call_site, i) + hdr.lpstart;
388 cs_len = read_uleb128(hdr.call_site, i);
389 cs_lp = read_uleb128(hdr.call_site, i);
390 cs_action = read_uleb128(hdr.call_site, i) - 1;
392 if (cs_ip == ip) {
393 int next;
394 __cxa_exception *full_exception = (__cxa_exception*)((cpu_word)ue_header - sizeof(__cxa_exception) + sizeof(_Unwind_Exception));
396 /* Go through action table (look for type to catch) */
397 do {
398 int position = cs_action;
400 int action_id = read_uleb128(hdr.action_table, position);
401 if (action_id == 0)
402 break;
404 cs_action = position;
405 next = read_sleb128(hdr.action_table, position);
407 std::type_info *type = *reinterpret_cast<std::type_info**>(reinterpret_cast<cpu_word>(hdr.ttype) - action_id * sizeof(cpu_word));
409 if (*type == *full_exception->exceptionType) {
410 /* cache collected data */
412 /* F*ck cleanup phase, jump now */
413 _Unwind_SetGR(context, 0, reinterpret_cast<cpu_word>(ue_header));
414 _Unwind_SetGR(context, 1, action_id);
415 _Unwind_SetIP(context, hdr.lpstart + cs_lp);
416 InstallContext(context);
418 return _URC_HANDLER_FOUND;
421 cs_action += next;
422 } while (next);
425 } while(reinterpret_cast<cpu_word>(&hdr.call_site[i]) <= reinterpret_cast<cpu_word>(hdr.action_table));
427 return _URC_CONTINUE_UNWIND;
429 } else if (actions == _UA_CLEANUP_PHASE) {
431 } else if (actions == _UA_HANDLER_FRAME) {
437 extern "C" void *__cxa_allocate_exception(cpu_word thrown_size){
438 char *excp = new char[thrown_size + sizeof(__cxa_exception)];
439 memset(excp, 0, sizeof(__cxa_exception));
440 return reinterpret_cast<void*>(reinterpret_cast<cpu_word>(excp) + sizeof(__cxa_exception));
443 extern "C" void __cxa_throw(void *obj, std::type_info *tinfo, void (*dest) (void *)) {
444 __cxa_exception *header = reinterpret_cast<__cxa_exception*>(obj) - 1;
446 header->exceptionDestructor = dest;
447 header->exceptionType = tinfo;
448 strncpy(header->unwindHeader.exception_class, "QUARC++\0", 8);
450 __cxa_get_globals()->uncaughtExceptions++;
452 _Unwind_RaiseException(&header->unwindHeader);
454 //critical("unhandled exception thrown");
455 asm("cli\nhlt"::"a"(0xdeadbeef));
458 extern "C" void __cxa_begin_catch() {
461 extern "C" void __cxa_end_catch(){}