- File:
-
- 1 edited
-
doc/working/exception/impl/exception.c (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
doc/working/exception/impl/exception.c
r6a48cc9 r35dd0f42 22 22 23 23 void __throw_resume(exception except) { 24 25 // DEBUG 26 printf("Throwing resumption exception %d\n", except); 24 void noop() { printf("do nothing\n");} 25 struct __cleanup_hook __blarg __attribute__((cleanup(noop))); 27 26 28 27 struct __try_resume_node * original_head = shared_stack.current_resume; … … 46 45 } 47 46 47 /* __try_resume_node functions: 48 * Currently I was planning to generate this code inline, and even if I don't 49 * putting it in the header so it can be inlined might be worth while. 50 * And if we put them in Cforall code we can use the operators. 51 */ 52 void __try_resume_node_new(struct __try_resume_node * node, 53 _Bool (*handler)(exception except)) { 54 node->next = shared_stack.top_resume; 55 shared_stack.top_resume = node; 56 node->try_to_handle = handler; 57 } 58 59 void __try_resume_node_delete(struct __try_resume_node * node) { 60 shared_stack.top_resume = node->next; 61 } 62 48 63 49 64 // TERMINATION =============================================================== 50 65 51 66 // Requires -fexceptions to work. 52 53 // Global which defines the current exception54 // Currently an int just to make matching easier55 //int this_exception; (became shared_stack.current_exception)56 57 // We need a piece of storage to raise the exception58 struct _Unwind_Exception this_exception_storage;59 60 // Function needed by force unwind61 // It basically says to unwind the whole stack and then exit when we reach the end of the stack62 static _Unwind_Reason_Code _Stop_Fn(63 int version,64 _Unwind_Action actions,65 _Unwind_Exception_Class exceptionClass,66 struct _Unwind_Exception * unwind_exception,67 struct _Unwind_Context * context,68 void * some_param) {69 if( actions & _UA_END_OF_STACK ) exit(1);70 if( actions & _UA_CLEANUP_PHASE ) return _URC_NO_REASON;71 72 return _URC_FATAL_PHASE2_ERROR;73 }74 75 // Example throw routine76 void __throw_terminate( int val ) {77 // Store the current exception78 shared_stack.current_exception = val;79 80 // DEBUG81 printf("Throwing termination exception %d\n", val);82 83 // Call stdlibc to raise the exception84 _Unwind_Reason_Code ret = _Unwind_RaiseException( &this_exception_storage );85 86 // If we reach here it means something happened87 // For resumption to work we need to find a way to return back to here88 // Most of them will probably boil down to setting a global flag and making the phase 1 either stop or fail.89 // Causing an error on purpose may help avoiding unnecessary work but it might have some weird side effects.90 // If we just pretend no handler was found that would work but may be expensive for no reason since we will always91 // search the whole stack92 93 if( ret == _URC_END_OF_STACK ) {94 // No proper handler was found95 // This can be handled in several way96 // C++ calls std::terminate97 // Here we force unwind the stack, basically raising a cancellation98 printf("Uncaught exception %p\n", &this_exception_storage);99 100 ret = _Unwind_ForcedUnwind( &this_exception_storage, _Stop_Fn, (void*)0x22 );101 printf("UNWIND ERROR %d after force unwind\n", ret);102 abort();103 }104 105 // We did not simply reach the end of the stack without finding a handler,106 // Something wen't wrong107 printf("UNWIND ERROR %d after raise exception\n", ret);108 abort();109 }110 111 // This is our personality routine112 // For every stack frame anotated with ".cfi_personality 0x3,__gcfa_personality_v0"113 // This function will be called twice when unwinding114 // Once in the search phased and once in the cleanup phase115 _Unwind_Reason_Code __gcfa_personality_v0 (116 int version, _Unwind_Action actions, unsigned long long exceptionClass,117 struct _Unwind_Exception* unwind_exception,118 struct _Unwind_Context* context)119 {120 121 // DEBUG122 //printf("CFA: 0x%lx\n", _Unwind_GetCFA(context));123 printf("Personality function (%d, %x, %llu, %p, %p):", version, actions, exceptionClass, unwind_exception, context);124 125 // If we've reached the end of the stack then there is nothing much we can do...126 if( actions & _UA_END_OF_STACK ) return _URC_END_OF_STACK;127 128 // DEBUG129 if (actions & _UA_SEARCH_PHASE) {130 printf(" lookup phase");131 }132 // DEBUG133 else if (actions & _UA_CLEANUP_PHASE) {134 printf(" cleanup phase");135 }136 // Just in case, probably can't actually happen137 else {138 printf(" error\n");139 return _URC_FATAL_PHASE1_ERROR;140 }141 142 // Get a pointer to the language specific data from which we will read what we need143 const unsigned char * lsd = (const unsigned char*) _Unwind_GetLanguageSpecificData( context );144 145 if( !lsd ) { //Nothing to do, keep unwinding146 printf(" no LSD");147 goto UNWIND;148 }149 150 // Get the instuction pointer and a reading pointer into the exception table151 lsda_header_info lsd_info;152 const unsigned char * cur_ptr = parse_lsda_header( context, lsd, &lsd_info);153 _Unwind_Ptr instruction_ptr = _Unwind_GetIP( context );154 155 // Linearly search the table for stuff to do156 while( cur_ptr < lsd_info.action_table ) {157 _Unwind_Ptr callsite_start;158 _Unwind_Ptr callsite_len;159 _Unwind_Ptr callsite_landing_pad;160 _uleb128_t callsite_action;161 162 // Decode the common stuff we have in here163 cur_ptr = read_encoded_value (0, lsd_info.call_site_encoding, cur_ptr, &callsite_start);164 cur_ptr = read_encoded_value (0, lsd_info.call_site_encoding, cur_ptr, &callsite_len);165 cur_ptr = read_encoded_value (0, lsd_info.call_site_encoding, cur_ptr, &callsite_landing_pad);166 cur_ptr = read_uleb128 (cur_ptr, &callsite_action);167 168 // Have we reach the correct frame info yet?169 if( lsd_info.Start + callsite_start + callsite_len < instruction_ptr ) {170 //DEBUG BEGIN171 void * ls = (void*)lsd_info.Start;172 void * cs = (void*)callsite_start;173 void * cl = (void*)callsite_len;174 void * bp = (void*)lsd_info.Start + callsite_start;175 void * ep = (void*)lsd_info.Start + callsite_start + callsite_len;176 void * ip = (void*)instruction_ptr;177 printf("\nfound %p - %p (%p, %p, %p), looking for %p\n", bp, ep, ls, cs, cl, ip);178 //DEBUG END179 continue;180 }181 182 // Have we gone too far183 if( lsd_info.Start + callsite_start > instruction_ptr ) {184 printf(" gone too far");185 break;186 }187 188 // Something to do?189 if( callsite_landing_pad ) {190 // Which phase are we in191 if (actions & _UA_SEARCH_PHASE) {192 // Search phase, this means we probably found a potential handler and must check if it is a match193 194 // If we have arbitrarily decided that 0 means nothing to do and 1 means there is a potential handler195 // This doesn't seem to conflict the gcc default behavior196 if (callsite_action != 0) {197 // Now we want to run some code to see if the handler matches198 // This is the tricky part where we want to the power to run arbitrary code199 // However, generating a new exception table entry and try routine every time200 // is way more expansive than we might like201 // The information we have is :202 // - The GR (Series of registers)203 // GR1=GP Global Pointer of frame ref by context204 // - The instruction pointer205 // - The instruction pointer info (???)206 // - The CFA (Canonical Frame Address)207 // - The BSP (Probably the base stack pointer)208 209 210 // The current apprach uses one exception table entry per try block211 _uleb128_t imatcher;212 // Get the relative offset to the213 cur_ptr = read_uleb128 (cur_ptr, &imatcher);214 215 // Get a function pointer from the relative offset and call it216 // _Unwind_Reason_Code (*matcher)() = (_Unwind_Reason_Code (*)())lsd_info.LPStart + imatcher;217 218 _Unwind_Reason_Code (*matcher)() =219 MATCHER_FROM_CONTEXT(context);220 int index = matcher(shared_stack.current_exception);221 _Unwind_Reason_Code ret = (0 == index)222 ? _URC_CONTINUE_UNWIND : _URC_HANDLER_FOUND;223 shared_stack.current_handler_index = index;224 225 // Based on the return value, check if we matched the exception226 if( ret == _URC_HANDLER_FOUND) printf(" handler found\n");227 else printf(" no handler\n");228 return ret;229 }230 231 // This is only a cleanup handler, ignore it232 printf(" no action");233 }234 else if (actions & _UA_CLEANUP_PHASE) {235 236 if( (callsite_action != 0) && !(actions & _UA_HANDLER_FRAME) ){237 // If this is a potential exception handler238 // but not the one that matched the exception in the seach phase,239 // just ignore it240 goto UNWIND;241 }242 243 // We need to run some clean-up or a handler244 // These statment do the right thing but I don't know any specifics at all245 _Unwind_SetGR( context, __builtin_eh_return_data_regno(0), (_Unwind_Ptr) unwind_exception );246 _Unwind_SetGR( context, __builtin_eh_return_data_regno(1), 0 );247 248 // I assume this sets the instruction pointer to the adress of the landing pad249 // It doesn't actually set it, it only state the value that needs to be set once we return _URC_INSTALL_CONTEXT250 _Unwind_SetIP( context, lsd_info.LPStart + callsite_landing_pad );251 252 // DEBUG253 printf(" action\n");254 255 // Return have some action to run256 return _URC_INSTALL_CONTEXT;257 }258 }259 260 // Nothing to do, move along261 printf(" no landing pad");262 }263 // No handling found264 printf(" table end reached\n");265 266 // DEBUG267 UNWIND:268 printf(" unwind\n");269 270 // Keep unwinding the stack271 return _URC_CONTINUE_UNWIND;272 }273 67 274 68 // Try statements are hoisted out see comments for details … … 347 141 " .size __try_terminate, .-__try_terminate\n" 348 142 " .ident \"GCC: (Ubuntu 6.2.0-3ubuntu11~16.04) 6.2.0 20160901\"\n" 349 //" .section .note.GNU-stack,\"x\",@progbits\n"143 " .section .note.GNU-stack,\"x\",@progbits\n" 350 144 ); 145 146 // Global which defines the current exception 147 // Currently an int just to make matching easier 148 int this_exception; 149 150 // We need a piece of storage to raise the exception 151 struct _Unwind_Exception this_exception_storage; 152 153 // Function needed by force unwind 154 // It basically says to unwind the whole stack and then exit when we reach the end of the stack 155 static _Unwind_Reason_Code _Stop_Fn( 156 int version, 157 _Unwind_Action actions, 158 _Unwind_Exception_Class exceptionClass, 159 struct _Unwind_Exception * unwind_exception, 160 struct _Unwind_Context * context, 161 void * some_param) { 162 if( actions & _UA_END_OF_STACK ) exit(1); 163 if( actions & _UA_CLEANUP_PHASE ) return _URC_NO_REASON; 164 165 return _URC_FATAL_PHASE2_ERROR; 166 } 167 168 // Example throw routine 169 void __throw_terminate( int val ) { 170 // Store the current exception 171 this_exception = val; 172 173 // DEBUG 174 printf("Throwing exception %d\n", this_exception); 175 176 // Call stdlibc to raise the exception 177 _Unwind_Reason_Code ret = _Unwind_RaiseException( &this_exception_storage ); 178 179 // If we reach here it means something happened 180 // For resumption to work we need to find a way to return back to here 181 // Most of them will probably boil down to setting a global flag and making the phase 1 either stop or fail. 182 // Causing an error on purpose may help avoiding unnecessary work but it might have some weird side effects. 183 // If we just pretend no handler was found that would work but may be expensive for no reason since we will always 184 // search the whole stack 185 186 if( ret == _URC_END_OF_STACK ) { 187 // No proper handler was found 188 // This can be handled in several way 189 // C++ calls std::terminate 190 // Here we force unwind the stack, basically raising a cancellation 191 printf("Uncaught exception %p\n", &this_exception_storage); 192 193 ret = _Unwind_ForcedUnwind( &this_exception_storage, _Stop_Fn, (void*)0x22 ); 194 printf("UNWIND ERROR %d after force unwind\n", ret); 195 abort(); 196 } 197 198 // We did not simply reach the end of the stack without finding a handler, 199 // Something wen't wrong 200 printf("UNWIND ERROR %d after raise exception\n", ret); 201 abort(); 202 } 203 204 // This is our personality routine 205 // For every stack frame anotated with ".cfi_personality 0x3,__gcfa_personality_v0" 206 // This function will be called twice when unwinding 207 // Once in the search phased and once in the cleanup phase 208 _Unwind_Reason_Code __gcfa_personality_v0 ( 209 int version, _Unwind_Action actions, unsigned long long exceptionClass, 210 struct _Unwind_Exception* unwind_exception, 211 struct _Unwind_Context* context) 212 { 213 printf("CFA: 0x%lx\n", _Unwind_GetCFA(context)); 214 215 // DEBUG 216 printf("Personality function (%d, %x, %llu, %p, %p):", version, actions, exceptionClass, unwind_exception, context); 217 218 // If we've reached the end of the stack then there is nothing much we can do... 219 if( actions & _UA_END_OF_STACK ) return _URC_END_OF_STACK; 220 221 // DEBUG 222 if (actions & _UA_SEARCH_PHASE) { 223 printf(" lookup phase"); 224 } 225 // DEBUG 226 else if (actions & _UA_CLEANUP_PHASE) { 227 printf(" cleanup phase"); 228 } 229 // Just in case, probably can't actually happen 230 else { 231 printf(" error\n"); 232 return _URC_FATAL_PHASE1_ERROR; 233 } 234 235 // Get a pointer to the language specific data from which we will read what we need 236 const unsigned char * lsd = (const unsigned char*) _Unwind_GetLanguageSpecificData( context ); 237 238 if( !lsd ) { //Nothing to do, keep unwinding 239 printf(" no LSD"); 240 goto UNWIND; 241 } 242 243 // Get the instuction pointer and a reading pointer into the exception table 244 lsda_header_info lsd_info; 245 const unsigned char * cur_ptr = parse_lsda_header( context, lsd, &lsd_info); 246 _Unwind_Ptr instruction_ptr = _Unwind_GetIP( context ); 247 248 // Linearly search the table for stuff to do 249 while( cur_ptr < lsd_info.action_table ) { 250 _Unwind_Ptr callsite_start; 251 _Unwind_Ptr callsite_len; 252 _Unwind_Ptr callsite_landing_pad; 253 _uleb128_t callsite_action; 254 255 // Decode the common stuff we have in here 256 cur_ptr = read_encoded_value (0, lsd_info.call_site_encoding, cur_ptr, &callsite_start); 257 cur_ptr = read_encoded_value (0, lsd_info.call_site_encoding, cur_ptr, &callsite_len); 258 cur_ptr = read_encoded_value (0, lsd_info.call_site_encoding, cur_ptr, &callsite_landing_pad); 259 cur_ptr = read_uleb128 (cur_ptr, &callsite_action); 260 261 // Have we reach the correct frame info yet? 262 if( lsd_info.Start + callsite_start + callsite_len < instruction_ptr ) { 263 //DEBUG BEGIN 264 void * ls = (void*)lsd_info.Start; 265 void * cs = (void*)callsite_start; 266 void * cl = (void*)callsite_len; 267 void * bp = (void*)lsd_info.Start + callsite_start; 268 void * ep = (void*)lsd_info.Start + callsite_start + callsite_len; 269 void * ip = (void*)instruction_ptr; 270 printf("\nfound %p - %p (%p, %p, %p), looking for %p\n", bp, ep, ls, cs, cl, ip); 271 //DEBUG END 272 continue; 273 } 274 275 // Have we gone too far 276 if( lsd_info.Start + callsite_start > instruction_ptr ) { 277 printf(" gone too far"); 278 break; 279 } 280 281 // Something to do? 282 if( callsite_landing_pad ) { 283 // Which phase are we in 284 if (actions & _UA_SEARCH_PHASE) { 285 // Search phase, this means we probably found a potential handler and must check if it is a match 286 287 // If we have arbitrarily decided that 0 means nothing to do and 1 means there is a potential handler 288 // This doesn't seem to conflict the gcc default behavior 289 if (callsite_action != 0) { 290 // Now we want to run some code to see if the handler matches 291 // This is the tricky part where we want to the power to run arbitrary code 292 // However, generating a new exception table entry and try routine every time 293 // is way more expansive than we might like 294 // The information we have is : 295 // - The GR (Series of registers) 296 // GR1=GP Global Pointer of frame ref by context 297 // - The instruction pointer 298 // - The instruction pointer info (???) 299 // - The CFA (Canonical Frame Address) 300 // - The BSP (Probably the base stack pointer) 301 302 303 // The current apprach uses one exception table entry per try block 304 _uleb128_t imatcher; 305 // Get the relative offset to the 306 cur_ptr = read_uleb128 (cur_ptr, &imatcher); 307 308 // Get a function pointer from the relative offset and call it 309 // _Unwind_Reason_Code (*matcher)() = (_Unwind_Reason_Code (*)())lsd_info.LPStart + imatcher; 310 311 _Unwind_Reason_Code (*matcher)() = 312 MATCHER_FROM_CONTEXT(context); 313 int index = matcher(shared_stack.current_exception); 314 _Unwind_Reason_Code ret = (0 == index) 315 ? _URC_CONTINUE_UNWIND : _URC_HANDLER_FOUND; 316 shared_stack.current_handler_index = index; 317 318 319 // Based on the return value, check if we matched the exception 320 if( ret == _URC_HANDLER_FOUND) printf(" handler found\n"); 321 else printf(" no handler\n"); 322 return ret; 323 } 324 325 // This is only a cleanup handler, ignore it 326 printf(" no action"); 327 } 328 else if (actions & _UA_CLEANUP_PHASE) { 329 330 if( (callsite_action != 0) && !(actions & _UA_HANDLER_FRAME) ){ 331 // If this is a potential exception handler 332 // but not the one that matched the exception in the seach phase, 333 // just ignore it 334 goto UNWIND; 335 } 336 337 // We need to run some clean-up or a handler 338 // These statment do the right thing but I don't know any specifics at all 339 _Unwind_SetGR( context, __builtin_eh_return_data_regno(0), (_Unwind_Ptr) unwind_exception ); 340 _Unwind_SetGR( context, __builtin_eh_return_data_regno(1), 0 ); 341 342 // I assume this sets the instruction pointer to the adress of the landing pad 343 // It doesn't actually set it, it only state the value that needs to be set once we return _URC_INSTALL_CONTEXT 344 _Unwind_SetIP( context, lsd_info.LPStart + callsite_landing_pad ); 345 346 // DEBUG 347 printf(" action\n"); 348 349 // Return have some action to run 350 return _URC_INSTALL_CONTEXT; 351 } 352 } 353 354 // Nothing to do, move along 355 printf(" no landing pad"); 356 } 357 // No handling found 358 printf(" table end reached\n"); 359 360 // DEBUG 361 UNWIND: 362 printf(" unwind\n"); 363 364 // Keep unwinding the stack 365 return _URC_CONTINUE_UNWIND; 366 }
Note:
See TracChangeset
for help on using the changeset viewer.