Changes in libcfa/src/exception.c [c960331:eb46fdf]
- File:
-
- 1 edited
-
libcfa/src/exception.c (modified) (16 diffs)
Legend:
- Unmodified
- Added
- Removed
-
libcfa/src/exception.c
rc960331 reb46fdf 9 9 // Author : Andrew Beach 10 10 // Created On : Mon Jun 26 15:13:00 2017 11 // Last Modified By : Andrew Beach12 // Last Modified On : T ue Oct 27 16:27:00 202013 // Update Count : 3511 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Feb 22 18:17:34 2018 13 // Update Count : 11 14 14 // 15 15 16 // Normally we would get this from the CFA prelude.17 16 #include <stddef.h> // for size_t 18 17 19 #include <unwind.h> // for struct _Unwind_Exception {...};20 21 18 #include "exception.h" 19 20 // Implementation of the secret header. 22 21 23 22 #include <stdlib.h> 24 23 #include <stdio.h> 24 #include <unwind.h> 25 25 #include <bits/debug.hfa> 26 #include "concurrency/invoke.h" 27 #include "stdhdr/assert.h" 28 29 #if defined( __ARM_ARCH ) 30 #warning FIX ME: temporary hack to keep ARM build working 26 27 // FIX ME: temporary hack to keep ARM build working 31 28 #ifndef _URC_FATAL_PHASE1_ERROR 32 #define _URC_FATAL_PHASE1_ERROR 329 #define _URC_FATAL_PHASE1_ERROR 2 33 30 #endif // ! _URC_FATAL_PHASE1_ERROR 34 31 #ifndef _URC_FATAL_PHASE2_ERROR 35 32 #define _URC_FATAL_PHASE2_ERROR 2 36 33 #endif // ! _URC_FATAL_PHASE2_ERROR 37 #endif // __ARM_ARCH38 34 39 35 #include "lsda.h" 40 36 41 /* The exception class for our exceptions. Because of the vendor component42 * its value would not be standard.43 * Vendor: UWPL44 * Language: CFA\045 */46 const _Unwind_Exception_Class __cfaehm_exception_class = 0x4c50575500414643;47 37 48 38 // Base exception vtable is abstract, you should not have base exceptions. 49 struct __cfa ehm_base_exception_t_vtable50 ___cfa ehm_base_exception_t_vtable_instance = {39 struct __cfaabi_ehm__base_exception_t_vtable 40 ___cfaabi_ehm__base_exception_t_vtable_instance = { 51 41 .parent = NULL, 52 42 .size = 0, … … 57 47 58 48 49 // Temperary global exception context. Does not work with concurency. 50 struct exception_context_t { 51 struct __cfaabi_ehm__try_resume_node * top_resume; 52 struct __cfaabi_ehm__try_resume_node * current_resume; 53 54 exception_t * current_exception; 55 int current_handler_index; 56 } shared_stack = {NULL, NULL, 0, 0}; 57 59 58 // Get the current exception context. 60 59 // There can be a single global until multithreading occurs, then each stack 61 // needs its own. We get this from libcfathreads (no weak attribute). 62 __attribute__((weak)) struct exception_context_t * this_exception_context() { 63 static struct exception_context_t shared_stack = {NULL, NULL}; 60 // needs its own. It will have to be updated to handle that. 61 struct exception_context_t * this_exception_context() { 64 62 return &shared_stack; 65 63 } 64 //#define SAVE_EXCEPTION_CONTEXT(to_name) 65 //struct exception_context_t * to_name = this_exception_context(); 66 //exception * this_exception() { 67 // return this_exception_context()->current_exception; 68 //} 69 70 71 // This macro should be the only thing that needs to change across machines. 72 // Used in the personality function, way down in termination. 73 // struct _Unwind_Context * -> _Unwind_Reason_Code(*)(exception_t *) 74 #define MATCHER_FROM_CONTEXT(ptr_to_context) \ 75 (*(_Unwind_Reason_Code(**)(exception_t *))(_Unwind_GetCFA(ptr_to_context) + 8)) 66 76 67 77 68 78 // RESUMPTION ================================================================ 69 79 70 static void reset_top_resume(struct __cfaehm_try_resume_node ** store) { 71 this_exception_context()->top_resume = *store; 72 } 73 74 void __cfaehm_throw_resume(exception_t * except, void (*defaultHandler)(exception_t *)) { 75 struct exception_context_t * context = this_exception_context(); 76 77 __cfadbg_print_safe(exception, "Throwing resumption exception\n"); 78 79 { 80 __attribute__((cleanup(reset_top_resume))) 81 struct __cfaehm_try_resume_node * original_head = context->top_resume; 82 struct __cfaehm_try_resume_node * current = context->top_resume; 83 84 for ( ; current ; current = current->next) { 85 context->top_resume = current->next; 86 if (current->handler(except)) { 87 return; 88 } 80 void __cfaabi_ehm__throw_resume(exception_t * except) { 81 82 __cfaabi_dbg_print_safe("Throwing resumption exception\n"); 83 84 struct __cfaabi_ehm__try_resume_node * original_head = shared_stack.current_resume; 85 struct __cfaabi_ehm__try_resume_node * current = 86 (original_head) ? original_head->next : shared_stack.top_resume; 87 88 for ( ; current ; current = current->next) { 89 shared_stack.current_resume = current; 90 if (current->handler(except)) { 91 shared_stack.current_resume = original_head; 92 return; 89 93 } 90 } // End the search and return to the top of the stack. 91 92 // No handler found, fall back to the default operation. 93 __cfadbg_print_safe(exception, "Unhandled exception\n"); 94 defaultHandler(except); 94 } 95 96 __cfaabi_dbg_print_safe("Unhandled exception\n"); 97 shared_stack.current_resume = original_head; 98 99 // Fall back to termination: 100 __cfaabi_ehm__throw_terminate(except); 101 // TODO: Default handler for resumption. 95 102 } 96 103 … … 99 106 // be added after the node is built but before it is made the top node. 100 107 101 void __cfa ehm_try_resume_setup(struct __cfaehm_try_resume_node * node,108 void __cfaabi_ehm__try_resume_setup(struct __cfaabi_ehm__try_resume_node * node, 102 109 _Bool (*handler)(exception_t * except)) { 110 node->next = shared_stack.top_resume; 111 node->handler = handler; 112 shared_stack.top_resume = node; 113 } 114 115 void __cfaabi_ehm__try_resume_cleanup(struct __cfaabi_ehm__try_resume_node * node) { 116 shared_stack.top_resume = node->next; 117 } 118 119 120 // TERMINATION =============================================================== 121 122 // MEMORY MANAGEMENT (still for integers) 123 // May have to move to cfa for constructors and destructors (references). 124 125 struct __cfaabi_ehm__node { 126 struct __cfaabi_ehm__node * next; 127 }; 128 129 #define NODE_TO_EXCEPT(node) ((exception_t *)(1 + (node))) 130 #define EXCEPT_TO_NODE(except) ((struct __cfaabi_ehm__node *)(except) - 1) 131 132 // Creates a copy of the indicated exception and sets current_exception to it. 133 static void __cfaabi_ehm__allocate_exception( exception_t * except ) { 103 134 struct exception_context_t * context = this_exception_context(); 104 node->next = context->top_resume;105 node->handler = handler;106 context->top_resume = node;107 }108 109 void __cfaehm_try_resume_cleanup(struct __cfaehm_try_resume_node * node) {110 struct exception_context_t * context = this_exception_context();111 context->top_resume = node->next;112 }113 114 115 // MEMORY MANAGEMENT =========================================================116 117 #define NODE_TO_EXCEPT(node) ((exception_t *)(1 + (node)))118 #define EXCEPT_TO_NODE(except) ((struct __cfaehm_node *)(except) - 1)119 #define UNWIND_TO_NODE(unwind) ((struct __cfaehm_node *)(unwind))120 #define NULL_MAP(map, ptr) ((ptr) ? (map(ptr)) : NULL)121 122 // How to clean up an exception in various situations.123 static void __cfaehm_exception_cleanup(124 _Unwind_Reason_Code reason,125 struct _Unwind_Exception * exception) {126 switch (reason) {127 case _URC_FOREIGN_EXCEPTION_CAUGHT:128 // This one we could clean-up to allow cross-language exceptions.129 case _URC_FATAL_PHASE1_ERROR:130 case _URC_FATAL_PHASE2_ERROR:131 default:132 abort();133 }134 }135 136 // Creates a copy of the indicated exception and sets current_exception to it.137 static void __cfaehm_allocate_exception( exception_t * except ) {138 struct exception_context_t * context = this_exception_context();139 135 140 136 // Allocate memory for the exception. 141 struct __cfa ehm_node * store = malloc(142 sizeof( struct __cfa ehm_node ) + except->virtual_table->size );137 struct __cfaabi_ehm__node * store = malloc( 138 sizeof( struct __cfaabi_ehm__node ) + except->virtual_table->size ); 143 139 144 140 if ( ! store ) { … … 147 143 } 148 144 149 // Initialize the node:150 exception_t * except_store = NODE_TO_EXCEPT(store);151 store->unwind_exception.exception_class = __cfaehm_exception_class;152 store->unwind_exception.exception_cleanup = __cfaehm_exception_cleanup;153 store->handler_index = 0;154 except->virtual_table->copy( except_store, except );155 156 145 // Add the node to the list: 157 store->next = NULL_MAP(EXCEPT_TO_NODE, context->current_exception); 158 context->current_exception = except_store; 146 store->next = EXCEPT_TO_NODE(context->current_exception); 147 context->current_exception = NODE_TO_EXCEPT(store); 148 149 // Copy the exception to storage. 150 except->virtual_table->copy( context->current_exception, except ); 159 151 } 160 152 161 153 // Delete the provided exception, unsetting current_exception if relivant. 162 static void __cfa ehm_delete_exception( exception_t * except ) {154 static void __cfaabi_ehm__delete_exception( exception_t * except ) { 163 155 struct exception_context_t * context = this_exception_context(); 164 156 165 __cfa dbg_print_safe(exception,"Deleting Exception\n");157 __cfaabi_dbg_print_safe("Deleting Exception\n"); 166 158 167 159 // Remove the exception from the list. 168 struct __cfa ehm_node * to_free = EXCEPT_TO_NODE(except);169 struct __cfa ehm_node * node;160 struct __cfaabi_ehm__node * to_free = EXCEPT_TO_NODE(except); 161 struct __cfaabi_ehm__node * node; 170 162 171 163 if ( context->current_exception == except ) { 172 164 node = to_free->next; 173 context->current_exception = NULL_MAP(NODE_TO_EXCEPT, node);165 context->current_exception = (node) ? NODE_TO_EXCEPT(node) : 0; 174 166 } else { 175 167 node = EXCEPT_TO_NODE(context->current_exception); 176 168 // It may always be in the first or second position. 177 while ( to_free != node->next ) {169 while( to_free != node->next ) { 178 170 node = node->next; 179 171 } … … 186 178 } 187 179 188 // CANCELLATION ============================================================== 180 // If this isn't a rethrow (*except==0), delete the provided exception. 181 void __cfaabi_ehm__cleanup_terminate( void * except ) { 182 if ( *(void**)except ) __cfaabi_ehm__delete_exception( *(exception_t **)except ); 183 } 184 185 186 // We need a piece of storage to raise the exception 187 struct _Unwind_Exception this_exception_storage; 189 188 190 189 // Function needed by force unwind … … 193 192 int version, 194 193 _Unwind_Action actions, 195 _Unwind_Exception_Class exception _class,194 _Unwind_Exception_Class exceptionClass, 196 195 struct _Unwind_Exception * unwind_exception, 197 struct _Unwind_Context * unwind_context, 198 void * stop_param) { 199 // Verify actions follow the rules we expect. 200 verify(actions & _UA_CLEANUP_PHASE); 201 verify(actions & _UA_FORCE_UNWIND); 202 verify(!(actions & _UA_SEARCH_PHASE)); 203 verify(!(actions & _UA_HANDLER_FRAME)); 204 205 if ( actions & _UA_END_OF_STACK ) { 206 abort(); 207 } else { 208 return _URC_NO_REASON; 209 } 210 } 211 212 __attribute__((weak)) _Unwind_Reason_Code 213 __cfaehm_cancellation_unwind( struct _Unwind_Exception * exception ) { 214 return _Unwind_ForcedUnwind( exception, _Stop_Fn, (void*)0x22 ); 215 } 216 217 // Cancel the current stack, prefroming approprate clean-up and messaging. 218 void __cfaehm_cancel_stack( exception_t * exception ) { 219 __cfaehm_allocate_exception( exception ); 220 221 struct exception_context_t * context = this_exception_context(); 222 struct __cfaehm_node * node = EXCEPT_TO_NODE(context->current_exception); 223 224 // Preform clean-up of any extra active exceptions. 225 while ( node->next ) { 226 struct __cfaehm_node * to_free = node->next; 227 node->next = to_free->next; 228 exception_t * except = NODE_TO_EXCEPT( to_free ); 229 except->virtual_table->free( except ); 230 free( to_free ); 231 } 232 233 _Unwind_Reason_Code ret; 234 ret = __cfaehm_cancellation_unwind( &node->unwind_exception ); 235 printf("UNWIND ERROR %d after force unwind\n", ret); 236 abort(); 237 } 238 239 240 // TERMINATION =============================================================== 241 242 // If this isn't a rethrow (*except==0), delete the provided exception. 243 void __cfaehm_cleanup_terminate( void * except ) { 244 if ( *(void**)except ) __cfaehm_delete_exception( *(exception_t **)except ); 245 } 246 247 static void __cfaehm_cleanup_default( exception_t ** except ) { 248 __cfaehm_delete_exception( *except ); 249 *except = NULL; 196 struct _Unwind_Context * context, 197 void * some_param) { 198 if( actions & _UA_END_OF_STACK ) exit(1); 199 if( actions & _UA_CLEANUP_PHASE ) return _URC_NO_REASON; 200 201 return _URC_FATAL_PHASE2_ERROR; 250 202 } 251 203 252 204 // The exception that is being thrown must already be stored. 253 static void __cfaehm_begin_unwind(void(*defaultHandler)(exception_t *)) { 254 struct exception_context_t * context = this_exception_context(); 255 if ( NULL == context->current_exception ) { 205 __attribute__((noreturn)) void __cfaabi_ehm__begin_unwind(void) { 206 if ( ! this_exception_context()->current_exception ) { 256 207 printf("UNWIND ERROR missing exception in begin unwind\n"); 257 208 abort(); 258 209 } 259 struct _Unwind_Exception * storage = 260 &EXCEPT_TO_NODE(context->current_exception)->unwind_exception; 210 261 211 262 212 // Call stdlibc to raise the exception 263 __cfadbg_print_safe(exception, "Begin unwinding (storage &p, context %p)\n", storage, context); 264 _Unwind_Reason_Code ret = _Unwind_RaiseException( storage ); 213 _Unwind_Reason_Code ret = _Unwind_RaiseException( &this_exception_storage ); 265 214 266 215 // If we reach here it means something happened. For resumption to work we need to find a way … … 271 220 // the whole stack. 272 221 222 if( ret == _URC_END_OF_STACK ) { 223 // No proper handler was found. This can be handled in many ways, C++ calls std::terminate. 224 // Here we force unwind the stack, basically raising a cancellation. 225 printf("Uncaught exception %p\n", &this_exception_storage); 226 227 ret = _Unwind_ForcedUnwind( &this_exception_storage, _Stop_Fn, (void*)0x22 ); 228 printf("UNWIND ERROR %d after force unwind\n", ret); 229 abort(); 230 } 231 273 232 // We did not simply reach the end of the stack without finding a handler. This is an error. 274 if ( ret != _URC_END_OF_STACK ) { 275 printf("UNWIND ERROR %d after raise exception\n", ret); 276 abort(); 277 } 278 279 // No handler found, go to the default operation. 280 __cfadbg_print_safe(exception, "Uncaught exception %p\n", storage); 281 282 __attribute__((cleanup(__cfaehm_cleanup_default))) 283 exception_t * exception = context->current_exception; 284 defaultHandler( exception ); 285 } 286 287 void __cfaehm_throw_terminate( exception_t * val, void (*defaultHandler)(exception_t *) ) { 288 __cfadbg_print_safe(exception, "Throwing termination exception\n"); 289 290 __cfaehm_allocate_exception( val ); 291 __cfaehm_begin_unwind( defaultHandler ); 292 } 293 294 static __attribute__((noreturn)) void __cfaehm_rethrow_adapter( exception_t * except ) { 295 // TODO: Print some error message. 296 (void)except; 233 printf("UNWIND ERROR %d after raise exception\n", ret); 297 234 abort(); 298 235 } 299 236 300 void __cfaehm_rethrow_terminate(void) { 301 __cfadbg_print_safe(exception, "Rethrowing termination exception\n"); 302 303 __cfaehm_begin_unwind( __cfaehm_rethrow_adapter ); 237 void __cfaabi_ehm__throw_terminate( exception_t * val ) { 238 __cfaabi_dbg_print_safe("Throwing termination exception\n"); 239 240 __cfaabi_ehm__allocate_exception( val ); 241 __cfaabi_ehm__begin_unwind(); 242 } 243 244 void __cfaabi_ehm__rethrow_terminate(void) { 245 __cfaabi_dbg_print_safe("Rethrowing termination exception\n"); 246 247 __cfaabi_ehm__begin_unwind(); 248 } 249 250 #if defined(PIC) 251 #warning Exceptions not yet supported when using Position-Independent Code 252 __attribute__((noinline)) 253 void __cfaabi_ehm__try_terminate(void (*try_block)(), 254 void (*catch_block)(int index, exception_t * except), 255 __attribute__((unused)) int (*match_block)(exception_t * except)) { 304 256 abort(); 305 257 } 306 307 #if defined( __x86_64 ) || defined( __i386 ) 258 #else // PIC 308 259 // This is our personality routine. For every stack frame annotated with 309 260 // ".cfi_personality 0x3,__gcfa_personality_v0" this function will be called twice when unwinding. 310 261 // Once in the search phase and once in the cleanup phase. 311 _Unwind_Reason_Code __gcfa_personality_v0( 312 int version, 313 _Unwind_Action actions, 314 unsigned long long exception_class, 315 struct _Unwind_Exception * unwind_exception, 316 struct _Unwind_Context * unwind_context) 262 _Unwind_Reason_Code __gcfa_personality_v0 ( 263 int version, _Unwind_Action actions, unsigned long long exceptionClass, 264 struct _Unwind_Exception* unwind_exception, 265 struct _Unwind_Context* context) 317 266 { 318 267 319 //__cfadbg_print_safe(exception, "CFA: 0x%lx\n", _Unwind_GetCFA(context)); 320 __cfadbg_print_safe(exception, "Personality function (%d, %x, %llu, %p, %p):", 321 version, actions, exception_class, unwind_exception, unwind_context); 322 323 // Verify that actions follow the rules we expect. 324 // This function should never be called at the end of the stack. 325 verify(!(actions & _UA_END_OF_STACK)); 326 // Either only the search phase flag is set or... 268 //__cfaabi_dbg_print_safe("CFA: 0x%lx\n", _Unwind_GetCFA(context)); 269 __cfaabi_dbg_print_safe("Personality function (%d, %x, %llu, %p, %p):", 270 version, actions, exceptionClass, unwind_exception, context); 271 272 // If we've reached the end of the stack then there is nothing much we can do... 273 if( actions & _UA_END_OF_STACK ) return _URC_END_OF_STACK; 274 327 275 if (actions & _UA_SEARCH_PHASE) { 328 verify(actions == _UA_SEARCH_PHASE); 329 __cfadbg_print_safe(exception, " lookup phase"); 330 // ... we are in clean-up phase. 331 } else { 332 verify(actions & _UA_CLEANUP_PHASE); 333 __cfadbg_print_safe(exception, " cleanup phase"); 334 // We shouldn't be the handler frame during forced unwind. 335 if (actions & _UA_HANDLER_FRAME) { 336 verify(!(actions & _UA_FORCE_UNWIND)); 337 __cfadbg_print_safe(exception, " (handler frame)"); 338 } else if (actions & _UA_FORCE_UNWIND) { 339 __cfadbg_print_safe(exception, " (force unwind)"); 340 } 276 __cfaabi_dbg_print_safe(" lookup phase"); 277 } 278 else if (actions & _UA_CLEANUP_PHASE) { 279 __cfaabi_dbg_print_safe(" cleanup phase"); 280 } 281 // Just in case, probably can't actually happen 282 else { 283 printf(" error\n"); 284 return _URC_FATAL_PHASE1_ERROR; 341 285 } 342 286 343 287 // Get a pointer to the language specific data from which we will read what we need 344 const unsigned char * lsd = _Unwind_GetLanguageSpecificData( unwind_context );345 346 if ( !lsd ) { //Nothing to do, keep unwinding288 const unsigned char * lsd = (const unsigned char*) _Unwind_GetLanguageSpecificData( context ); 289 290 if( !lsd ) { //Nothing to do, keep unwinding 347 291 printf(" no LSD"); 348 292 goto UNWIND; … … 351 295 // Get the instuction pointer and a reading pointer into the exception table 352 296 lsda_header_info lsd_info; 353 const unsigned char * cur_ptr = parse_lsda_header(unwind_context, lsd, &lsd_info); 354 _Unwind_Ptr instruction_ptr = _Unwind_GetIP(unwind_context); 355 356 struct exception_context_t * context = this_exception_context(); 297 const unsigned char * cur_ptr = parse_lsda_header(context, lsd, &lsd_info); 298 _Unwind_Ptr instruction_ptr = _Unwind_GetIP( context ); 357 299 358 300 // Linearly search the table for stuff to do 359 while ( cur_ptr < lsd_info.action_table ) {301 while( cur_ptr < lsd_info.action_table ) { 360 302 _Unwind_Ptr callsite_start; 361 303 _Unwind_Ptr callsite_len; … … 370 312 371 313 // Have we reach the correct frame info yet? 372 if ( lsd_info.Start + callsite_start + callsite_len < instruction_ptr ) {314 if( lsd_info.Start + callsite_start + callsite_len < instruction_ptr ) { 373 315 #ifdef __CFA_DEBUG_PRINT__ 374 316 void * ls = (void*)lsd_info.Start; … … 378 320 void * ep = (void*)lsd_info.Start + callsite_start + callsite_len; 379 321 void * ip = (void*)instruction_ptr; 380 __cfa dbg_print_safe(exception,"\nfound %p - %p (%p, %p, %p), looking for %p\n",322 __cfaabi_dbg_print_safe("\nfound %p - %p (%p, %p, %p), looking for %p\n", 381 323 bp, ep, ls, cs, cl, ip); 382 324 #endif // __CFA_DEBUG_PRINT__ … … 385 327 386 328 // Have we gone too far? 387 if ( lsd_info.Start + callsite_start > instruction_ptr ) {329 if( lsd_info.Start + callsite_start > instruction_ptr ) { 388 330 printf(" gone too far"); 389 331 break; 390 332 } 391 333 392 // Check for what we must do: 393 if ( 0 == callsite_landing_pad ) { 394 // Nothing to do, move along 395 __cfadbg_print_safe(exception, " no landing pad"); 396 } else if (actions & _UA_SEARCH_PHASE) { 397 // In search phase, these means we found a potential handler we must check. 398 399 // We have arbitrarily decided that 0 means nothing to do and 1 means there is 400 // a potential handler. This doesn't seem to conflict the gcc default behavior. 401 if (callsite_action != 0) { 402 // Now we want to run some code to see if the handler matches 403 // This is the tricky part where we want to the power to run arbitrary code 404 // However, generating a new exception table entry and try routine every time 405 // is way more expansive than we might like 406 // The information we have is : 407 // - The GR (Series of registers) 408 // GR1=GP Global Pointer of frame ref by context 409 // - The instruction pointer 410 // - The instruction pointer info (???) 411 // - The CFA (Canonical Frame Address) 412 // - The BSP (Probably the base stack pointer) 413 414 // The current apprach uses one exception table entry per try block 415 _uleb128_t imatcher; 416 // Get the relative offset to the {...}? 417 cur_ptr = read_uleb128(cur_ptr, &imatcher); 418 419 _Unwind_Word match_pos = 420 # if defined( __x86_64 ) 421 _Unwind_GetCFA(unwind_context) + 8; 422 # elif defined( __i386 ) 423 _Unwind_GetCFA(unwind_context) + 24; 424 # elif defined( __ARM_ARCH ) 425 # warning FIX ME: check if anything needed for ARM 426 42; 427 # endif 428 int (*matcher)(exception_t *) = *(int(**)(exception_t *))match_pos; 429 430 int index = matcher(context->current_exception); 431 _Unwind_Reason_Code ret = (0 == index) 432 ? _URC_CONTINUE_UNWIND : _URC_HANDLER_FOUND; 433 UNWIND_TO_NODE(unwind_exception)->handler_index = index; 434 435 // Based on the return value, check if we matched the exception 436 if (ret == _URC_HANDLER_FOUND) { 437 __cfadbg_print_safe(exception, " handler found\n"); 438 } else { 439 // TODO: Continue the search if there is more in the table. 440 __cfadbg_print_safe(exception, " no handler\n"); 334 // Something to do? 335 if( callsite_landing_pad ) { 336 // Which phase are we in 337 if (actions & _UA_SEARCH_PHASE) { 338 // In search phase, these means we found a potential handler we must check. 339 340 // We have arbitrarily decided that 0 means nothing to do and 1 means there is 341 // a potential handler. This doesn't seem to conflict the gcc default behavior. 342 if (callsite_action != 0) { 343 // Now we want to run some code to see if the handler matches 344 // This is the tricky part where we want to the power to run arbitrary code 345 // However, generating a new exception table entry and try routine every time 346 // is way more expansive than we might like 347 // The information we have is : 348 // - The GR (Series of registers) 349 // GR1=GP Global Pointer of frame ref by context 350 // - The instruction pointer 351 // - The instruction pointer info (???) 352 // - The CFA (Canonical Frame Address) 353 // - The BSP (Probably the base stack pointer) 354 355 356 // The current apprach uses one exception table entry per try block 357 _uleb128_t imatcher; 358 // Get the relative offset to the {...}? 359 cur_ptr = read_uleb128(cur_ptr, &imatcher); 360 361 _Unwind_Reason_Code (*matcher)(exception_t *) = 362 MATCHER_FROM_CONTEXT(context); 363 int index = matcher(shared_stack.current_exception); 364 _Unwind_Reason_Code ret = (0 == index) 365 ? _URC_CONTINUE_UNWIND : _URC_HANDLER_FOUND; 366 shared_stack.current_handler_index = index; 367 368 // Based on the return value, check if we matched the exception 369 if( ret == _URC_HANDLER_FOUND) { 370 __cfaabi_dbg_print_safe(" handler found\n"); 371 } else { 372 __cfaabi_dbg_print_safe(" no handler\n"); 373 } 374 return ret; 441 375 } 442 return ret; 376 377 // This is only a cleanup handler, ignore it 378 __cfaabi_dbg_print_safe(" no action"); 443 379 } 444 445 // This is only a cleanup handler, ignore it 446 __cfadbg_print_safe(exception, " no action"); 447 } else { 448 // In clean-up phase, no destructors here but this could be the handler. 449 450 if ( (callsite_action != 0) && !(actions & _UA_HANDLER_FRAME) ){ 451 // If this is a potential exception handler 452 // but not the one that matched the exception in the seach phase, 453 // just ignore it 454 goto UNWIND; 380 else if (actions & _UA_CLEANUP_PHASE) { 381 382 if( (callsite_action != 0) && !(actions & _UA_HANDLER_FRAME) ){ 383 // If this is a potential exception handler 384 // but not the one that matched the exception in the seach phase, 385 // just ignore it 386 goto UNWIND; 387 } 388 389 // We need to run some clean-up or a handler 390 // These statment do the right thing but I don't know any specifics at all 391 _Unwind_SetGR( context, __builtin_eh_return_data_regno(0), (_Unwind_Ptr) unwind_exception ); 392 _Unwind_SetGR( context, __builtin_eh_return_data_regno(1), 0 ); 393 394 // I assume this sets the instruction pointer to the adress of the landing pad 395 // It doesn't actually set it, it only state the value that needs to be set once we return _URC_INSTALL_CONTEXT 396 _Unwind_SetIP( context, ((lsd_info.LPStart) + (callsite_landing_pad)) ); 397 398 __cfaabi_dbg_print_safe(" action\n"); 399 400 // Return have some action to run 401 return _URC_INSTALL_CONTEXT; 455 402 } 456 457 // We need to run some clean-up or a handler458 // These statment do the right thing but I don't know any specifics at all459 _Unwind_SetGR( unwind_context, __builtin_eh_return_data_regno(0),460 (_Unwind_Ptr)unwind_exception );461 _Unwind_SetGR( unwind_context, __builtin_eh_return_data_regno(1), 0 );462 463 // I assume this sets the instruction pointer to the adress of the landing pad464 // It doesn't actually set it, it only state the value that needs to be set once we465 // return _URC_INSTALL_CONTEXT466 _Unwind_SetIP( unwind_context, ((lsd_info.LPStart) + (callsite_landing_pad)) );467 468 __cfadbg_print_safe(exception, " action\n");469 470 // Return have some action to run471 return _URC_INSTALL_CONTEXT;472 403 } 404 405 // Nothing to do, move along 406 __cfaabi_dbg_print_safe(" no landing pad"); 473 407 } 474 408 // No handling found 475 __cfa dbg_print_safe(exception, " table end reached");409 __cfaabi_dbg_print_safe(" table end reached\n"); 476 410 477 411 UNWIND: 478 __cfa dbg_print_safe(exception," unwind\n");412 __cfaabi_dbg_print_safe(" unwind\n"); 479 413 480 414 // Keep unwinding the stack 481 415 return _URC_CONTINUE_UNWIND; 482 416 } 483 484 #pragma GCC push_options485 #pragma GCC optimize(0)486 417 487 418 // Try statements are hoisted out see comments for details. While this could probably be unique 488 419 // and simply linked from libcfa but there is one problem left, see the exception table for details 489 420 __attribute__((noinline)) 490 void __cfa ehm_try_terminate(void (*try_block)(),421 void __cfaabi_ehm__try_terminate(void (*try_block)(), 491 422 void (*catch_block)(int index, exception_t * except), 492 423 __attribute__((unused)) int (*match_block)(exception_t * except)) { … … 494 425 //! printf("%p %p %p %p\n", &try_block, &catch_block, &match_block, &xy); 495 426 427 // Setup statments: These 2 statments won't actually result in any code, they only setup global tables. 428 // However, they clobber gcc cancellation support from gcc. We can replace the personality routine but 429 // replacing the exception table gcc generates is not really doable, it generates labels based on how the 430 // assembly works. 431 496 432 // Setup the personality routine and exception table. 497 // Unforturnately these clobber gcc cancellation support which means we can't get access to498 // the attribute cleanup tables at the same time. We would have to inspect the assembly to499 // create a new set ourselves.500 #ifdef __PIC__501 asm volatile (".cfi_personality 0x9b,CFA.ref.__gcfa_personality_v0");502 asm volatile (".cfi_lsda 0x1b, .LLSDACFA2");503 #else504 433 asm volatile (".cfi_personality 0x3,__gcfa_personality_v0"); 505 434 asm volatile (".cfi_lsda 0x3, .LLSDACFA2"); 506 #endif507 435 508 436 // Label which defines the start of the area for which the handler is setup. … … 522 450 // Label which defines the end of the area for which the handler is setup. 523 451 asm volatile (".TRYEND:"); 524 // Label which defines the start of the exception landing pad. Basically what is called when525 // the exception is caught. Note, if multiple handlers are given, the multiplexing should be526 // done by the generated code, not theexception runtime.452 // Label which defines the start of the exception landing pad. Basically what is called when the exception is 453 // caught. Note, if multiple handlers are given, the multiplexing should be done by the generated code, not the 454 // exception runtime. 527 455 asm volatile (".CATCH:"); 528 456 529 457 // Exception handler 530 // Note: Saving the exception context on the stack breaks termination exceptions. 531 catch_block( EXCEPT_TO_NODE( this_exception_context()->current_exception )->handler_index, 532 this_exception_context()->current_exception ); 458 catch_block( shared_stack.current_handler_index, 459 shared_stack.current_exception ); 533 460 } 534 461 … … 537 464 // have a single call to the try routine. 538 465 539 #ifdef __PIC__ 540 asm ( 541 // HEADER 542 ".LFECFA1:\n" 543 " .globl __gcfa_personality_v0\n" 544 " .section .gcc_except_table,\"a\",@progbits\n" 545 // TABLE HEADER (important field is the BODY length at the end) 546 ".LLSDACFA2:\n" 547 " .byte 0xff\n" 548 " .byte 0xff\n" 549 " .byte 0x1\n" 550 " .uleb128 .LLSDACSECFA2-.LLSDACSBCFA2\n" 551 // BODY (language specific data) 552 // This uses language specific data and can be modified arbitrarily 553 // We use handled area offset, handled area length, 554 // handler landing pad offset and 1 (action code, gcc seems to use 0). 555 ".LLSDACSBCFA2:\n" 556 " .uleb128 .TRYSTART-__cfaehm_try_terminate\n" 557 " .uleb128 .TRYEND-.TRYSTART\n" 558 " .uleb128 .CATCH-__cfaehm_try_terminate\n" 559 " .uleb128 1\n" 560 ".LLSDACSECFA2:\n" 561 // TABLE FOOTER 562 " .text\n" 563 " .size __cfaehm_try_terminate, .-__cfaehm_try_terminate\n" 564 ); 565 566 // Somehow this piece of helps with the resolution of debug symbols. 567 __attribute__((unused)) static const int dummy = 0; 568 569 asm ( 570 // Add a hidden symbol which points at the function. 571 " .hidden CFA.ref.__gcfa_personality_v0\n" 572 " .weak CFA.ref.__gcfa_personality_v0\n" 573 // No clue what this does specifically 574 " .section .data.rel.local.CFA.ref.__gcfa_personality_v0,\"awG\",@progbits,CFA.ref.__gcfa_personality_v0,comdat\n" 575 " .align 8\n" 576 " .type CFA.ref.__gcfa_personality_v0, @object\n" 577 " .size CFA.ref.__gcfa_personality_v0, 8\n" 578 "CFA.ref.__gcfa_personality_v0:\n" 579 #if defined( __x86_64 ) 580 " .quad __gcfa_personality_v0\n" 581 #else // then __i386 582 " .long __gcfa_personality_v0\n" 583 #endif 584 ); 585 #else // __PIC__ 466 #if defined( __i386 ) || defined( __x86_64 ) 586 467 asm ( 587 468 // HEADER … … 598 479 ".LLSDACSBCFA2:\n" 599 480 // Handled area start (relative to start of function) 600 " .uleb128 .TRYSTART-__cfa ehm_try_terminate\n"481 " .uleb128 .TRYSTART-__cfaabi_ehm__try_terminate\n" 601 482 // Handled area length 602 483 " .uleb128 .TRYEND-.TRYSTART\n" 603 484 // Handler landing pad address (relative to start of function) 604 " .uleb128 .CATCH-__cfa ehm_try_terminate\n"485 " .uleb128 .CATCH-__cfaabi_ehm__try_terminate\n" 605 486 // Action code, gcc seems to always use 0. 606 487 " .uleb128 1\n" … … 608 489 ".LLSDACSECFA2:\n" 609 490 " .text\n" 610 " .size __cfa ehm_try_terminate, .-__cfaehm_try_terminate\n"491 " .size __cfaabi_ehm__try_terminate, .-__cfaabi_ehm__try_terminate\n" 611 492 " .ident \"GCC: (Ubuntu 6.2.0-3ubuntu11~16.04) 6.2.0 20160901\"\n" 612 " .section .note.GNU-stack,\"x\",@progbits\n"493 // " .section .note.GNU-stack,\"x\",@progbits\n" 613 494 ); 614 #endif // __PIC__ 615 616 #pragma GCC pop_options 617 618 #elif defined( __ARM_ARCH ) 619 _Unwind_Reason_Code __gcfa_personality_v0( 620 int version, 621 _Unwind_Action actions, 622 unsigned long long exception_class, 623 struct _Unwind_Exception * unwind_exception, 624 struct _Unwind_Context * unwind_context) { 625 return _URC_CONTINUE_UNWIND; 626 } 627 628 __attribute__((noinline)) 629 void __cfaehm_try_terminate(void (*try_block)(), 630 void (*catch_block)(int index, exception_t * except), 631 __attribute__((unused)) int (*match_block)(exception_t * except)) { 632 } 633 #else 634 #error unsupported hardware architecture 635 #endif // __x86_64 || __i386 495 #endif // __i386 || __x86_64 496 #endif // PIC
Note:
See TracChangeset
for help on using the changeset viewer.