Changes in src/libcfa/exception.c [cbce272:38ac6ec]
- File:
-
- 1 edited
-
src/libcfa/exception.c (modified) (15 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/libcfa/exception.c
rcbce272 r38ac6ec 10 10 // Created On : Mon Jun 26 15:13:00 2017 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Fri Aug 4 15:20:00 2017 13 // Update Count : 6 14 // 15 16 #include <stddef.h> // for size_t 12 // Last Modified On : Tus Jul 11 16:36:00 2017 13 // Update Count : 1 14 // 17 15 18 16 #include "exception.h" … … 24 22 #include <unwind.h> 25 23 26 // FIX ME: temporary hack to keep ARM build working27 #ifndef _URC_FATAL_PHASE1_ERROR28 #define _URC_FATAL_PHASE1_ERROR 229 #endif // ! _URC_FATAL_PHASE1_ERROR30 #ifndef _URC_FATAL_PHASE2_ERROR31 #define _URC_FATAL_PHASE2_ERROR 232 #endif // ! _URC_FATAL_PHASE2_ERROR33 34 24 #include "lsda.h" 35 25 36 37 // Base exception vtable is abstract, you should not have base exceptions.38 struct __cfaehm__base_exception_t_vtable39 ___cfaehm__base_exception_t_vtable_instance = {40 .parent = NULL,41 .size = 0,42 .copy = NULL,43 .free = NULL,44 .msg = NULL45 };46 47 48 26 // Temperary global exception context. Does not work with concurency. 49 struct exception_context_t {27 struct shared_stack_t { 50 28 struct __cfaehm__try_resume_node * top_resume; 51 29 struct __cfaehm__try_resume_node * current_resume; 52 30 53 exception *current_exception;31 exception current_exception; 54 32 int current_handler_index; 55 33 } shared_stack = {NULL, NULL, 0, 0}; 56 34 57 // Get the current exception context. 58 // There can be a single global until multithreading occurs, then each stack 59 // needs its own. It will have to be updated to handle that. 60 struct exception_context_t * this_exception_context() { 61 return &shared_stack; 62 } 63 //#define SAVE_EXCEPTION_CONTEXT(to_name) 64 //struct exception_context_t * to_name = this_exception_context(); 65 //exception * this_exception() { 66 // return this_exception_context()->current_exception; 67 //} 68 69 70 // This macro should be the only thing that needs to change across machines. Used in the personality function, way down 71 // in termination. 35 36 37 // This macro should be the only thing that needs to change across machines. 38 // Used in the personality function, way down in termination. 72 39 // struct _Unwind_Context * -> _Unwind_Reason_Code(*)(exception *) 73 40 #define MATCHER_FROM_CONTEXT(ptr_to_context) \ … … 80 47 81 48 // DEBUG 82 printf("Throwing resumption exception \n");49 printf("Throwing resumption exception %d\n", *except); 83 50 84 51 struct __cfaehm__try_resume_node * original_head = shared_stack.current_resume; … … 94 61 } 95 62 96 printf("Unhandled exception \n");63 printf("Unhandled exception %d\n", *except); 97 64 shared_stack.current_resume = original_head; 98 65 … … 102 69 } 103 70 104 // Do we control where exceptions get thrown even with concurency? If not these are not quite thread safe, the cleanup 105 // hook has to be added after the node is built but before it is made the top node. 106 71 /* Do we control where exceptions get thrown even with concurency? 72 * If not these are not quite thread safe, the cleanup hook has to be added 73 * after the node is built but before it is made the top node. 74 */ 107 75 void __cfaehm__try_resume_setup(struct __cfaehm__try_resume_node * node, 108 76 int (*handler)(exception * except)) { … … 119 87 // TERMINATION =============================================================== 120 88 121 // MEMORY MANAGEMENT (still for integers) 122 // May have to move to cfa for constructors and destructors (references). 123 124 struct __cfaehm__node { 125 struct __cfaehm__node * next; 126 }; 127 128 #define NODE_TO_EXCEPT(node) ((exception *)(1 + (node))) 129 #define EXCEPT_TO_NODE(except) ((struct __cfaehm__node *)(except) - 1) 130 131 // Creates a copy of the indicated exception and sets current_exception to it. 132 static void __cfaehm__allocate_exception( exception * except ) { 133 struct exception_context_t * context = this_exception_context(); 134 135 // Allocate memory for the exception. 136 struct __cfaehm__node * store = malloc( 137 sizeof( struct __cfaehm__node ) + except->virtual_table->size ); 138 139 if ( ! store ) { 140 // Failure: cannot allocate exception. Terminate thread. 141 abort(); // <- Although I think it might be the process. 142 } 143 144 // Add the node to the list: 145 store->next = EXCEPT_TO_NODE(context->current_exception); 146 context->current_exception = NODE_TO_EXCEPT(store); 147 148 // Copy the exception to storage. 149 except->virtual_table->copy( context->current_exception, except ); 150 } 151 152 // Delete the provided exception, unsetting current_exception if relivant. 153 static void __cfaehm__delete_exception( exception * except ) { 154 struct exception_context_t * context = this_exception_context(); 155 156 // DEBUG 157 printf( "Deleting Exception\n"); 158 159 // Remove the exception from the list. 160 struct __cfaehm__node * to_free = EXCEPT_TO_NODE(except); 161 struct __cfaehm__node * node; 162 163 if ( context->current_exception == except ) { 164 node = to_free->next; 165 context->current_exception = (node) ? NODE_TO_EXCEPT(node) : 0; 166 } else { 167 node = EXCEPT_TO_NODE(context->current_exception); 168 // It may always be in the first or second position. 169 while( to_free != node->next ) { 170 node = node->next; 171 } 172 node->next = to_free->next; 173 } 174 175 // Free the old exception node. 176 except->virtual_table->free( except ); 177 free( to_free ); 178 } 179 180 // If this isn't a rethrow (*except==0), delete the provided exception. 181 void __cfaehm__cleanup_terminate( void * except ) { 182 if ( *(void**)except ) __cfaehm__delete_exception( *(exception**)except ); 183 } 184 89 // Requires -fexceptions to work. 90 91 // Global which defines the current exception 92 // Currently an int just to make matching easier 93 //int this_exception; (became shared_stack.current_exception) 185 94 186 95 // We need a piece of storage to raise the exception … … 202 111 } 203 112 204 // The exception that is being thrown must already be stored. 205 __attribute__((noreturn)) void __cfaehm__begin_unwind(void) { 206 if ( ! this_exception_context()->current_exception ) { 207 printf("UNWIND ERROR missing exception in begin unwind\n"); 208 abort(); 209 } 210 113 void __cfaehm__throw_terminate( exception * val ) { 114 // Store the current exception 115 shared_stack.current_exception = *val; 116 117 // DEBUG 118 printf("Throwing termination exception %d\n", *val); 211 119 212 120 // Call stdlibc to raise the exception 213 121 _Unwind_Reason_Code ret = _Unwind_RaiseException( &this_exception_storage ); 214 122 215 // If we reach here it means something happened. For resumption to work we need to find a way to return back to 216 // here. Most of them will probably boil down to setting a global flag and making the phase 1 either stop or 217 // fail. Causing an error on purpose may help avoiding unnecessary work but it might have some weird side 218 // effects. If we just pretend no handler was found that would work but may be expensive for no reason since we 219 // will always search the whole stack. 123 // If we reach here it means something happened 124 // For resumption to work we need to find a way to return back to here 125 // Most of them will probably boil down to setting a global flag and making the phase 1 either stop or fail. 126 // Causing an error on purpose may help avoiding unnecessary work but it might have some weird side effects. 127 // If we just pretend no handler was found that would work but may be expensive for no reason since we will always 128 // search the whole stack 220 129 221 130 if( ret == _URC_END_OF_STACK ) { 222 // No proper handler was found. This can be handled in several way. C++ calls std::terminate Here we 223 // force unwind the stack, basically raising a cancellation. 131 // No proper handler was found 132 // This can be handled in several way 133 // C++ calls std::terminate 134 // Here we force unwind the stack, basically raising a cancellation 224 135 printf("Uncaught exception %p\n", &this_exception_storage); 225 136 … … 229 140 } 230 141 231 // We did not simply reach the end of the stack without finding a handler. Something wen't wrong 142 // We did not simply reach the end of the stack without finding a handler, 143 // Something wen't wrong 232 144 printf("UNWIND ERROR %d after raise exception\n", ret); 233 145 abort(); 234 146 } 235 147 236 void __cfaehm__throw_terminate( exception * val ) { 237 // DEBUG 238 printf("Throwing termination exception\n"); 239 240 __cfaehm__allocate_exception( val ); 241 __cfaehm__begin_unwind(); 242 } 243 148 // Nesting this the other way would probably be faster. 244 149 void __cfaehm__rethrow_terminate(void) { 245 150 // DEBUG 246 151 printf("Rethrowing termination exception\n"); 247 152 248 __cfaehm__begin_unwind(); 249 } 250 251 // This is our personality routine. For every stack frame anotated with ".cfi_personality 0x3,__gcfa_personality_v0". 252 // This function will be called twice when unwinding. Once in the search phased and once in the cleanup phase. 153 __cfaehm__throw_terminate(&shared_stack.current_exception); 154 } 155 156 // This is our personality routine 157 // For every stack frame anotated with ".cfi_personality 0x3,__gcfa_personality_v0" 158 // This function will be called twice when unwinding 159 // Once in the search phased and once in the cleanup phase 253 160 _Unwind_Reason_Code __gcfa_personality_v0 ( 254 161 int version, _Unwind_Action actions, unsigned long long exceptionClass, … … 356 263 _Unwind_Reason_Code (*matcher)(exception *) = 357 264 MATCHER_FROM_CONTEXT(context); 358 int index = matcher( shared_stack.current_exception);265 int index = matcher(&shared_stack.current_exception); 359 266 _Unwind_Reason_Code ret = (0 == index) 360 267 ? _URC_CONTINUE_UNWIND : _URC_HANDLER_FOUND; … … 386 293 // I assume this sets the instruction pointer to the adress of the landing pad 387 294 // It doesn't actually set it, it only state the value that needs to be set once we return _URC_INSTALL_CONTEXT 388 _Unwind_SetIP( context, ((lsd_info.LPStart) + (callsite_landing_pad)));295 _Unwind_SetIP( context, lsd_info.LPStart + callsite_landing_pad ); 389 296 390 297 // DEBUG … … 410 317 } 411 318 412 // Try statements are hoisted out see comments for details. With this could probably be unique and simply linked from 413 // libcfa but there is one problem left, see the exception table for details 319 // Try statements are hoisted out see comments for details 320 // With this could probably be unique and simply linked from 321 // libcfa but there is one problem left, see the exception table 322 // for details 414 323 __attribute__((noinline)) 415 324 void __cfaehm__try_terminate(void (*try_block)(), … … 419 328 //! printf("%p %p %p %p\n", &try_block, &catch_block, &match_block, &xy); 420 329 421 // Setup statments: These 2 statments won't actually result in any code, they only setup global tables. 422 // However, they clobber gcc cancellation support from gcc. We can replace the personality routine but 423 // replacing the exception table gcc generates is not really doable, it generates labels based on how the 424 // assembly works. 425 330 // Setup statments 331 // These 2 statments won't actually result in any code, 332 // they only setup global tables. 333 // However, they clobber gcc cancellation support from gcc. 334 // We can replace the personality routine but replacing the exception 335 // table gcc generates is not really doable, it generates labels based 336 // on how the assembly works. 426 337 // Setup the personality routine 427 338 asm volatile (".cfi_personality 0x3,__gcfa_personality_v0"); … … 429 340 asm volatile (".cfi_lsda 0x3, .LLSDACFA2"); 430 341 431 // Label which defines the start of the area for which the handler is setup .342 // Label which defines the start of the area for which the handler is setup 432 343 asm volatile (".TRYSTART:"); 433 344 … … 443 354 // Exceptionnal path 444 355 CATCH : __attribute__(( unused )); 445 // Label which defines the end of the area for which the handler is setup .356 // Label which defines the end of the area for which the handler is setup 446 357 asm volatile (".TRYEND:"); 447 // Label which defines the start of the exception landing pad. Basically what is called when the exception is 448 // caught. Note, if multiple handlers are given, the multiplexing should be done by the generated code, not the 449 // exception runtime. 358 // Label which defines the start of the exception landing pad 359 // basically what will be called when the exception is caught 360 // Note, if multiple handlers are given, the multiplexing should be done 361 // by the generated code, not the exception runtime 450 362 asm volatile (".CATCH:"); 451 363 452 364 // Exception handler 453 catch_block( shared_stack.current_handler_index, 454 shared_stack.current_exception ); 455 } 456 457 // Exception table data we need to generate. While this is almost generic, the custom data refers to foo_try_match try 458 // match, which is no way generic. Some more works need to be done if we want to have a single call to the try routine. 459 460 #if defined( __x86_64__ ) || defined( __i386__ ) 365 catch_block(shared_stack.current_handler_index, 366 &shared_stack.current_exception); 367 } 368 369 // Exception table data we need to generate 370 // While this is almost generic, the custom data refers to 371 // foo_try_match try match, which is no way generic 372 // Some more works need to be done if we want to have a single 373 // call to the try routine 461 374 asm ( 462 375 //HEADER … … 481 394 // " .section .note.GNU-stack,\"x\",@progbits\n" 482 395 ); 483 #endif // __x86_64__ || __i386__
Note:
See TracChangeset
for help on using the changeset viewer.