Changeset eef8dfb for libcfa/src/exception.c
- Timestamp:
- Jan 7, 2021, 2:55:57 PM (5 years ago)
- Branches:
- ADT, arm-eh, ast-experimental, enum, forall-pointer-decay, jacob/cs343-translation, master, new-ast-unique-expr, pthread-emulation, qualifiedEnum
- Children:
- 58fe85a
- Parents:
- bdfc032 (diff), 44e37ef (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
libcfa/src/exception.c
rbdfc032 reef8dfb 9 9 // Author : Andrew Beach 10 10 // Created On : Mon Jun 26 15:13:00 2017 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : T hu Feb 22 18:17:34 201813 // Update Count : 1111 // Last Modified By : Andrew Beach 12 // Last Modified On : Tue Oct 27 16:27:00 2020 13 // Update Count : 35 14 14 // 15 15 16 // Normally we would get this from the CFA prelude. 16 17 #include <stddef.h> // for size_t 17 18 19 #include <unwind.h> // for struct _Unwind_Exception {...}; 20 18 21 #include "exception.h" 19 20 // Implementation of the secret header.21 22 22 23 #include <stdlib.h> 23 24 #include <stdio.h> 24 #include <unwind.h>25 25 #include <bits/debug.hfa> 26 27 // FIX ME: temporary hack to keep ARM build working 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 28 31 #ifndef _URC_FATAL_PHASE1_ERROR 29 #define _URC_FATAL_PHASE1_ERROR 232 #define _URC_FATAL_PHASE1_ERROR 3 30 33 #endif // ! _URC_FATAL_PHASE1_ERROR 31 34 #ifndef _URC_FATAL_PHASE2_ERROR 32 35 #define _URC_FATAL_PHASE2_ERROR 2 33 36 #endif // ! _URC_FATAL_PHASE2_ERROR 37 #endif // __ARM_ARCH 34 38 35 39 #include "lsda.h" 36 40 41 /* The exception class for our exceptions. Because of the vendor component 42 * its value would not be standard. 43 * Vendor: UWPL 44 * Language: CFA\0 45 */ 46 const _Unwind_Exception_Class __cfaehm_exception_class = 0x4c50575500414643; 37 47 38 48 // Base exception vtable is abstract, you should not have base exceptions. 39 struct __cfa abi_ehm__base_exception_t_vtable40 ___cfa abi_ehm__base_exception_t_vtable_instance = {49 struct __cfaehm_base_exception_t_vtable 50 ___cfaehm_base_exception_t_vtable_instance = { 41 51 .parent = NULL, 42 52 .size = 0, … … 47 57 48 58 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 58 59 // Get the current exception context. 59 60 // There can be a single global until multithreading occurs, then each stack 60 // needs its own. It will have to be updated to handle that. 61 struct exception_context_t * this_exception_context() { 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}; 62 64 return &shared_stack; 63 65 } 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))76 66 77 67 78 68 // RESUMPTION ================================================================ 79 69 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; 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 } 93 89 } 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. 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); 102 95 } 103 96 … … 106 99 // be added after the node is built but before it is made the top node. 107 100 108 void __cfa abi_ehm__try_resume_setup(struct __cfaabi_ehm__try_resume_node * node,101 void __cfaehm_try_resume_setup(struct __cfaehm_try_resume_node * node, 109 102 _Bool (*handler)(exception_t * except)) { 110 node->next = shared_stack.top_resume; 103 struct exception_context_t * context = this_exception_context(); 104 node->next = context->top_resume; 111 105 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 }; 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 ========================================================= 128 116 129 117 #define NODE_TO_EXCEPT(node) ((exception_t *)(1 + (node))) 130 #define EXCEPT_TO_NODE(except) ((struct __cfaabi_ehm__node *)(except) - 1) 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 } 131 135 132 136 // Creates a copy of the indicated exception and sets current_exception to it. 133 static void __cfa abi_ehm__allocate_exception( exception_t * except ) {137 static void __cfaehm_allocate_exception( exception_t * except ) { 134 138 struct exception_context_t * context = this_exception_context(); 135 139 136 140 // Allocate memory for the exception. 137 struct __cfa abi_ehm__node * store = malloc(138 sizeof( struct __cfa abi_ehm__node ) + except->virtual_table->size );141 struct __cfaehm_node * store = malloc( 142 sizeof( struct __cfaehm_node ) + except->virtual_table->size ); 139 143 140 144 if ( ! store ) { … … 143 147 } 144 148 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 145 156 // Add the node to the list: 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 ); 157 store->next = NULL_MAP(EXCEPT_TO_NODE, context->current_exception); 158 context->current_exception = except_store; 151 159 } 152 160 153 161 // Delete the provided exception, unsetting current_exception if relivant. 154 static void __cfa abi_ehm__delete_exception( exception_t * except ) {155 struct exception_context_t * context = this_exception_context(); 156 157 __cfa abi_dbg_print_safe("Deleting Exception\n");162 static void __cfaehm_delete_exception( exception_t * except ) { 163 struct exception_context_t * context = this_exception_context(); 164 165 __cfadbg_print_safe(exception, "Deleting Exception\n"); 158 166 159 167 // Remove the exception from the list. 160 struct __cfa abi_ehm__node * to_free = EXCEPT_TO_NODE(except);161 struct __cfa abi_ehm__node * node;168 struct __cfaehm_node * to_free = EXCEPT_TO_NODE(except); 169 struct __cfaehm_node * node; 162 170 163 171 if ( context->current_exception == except ) { 164 172 node = to_free->next; 165 context->current_exception = (node) ? NODE_TO_EXCEPT(node) : 0;173 context->current_exception = NULL_MAP(NODE_TO_EXCEPT, node); 166 174 } else { 167 175 node = EXCEPT_TO_NODE(context->current_exception); 168 176 // It may always be in the first or second position. 169 while ( to_free != node->next ) {177 while ( to_free != node->next ) { 170 178 node = node->next; 171 179 } … … 178 186 } 179 187 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; 188 // CANCELLATION ============================================================== 188 189 189 190 // Function needed by force unwind … … 192 193 int version, 193 194 _Unwind_Action actions, 194 _Unwind_Exception_Class exception Class,195 _Unwind_Exception_Class exception_class, 195 196 struct _Unwind_Exception * unwind_exception, 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; 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; 202 250 } 203 251 204 252 // The exception that is being thrown must already be stored. 205 __attribute__((noreturn)) void __cfaabi_ehm__begin_unwind(void) { 206 if ( ! this_exception_context()->current_exception ) { 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 ) { 207 256 printf("UNWIND ERROR missing exception in begin unwind\n"); 208 257 abort(); 209 258 } 210 259 struct _Unwind_Exception * storage = 260 &EXCEPT_TO_NODE(context->current_exception)->unwind_exception; 211 261 212 262 // Call stdlibc to raise the exception 213 _Unwind_Reason_Code ret = _Unwind_RaiseException( &this_exception_storage ); 263 __cfadbg_print_safe(exception, "Begin unwinding (storage &p, context %p)\n", storage, context); 264 _Unwind_Reason_Code ret = _Unwind_RaiseException( storage ); 214 265 215 266 // If we reach here it means something happened. For resumption to work we need to find a way … … 220 271 // the whole stack. 221 272 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); 273 // 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); 229 276 abort(); 230 277 } 231 278 232 // We did not simply reach the end of the stack without finding a handler. This is an error. 233 printf("UNWIND ERROR %d after raise exception\n", ret); 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; 234 297 abort(); 235 298 } 236 299 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)) { 300 void __cfaehm_rethrow_terminate(void) { 301 __cfadbg_print_safe(exception, "Rethrowing termination exception\n"); 302 303 __cfaehm_begin_unwind( __cfaehm_rethrow_adapter ); 256 304 abort(); 257 305 } 258 #else // PIC 306 307 #if defined( __x86_64 ) || defined( __i386 ) 259 308 // This is our personality routine. For every stack frame annotated with 260 309 // ".cfi_personality 0x3,__gcfa_personality_v0" this function will be called twice when unwinding. 261 310 // Once in the search phase and once in the cleanup phase. 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) 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) 266 317 { 267 318 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 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... 275 327 if (actions & _UA_SEARCH_PHASE) { 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; 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 } 285 341 } 286 342 287 343 // Get a pointer to the language specific data from which we will read what we need 288 const unsigned char * lsd = (const unsigned char*) _Unwind_GetLanguageSpecificData(context );289 290 if ( !lsd ) { //Nothing to do, keep unwinding344 const unsigned char * lsd = _Unwind_GetLanguageSpecificData( unwind_context ); 345 346 if ( !lsd ) { //Nothing to do, keep unwinding 291 347 printf(" no LSD"); 292 348 goto UNWIND; … … 295 351 // Get the instuction pointer and a reading pointer into the exception table 296 352 lsda_header_info lsd_info; 297 const unsigned char * cur_ptr = parse_lsda_header(context, lsd, &lsd_info); 298 _Unwind_Ptr instruction_ptr = _Unwind_GetIP( context ); 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(); 299 357 300 358 // Linearly search the table for stuff to do 301 while ( cur_ptr < lsd_info.action_table ) {359 while ( cur_ptr < lsd_info.action_table ) { 302 360 _Unwind_Ptr callsite_start; 303 361 _Unwind_Ptr callsite_len; … … 312 370 313 371 // Have we reach the correct frame info yet? 314 if ( lsd_info.Start + callsite_start + callsite_len < instruction_ptr ) {372 if ( lsd_info.Start + callsite_start + callsite_len < instruction_ptr ) { 315 373 #ifdef __CFA_DEBUG_PRINT__ 316 374 void * ls = (void*)lsd_info.Start; … … 320 378 void * ep = (void*)lsd_info.Start + callsite_start + callsite_len; 321 379 void * ip = (void*)instruction_ptr; 322 __cfa abi_dbg_print_safe("\nfound %p - %p (%p, %p, %p), looking for %p\n",380 __cfadbg_print_safe(exception, "\nfound %p - %p (%p, %p, %p), looking for %p\n", 323 381 bp, ep, ls, cs, cl, ip); 324 382 #endif // __CFA_DEBUG_PRINT__ … … 327 385 328 386 // Have we gone too far? 329 if ( lsd_info.Start + callsite_start > instruction_ptr ) {387 if ( lsd_info.Start + callsite_start > instruction_ptr ) { 330 388 printf(" gone too far"); 331 389 break; 332 390 } 333 391 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; 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"); 375 441 } 376 377 // This is only a cleanup handler, ignore it 378 __cfaabi_dbg_print_safe(" no action"); 442 return ret; 379 443 } 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; 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; 402 455 } 456 457 // We need to run some clean-up or a handler 458 // These statment do the right thing but I don't know any specifics at all 459 _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 pad 464 // It doesn't actually set it, it only state the value that needs to be set once we 465 // return _URC_INSTALL_CONTEXT 466 _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 run 471 return _URC_INSTALL_CONTEXT; 403 472 } 404 405 // Nothing to do, move along406 __cfaabi_dbg_print_safe(" no landing pad");407 473 } 408 474 // No handling found 409 __cfa abi_dbg_print_safe(" table end reached\n");475 __cfadbg_print_safe(exception, " table end reached"); 410 476 411 477 UNWIND: 412 __cfa abi_dbg_print_safe(" unwind\n");478 __cfadbg_print_safe(exception, " unwind\n"); 413 479 414 480 // Keep unwinding the stack 415 481 return _URC_CONTINUE_UNWIND; 416 482 } 483 484 #pragma GCC push_options 485 #pragma GCC optimize(0) 417 486 418 487 // Try statements are hoisted out see comments for details. While this could probably be unique 419 488 // and simply linked from libcfa but there is one problem left, see the exception table for details 420 489 __attribute__((noinline)) 421 void __cfa abi_ehm__try_terminate(void (*try_block)(),490 void __cfaehm_try_terminate(void (*try_block)(), 422 491 void (*catch_block)(int index, exception_t * except), 423 492 __attribute__((unused)) int (*match_block)(exception_t * except)) { … … 425 494 //! printf("%p %p %p %p\n", &try_block, &catch_block, &match_block, &xy); 426 495 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 but429 // replacing the exception table gcc generates is not really doable, it generates labels based on how the430 // assembly works.431 432 496 // Setup the personality routine and exception table. 497 // Unforturnately these clobber gcc cancellation support which means we can't get access to 498 // the attribute cleanup tables at the same time. We would have to inspect the assembly to 499 // 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 #else 433 504 asm volatile (".cfi_personality 0x3,__gcfa_personality_v0"); 434 505 asm volatile (".cfi_lsda 0x3, .LLSDACFA2"); 506 #endif 435 507 436 508 // Label which defines the start of the area for which the handler is setup. … … 450 522 // Label which defines the end of the area for which the handler is setup. 451 523 asm volatile (".TRYEND:"); 452 // Label which defines the start of the exception landing pad. Basically what is called when the exception is453 // caught. Note, if multiple handlers are given, the multiplexing should be done by the generated code, not the454 // exception runtime.524 // Label which defines the start of the exception landing pad. Basically what is called when 525 // the exception is caught. Note, if multiple handlers are given, the multiplexing should be 526 // done by the generated code, not the exception runtime. 455 527 asm volatile (".CATCH:"); 456 528 457 529 // Exception handler 458 catch_block( shared_stack.current_handler_index, 459 shared_stack.current_exception ); 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 ); 460 533 } 461 534 … … 464 537 // have a single call to the try routine. 465 538 466 #if defined( __i386 ) || defined( __x86_64 ) 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__ 467 586 asm ( 468 587 // HEADER … … 479 598 ".LLSDACSBCFA2:\n" 480 599 // Handled area start (relative to start of function) 481 " .uleb128 .TRYSTART-__cfa abi_ehm__try_terminate\n"600 " .uleb128 .TRYSTART-__cfaehm_try_terminate\n" 482 601 // Handled area length 483 602 " .uleb128 .TRYEND-.TRYSTART\n" 484 603 // Handler landing pad address (relative to start of function) 485 " .uleb128 .CATCH-__cfa abi_ehm__try_terminate\n"604 " .uleb128 .CATCH-__cfaehm_try_terminate\n" 486 605 // Action code, gcc seems to always use 0. 487 606 " .uleb128 1\n" … … 489 608 ".LLSDACSECFA2:\n" 490 609 " .text\n" 491 " .size __cfa abi_ehm__try_terminate, .-__cfaabi_ehm__try_terminate\n"610 " .size __cfaehm_try_terminate, .-__cfaehm_try_terminate\n" 492 611 " .ident \"GCC: (Ubuntu 6.2.0-3ubuntu11~16.04) 6.2.0 20160901\"\n" 493 //" .section .note.GNU-stack,\"x\",@progbits\n"612 " .section .note.GNU-stack,\"x\",@progbits\n" 494 613 ); 495 #endif // __i386 || __x86_64 496 #endif // PIC 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
Note:
See TracChangeset
for help on using the changeset viewer.