1 /** 2 * Exception handling support for Dwarf-style portable exceptions. 3 * 4 * Copyright: Copyright (c) 2015-2016 by D Language Foundation 5 * License: Distributed under the 6 * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). 7 * (See accompanying file LICENSE) 8 * Authors: Walter Bright 9 * Source: $(DRUNTIMESRC rt/_dwarfeh.d) 10 */ 11 12 module rt.dwarfeh; 13 14 // debug = EH_personality; 15 16 version (Posix): 17 18 import rt.dmain2: _d_print_throwable; 19 import core.internal.backtrace.unwind; 20 import core.stdc.stdio; 21 import core.stdc.stdlib; 22 23 /* These are the register numbers for _Unwind_SetGR(). 24 * Hints for these can be found by looking at the EH_RETURN_DATA_REGNO macro in 25 * GCC. If you have a native gcc you can try the following: 26 * 27 * #include <stdio.h> 28 * 29 * int main(int argc, char *argv[]) 30 * { 31 * printf("EH_RETURN_DATA_REGNO(0) = %d\n", __builtin_eh_return_data_regno(0)); 32 * printf("EH_RETURN_DATA_REGNO(1) = %d\n", __builtin_eh_return_data_regno(1)); 33 * return 0; 34 * } 35 */ 36 version (X86_64) 37 { 38 enum eh_exception_regno = 0; 39 enum eh_selector_regno = 1; 40 } 41 else version (X86) 42 { 43 enum eh_exception_regno = 0; 44 enum eh_selector_regno = 2; 45 } 46 else version (AArch64) 47 { 48 enum eh_exception_regno = 0; 49 enum eh_selector_regno = 1; 50 } 51 else version (ARM) 52 { 53 enum eh_exception_regno = 0; 54 enum eh_selector_regno = 1; 55 } 56 else version (PPC64) 57 { 58 enum eh_exception_regno = 3; 59 enum eh_selector_regno = 4; 60 } 61 else version (PPC) 62 { 63 enum eh_exception_regno = 3; 64 enum eh_selector_regno = 4; 65 } 66 else version (MIPS64) 67 { 68 enum eh_exception_regno = 4; 69 enum eh_selector_regno = 5; 70 } 71 else version (MIPS32) 72 { 73 enum eh_exception_regno = 4; 74 enum eh_selector_regno = 5; 75 } 76 else version (RISCV64) 77 { 78 enum eh_exception_regno = 10; 79 enum eh_selector_regno = 11; 80 } 81 else version (RISCV32) 82 { 83 enum eh_exception_regno = 10; 84 enum eh_selector_regno = 11; 85 } 86 else 87 { 88 static assert(0, "Unknown EH register numbers for this architecture"); 89 } 90 91 extern (C) 92 { 93 int _d_isbaseof(ClassInfo b, ClassInfo c); 94 void _d_createTrace(Throwable o, void* context); 95 } 96 97 private _Unwind_Ptr readUnaligned(T, bool consume)(ref const(ubyte)* p) 98 { 99 version (X86) enum hasUnalignedLoads = true; 100 else version (X86_64) enum hasUnalignedLoads = true; 101 else enum hasUnalignedLoads = false; 102 103 static if (hasUnalignedLoads) 104 { 105 T value = *cast(T*) p; 106 } 107 else 108 { 109 import core.stdc.string : memcpy; 110 T value = void; 111 memcpy(&value, p, T.sizeof); 112 } 113 114 static if (consume) 115 p += T.sizeof; 116 return cast(_Unwind_Ptr) value; 117 } 118 119 debug (EH_personality) 120 { 121 private void writeln(in char* format, ...) @nogc nothrow 122 { 123 import core.stdc.stdarg; 124 125 va_list args; 126 va_start(args, format); 127 vfprintf(stdout, format, args); 128 fprintf(stdout, "\n"); 129 fflush(stdout); 130 } 131 } 132 133 /* High 4 bytes = vendor, low 4 bytes = language 134 * For us: "DMD\0D\0\0\0" 135 */ 136 enum _Unwind_Exception_Class dmdExceptionClass = 137 (cast(_Unwind_Exception_Class)'D' << 56) | 138 (cast(_Unwind_Exception_Class)'M' << 48) | 139 (cast(_Unwind_Exception_Class)'D' << 40) | 140 (cast(_Unwind_Exception_Class)'D' << 24); 141 142 /** 143 * Wrap the unwinder's data with our own compiler specific struct 144 * with our own data. 145 */ 146 struct ExceptionHeader 147 { 148 Throwable object; // the thrown D object 149 _Unwind_Exception exception_object; // the unwinder's data 150 151 // Save info on the handler that was detected 152 int handler; // which catch 153 const(ubyte)* languageSpecificData; // Language Specific Data Area for function enclosing the handler 154 _Unwind_Ptr landingPad; // pointer to catch code 155 156 // Stack other thrown exceptions in current thread through here. 157 ExceptionHeader* next; 158 159 static ExceptionHeader* stack; // thread local stack of chained exceptions 160 161 /* Pre-allocate storage for 1 instance per thread. 162 * Use calloc/free for multiple exceptions in flight. 163 * Does not use GC 164 */ 165 static ExceptionHeader ehstorage; 166 167 /************ 168 * Allocate and initialize an ExceptionHeader. 169 * Params: 170 * o = thrown object 171 * Returns: 172 * allocated and initalized ExceptionHeader 173 */ 174 static ExceptionHeader* create(Throwable o) @nogc 175 { 176 auto eh = &ehstorage; 177 if (eh.object) // if in use 178 { 179 eh = cast(ExceptionHeader*)core.stdc.stdlib.calloc(ExceptionHeader.sizeof, 1); 180 if (!eh) 181 terminate(__LINE__); // out of memory while throwing - not much else can be done 182 } 183 eh.object = o; 184 eh.exception_object.exception_class = dmdExceptionClass; 185 debug (EH_personality) writeln("create(): %p", eh); 186 return eh; 187 } 188 189 /********************** 190 * Free ExceptionHeader that was created by create(). 191 * Params: 192 * eh = ExceptionHeader to free 193 */ 194 static void free(ExceptionHeader* eh) 195 { 196 debug (EH_personality) writeln("free(%p)", eh); 197 /* Smite contents even if subsequently free'd, 198 * to ward off dangling pointer bugs. 199 */ 200 *eh = ExceptionHeader.init; 201 if (eh != &ehstorage) 202 core.stdc.stdlib.free(eh); 203 } 204 205 /************************* 206 * Push this onto stack of chained exceptions. 207 */ 208 void push() 209 { 210 next = stack; 211 stack = &this; 212 } 213 214 /************************ 215 * Pop and return top of chained exception stack. 216 */ 217 static ExceptionHeader* pop() 218 { 219 auto eh = stack; 220 stack = eh.next; 221 return eh; 222 } 223 224 /******************************* 225 * Convert from pointer to exception_object to pointer to ExceptionHeader 226 * that it is embedded inside of. 227 * Params: 228 * eo = pointer to exception_object field 229 * Returns: 230 * pointer to ExceptionHeader that eo points into. 231 */ 232 static ExceptionHeader* toExceptionHeader(_Unwind_Exception* eo) 233 { 234 return cast(ExceptionHeader*)(cast(void*)eo - ExceptionHeader.exception_object.offsetof); 235 } 236 } 237 238 /******************************************* 239 * The first thing a catch handler does is call this. 240 * Params: 241 * exceptionObject = value passed to catch handler by unwinder 242 * Returns: 243 * object that was caught 244 */ 245 extern(C) Throwable __dmd_begin_catch(_Unwind_Exception* exceptionObject) 246 { 247 ExceptionHeader *eh = ExceptionHeader.toExceptionHeader(exceptionObject); 248 debug (EH_personality) writeln("__dmd_begin_catch(%p), object = %p", eh, eh.object); 249 250 auto o = eh.object; 251 // Remove our reference to the exception. We should not decrease its refcount, 252 // because we pass the object on to the caller. 253 eh.object = null; 254 255 // Pop off of chain 256 if (eh != ExceptionHeader.pop()) 257 terminate(__LINE__); // eh should have been at top of stack 258 259 _Unwind_DeleteException(&eh.exception_object); // done with eh 260 return o; 261 } 262 263 /**************************************** 264 * Called when fibers switch contexts. 265 * Params: 266 * newContext = stack to switch to 267 * Returns: 268 * previous value of stack 269 */ 270 extern(C) void* _d_eh_swapContextDwarf(void* newContext) nothrow @nogc 271 { 272 auto old = ExceptionHeader.stack; 273 ExceptionHeader.stack = cast(ExceptionHeader*)newContext; 274 return old; 275 } 276 277 278 /********************* 279 * Called by D code to throw an exception via 280 * --- 281 * throw o; 282 * --- 283 * Params: 284 * o = Object to throw 285 * Returns: 286 * doesn't return 287 */ 288 extern(C) void _d_throwdwarf(Throwable o) 289 { 290 ExceptionHeader *eh = ExceptionHeader.create(o); 291 292 eh.push(); // add to thrown exception stack 293 debug (EH_personality) writeln("_d_throwdwarf: eh = %p, eh.next = %p", eh, eh.next); 294 295 /* Increment reference count if `o` is a refcounted Throwable 296 */ 297 auto refcount = o.refcount(); 298 if (refcount) // non-zero means it's refcounted 299 o.refcount() = refcount + 1; 300 301 /* Called by unwinder when exception object needs destruction by other than our code. 302 */ 303 extern (C) static void exception_cleanup(_Unwind_Reason_Code reason, _Unwind_Exception* eo) 304 { 305 debug (EH_personality) writeln("exception_cleanup()"); 306 switch (reason) 307 { 308 case _URC_FATAL_PHASE1_ERROR: // unknown error code 309 case _URC_FATAL_PHASE2_ERROR: // probably corruption 310 default: // uh-oh 311 terminate(__LINE__); // C++ calls terminate() instead 312 break; 313 314 case _URC_FOREIGN_EXCEPTION_CAUGHT: 315 case _URC_NO_REASON: 316 auto eh = ExceptionHeader.toExceptionHeader(eo); 317 ExceptionHeader.free(eh); 318 break; 319 } 320 321 } 322 323 eh.exception_object.exception_cleanup = &exception_cleanup; 324 325 _d_createTrace(o, null); 326 327 auto r = _Unwind_RaiseException(&eh.exception_object); 328 329 /* Shouldn't have returned, but if it did: 330 */ 331 switch (r) 332 { 333 case _URC_END_OF_STACK: 334 /* Unwound the stack without encountering a catch clause. 335 * In C++, this would mean call uncaught_exception(). 336 * In D, this can happen only if `rt_trapExceptions` is cleared 337 * since otherwise everything is enclosed by a top-level 338 * try/catch. 339 */ 340 fprintf(stderr, "%s:%d: uncaught exception reached top of stack\n", __FILE__.ptr, __LINE__); 341 fprintf(stderr, "This might happen if you're missing a top level catch in your fiber or signal handler\n"); 342 /** 343 As _d_print_throwable() itself may throw multiple times when calling core.demangle, 344 and with the uncaught exception still on the EH stack, this doesn't bode well with core.demangle's error recovery. 345 */ 346 __dmd_begin_catch(&eh.exception_object); 347 _d_print_throwable(o); 348 abort(); 349 assert(0); 350 351 case _URC_FATAL_PHASE1_ERROR: 352 /* Unexpected error, likely some sort of corruption. 353 * In C++, terminate() would be called. 354 */ 355 terminate(__LINE__); // should never happen 356 assert(0); 357 358 case _URC_FATAL_PHASE2_ERROR: 359 /* Unexpected error. Program is in an unknown state. 360 * In C++, terminate() would be called. 361 */ 362 terminate(__LINE__); // should never happen 363 assert(0); 364 365 default: 366 terminate(__LINE__); // should never happen 367 assert(0); 368 } 369 } 370 371 372 /***************************************** 373 * "personality" function, specific to each language. 374 * This one, of course, is specific to DMD. 375 * Params: 376 * ver = version must be 1 377 * actions = bitwise OR of the 4 actions _UA_xxx. 378 * _UA_SEARCH_PHASE means return _URC_HANDLER_FOUND if current frame has a handler, 379 * _URC_CONTINUE_UNWIND if not. Cannot be used with _UA_CLEANUP_PHASE. 380 * _UA_CLEANUP_PHASE means perform cleanup for current frame by calling nested functions 381 * and returning _URC_CONTINUE_UNWIND. Or, set up registers and IP for Landing Pad 382 * and return _URC_INSTALL_CONTEXT. 383 * _UA_HANDLER_FRAME means this frame was the one with the handler in Phase 1, and now 384 * it is Phase 2 and the handler must be run. 385 * _UA_FORCE_UNWIND means unwinding the stack for longjmp or thread cancellation. Run 386 * finally clauses, not catch clauses, finallys must end with call to _Uwind_Resume(). 387 * exceptionClass = 8 byte value indicating type of thrown exception. If the low 4 bytes 388 * are "C++\0", it's a C++ exception. 389 * exceptionObject = language specific exception information 390 * context = opaque type of unwinder state information 391 * Returns: 392 * reason code 393 * See_Also: 394 * http://www.ucw.cz/~hubicka/papers/abi/node25.html 395 */ 396 397 extern (C) _Unwind_Reason_Code __dmd_personality_v0(int ver, _Unwind_Action actions, 398 _Unwind_Exception_Class exceptionClass, _Unwind_Exception* exceptionObject, 399 _Unwind_Context* context) 400 { 401 if (ver != 1) 402 return _URC_FATAL_PHASE1_ERROR; 403 assert(context); 404 405 const(ubyte)* language_specific_data; 406 int handler; 407 _Unwind_Ptr landing_pad; 408 409 debug (EH_personality) 410 { 411 writeln("__dmd_personality_v0(actions = x%x, eo = %p, context = %p)", cast(int)actions, exceptionObject, context); 412 writeln("exceptionClass = x%08llx", exceptionClass); 413 414 if (exceptionClass == dmdExceptionClass) 415 { 416 auto eh = ExceptionHeader.toExceptionHeader(exceptionObject); 417 for (auto ehx = eh; ehx; ehx = ehx.next) 418 writeln(" eh: %p next=%014p lsda=%p '%.*s'", ehx, ehx.next, ehx.languageSpecificData, ehx.object.msg.length, ehx.object.msg.ptr); 419 } 420 } 421 422 language_specific_data = cast(const(ubyte)*)_Unwind_GetLanguageSpecificData(context); 423 debug (EH_personality) writeln("lsda = %p", language_specific_data); 424 425 auto Start = _Unwind_GetRegionStart(context); 426 427 /* Get instruction pointer (ip) at start of instruction that threw 428 */ 429 version (CRuntime_Glibc) 430 { 431 int ip_before_insn; 432 // The instruction pointer must not be decremented when unwinding from a 433 // signal handler frame (asynchronous exception, also see 434 // etc.linux.memoryerror). So use _Unwind_GetIPInfo where available. 435 auto ip = _Unwind_GetIPInfo(context, &ip_before_insn); 436 if (!ip_before_insn) 437 --ip; 438 } 439 else 440 { 441 auto ip = _Unwind_GetIP(context); 442 --ip; 443 } 444 debug (EH_personality) writeln("ip = x%x", cast(int)(ip - Start)); 445 debug (EH_personality) writeln("\tStart = %p, ipoff = %p, lsda = %p", Start, ip - Start, language_specific_data); 446 447 auto result = scanLSDA(language_specific_data, ip - Start, exceptionClass, 448 (actions & _UA_FORCE_UNWIND) != 0, // don't catch when forced unwinding 449 (actions & _UA_SEARCH_PHASE) != 0, // search phase is looking for handlers 450 exceptionObject, 451 landing_pad, 452 handler); 453 landing_pad += Start; 454 455 final switch (result) 456 { 457 case LsdaResult.notFound: 458 fprintf(stderr, "not found\n"); 459 terminate(__LINE__); 460 assert(0); 461 462 case LsdaResult.foreign: 463 terminate(__LINE__); 464 assert(0); 465 466 case LsdaResult.corrupt: 467 fprintf(stderr, "LSDA is corrupt\n"); 468 terminate(__LINE__); 469 assert(0); 470 471 case LsdaResult.noAction: 472 debug (EH_personality) writeln(" no action"); 473 return _URC_CONTINUE_UNWIND; 474 475 case LsdaResult.cleanup: 476 debug (EH_personality) writeln(" cleanup"); 477 if (actions & _UA_SEARCH_PHASE) 478 { 479 return _URC_CONTINUE_UNWIND; 480 } 481 break; 482 483 case LsdaResult.handler: 484 debug (EH_personality) writeln(" handler"); 485 assert(!(actions & _UA_FORCE_UNWIND)); 486 if (actions & _UA_SEARCH_PHASE) 487 { 488 if (exceptionClass == dmdExceptionClass) 489 { 490 auto eh = ExceptionHeader.toExceptionHeader(exceptionObject); 491 debug (EH_personality) writeln(" eh.lsda = %p, lsda = %p", eh.languageSpecificData, language_specific_data); 492 eh.handler = handler; 493 eh.languageSpecificData = language_specific_data; 494 eh.landingPad = landing_pad; 495 } 496 return _URC_HANDLER_FOUND; 497 } 498 break; 499 } 500 501 debug (EH_personality) writeln(" lsda = %p, landing_pad = %p, handler = %d", language_specific_data, landing_pad, handler); 502 503 // Figure out what to do when there are multiple exceptions in flight 504 if (exceptionClass == dmdExceptionClass) 505 { 506 auto eh = ExceptionHeader.toExceptionHeader(exceptionObject); 507 debug (EH_personality) writeln(" '%.*s' next = %p", eh.object.msg.length, eh.object.msg.ptr, eh.next); 508 auto currentLsd = language_specific_data; 509 bool bypassed = false; 510 while (eh.next) 511 { 512 ExceptionHeader* ehn = eh.next; 513 514 Error e = cast(Error)eh.object; 515 if (e !is null && !cast(Error)ehn.object) 516 { 517 /* eh is an Error, ehn is not. Skip ehn. 518 */ 519 debug (EH_personality) writeln("bypass"); 520 currentLsd = ehn.languageSpecificData; 521 522 // Continuing to construct the bypassed chain 523 eh = ehn; 524 bypassed = true; 525 continue; 526 } 527 528 // Don't combine when the exceptions are from different functions 529 if (currentLsd != ehn.languageSpecificData) 530 { 531 debug (EH_personality) writeln("break: %p %p", currentLsd, ehn.languageSpecificData); 532 break; 533 } 534 535 else 536 { 537 debug (EH_personality) writeln("chain"); 538 // Append eh's object to ehn's object chain 539 // And replace our exception object with in-flight one 540 eh.object = Throwable.chainTogether(ehn.object, eh.object); 541 542 if (ehn.handler != handler && !bypassed) 543 { 544 handler = ehn.handler; 545 546 eh.handler = handler; 547 eh.languageSpecificData = language_specific_data; 548 eh.landingPad = landing_pad; 549 } 550 } 551 552 // Remove ehn from threaded chain 553 eh.next = ehn.next; 554 debug (EH_personality) writeln("delete %p", ehn); 555 _Unwind_DeleteException(&ehn.exception_object); // discard ehn 556 } 557 if (bypassed) 558 { 559 eh = ExceptionHeader.toExceptionHeader(exceptionObject); 560 Error e = cast(Error)eh.object; 561 auto ehn = eh.next; 562 e.bypassedException = ehn.object; 563 eh.next = ehn.next; 564 _Unwind_DeleteException(&ehn.exception_object); 565 } 566 } 567 568 // Set up registers and jump to cleanup or handler 569 _Unwind_SetGR(context, eh_exception_regno, cast(_Unwind_Ptr)exceptionObject); 570 _Unwind_SetGR(context, eh_selector_regno, handler); 571 _Unwind_SetIP(context, landing_pad); 572 573 return _URC_INSTALL_CONTEXT; 574 } 575 576 /************************************************* 577 * Look at the chain of inflight exceptions and pick the class type that'll 578 * be looked for in catch clauses. 579 * Params: 580 * exceptionObject = language specific exception information 581 * currentLsd = pointer to LSDA table 582 * Returns: 583 * class type to look for 584 */ 585 ClassInfo getClassInfo(_Unwind_Exception* exceptionObject, const(ubyte)* currentLsd) 586 { 587 ExceptionHeader* eh = ExceptionHeader.toExceptionHeader(exceptionObject); 588 Throwable ehobject = eh.object; 589 debug (EH_personality) writeln("start: %p '%.*s'", ehobject, cast(int)(typeid(ehobject).info.name.length), ehobject.classinfo.info.name.ptr); 590 for (ExceptionHeader* ehn = eh.next; ehn; ehn = ehn.next) 591 { 592 // like __dmd_personality_v0, don't combine when the exceptions are from different functions 593 // (fixes issue 19831, exception thrown and caught while inside finally block) 594 if (currentLsd != ehn.languageSpecificData) 595 { 596 debug (EH_personality) writeln("break: %p %p", currentLsd, ehn.languageSpecificData); 597 break; 598 } 599 600 debug (EH_personality) writeln("ehn = %p '%.*s'", ehn.object, cast(int)(typeid(ehn.object).info.name.length), ehn.object.classinfo.info.name.ptr); 601 Error e = cast(Error)ehobject; 602 if (e is null || (cast(Error)ehn.object) !is null) 603 { 604 ehobject = ehn.object; 605 } 606 } 607 debug (EH_personality) writeln("end : %p", ehobject); 608 return typeid(ehobject); 609 } 610 611 /****************************** 612 * Decode Unsigned LEB128. 613 * Params: 614 * p = pointer to data pointer, *p is updated 615 * to point past decoded value 616 * Returns: 617 * decoded value 618 * See_Also: 619 * https://en.wikipedia.org/wiki/LEB128 620 */ 621 _uleb128_t uLEB128(const(ubyte)** p) 622 { 623 auto q = *p; 624 _uleb128_t result = 0; 625 uint shift = 0; 626 while (1) 627 { 628 ubyte b = *q++; 629 result |= cast(_uleb128_t)(b & 0x7F) << shift; 630 if ((b & 0x80) == 0) 631 break; 632 shift += 7; 633 } 634 *p = q; 635 return result; 636 } 637 638 /****************************** 639 * Decode Signed LEB128. 640 * Params: 641 * p = pointer to data pointer, *p is updated 642 * to point past decoded value 643 * Returns: 644 * decoded value 645 * See_Also: 646 * https://en.wikipedia.org/wiki/LEB128 647 */ 648 _sleb128_t sLEB128(const(ubyte)** p) 649 { 650 auto q = *p; 651 ubyte b; 652 653 _sleb128_t result = 0; 654 uint shift = 0; 655 while (1) 656 { 657 b = *q++; 658 result |= cast(_sleb128_t)(b & 0x7F) << shift; 659 shift += 7; 660 if ((b & 0x80) == 0) 661 break; 662 } 663 if (shift < result.sizeof * 8 && (b & 0x40)) 664 result |= -(cast(_sleb128_t)1 << shift); 665 *p = q; 666 return result; 667 } 668 669 enum 670 { 671 DW_EH_PE_FORMAT_MASK = 0x0F, 672 DW_EH_PE_APPL_MASK = 0x70, 673 DW_EH_PE_indirect = 0x80, 674 675 DW_EH_PE_omit = 0xFF, 676 DW_EH_PE_ptr = 0x00, 677 DW_EH_PE_uleb128 = 0x01, 678 DW_EH_PE_udata2 = 0x02, 679 DW_EH_PE_udata4 = 0x03, 680 DW_EH_PE_udata8 = 0x04, 681 DW_EH_PE_sleb128 = 0x09, 682 DW_EH_PE_sdata2 = 0x0A, 683 DW_EH_PE_sdata4 = 0x0B, 684 DW_EH_PE_sdata8 = 0x0C, 685 686 DW_EH_PE_absptr = 0x00, 687 DW_EH_PE_pcrel = 0x10, 688 DW_EH_PE_textrel = 0x20, 689 DW_EH_PE_datarel = 0x30, 690 DW_EH_PE_funcrel = 0x40, 691 DW_EH_PE_aligned = 0x50, 692 } 693 694 695 /************************************************** 696 * Read and extract information from the LSDA (aka gcc_except_table section). 697 * The dmd Call Site Table is structurally different from other implementations. It 698 * is organized as nested ranges, and one ip can map to multiple ranges. The most 699 * nested candidate is selected when searched. Other implementations have one candidate 700 * per ip. 701 * Params: 702 * lsda = pointer to LSDA table 703 * ip = offset from start of function at which exception happened 704 * exceptionClass = which language threw the exception 705 * cleanupsOnly = only look for cleanups 706 * preferHandler = if a handler encloses a cleanup, prefer the handler 707 * exceptionObject = language specific exception information 708 * landingPad = set to landing pad 709 * handler = set to index of which catch clause was matched 710 * Returns: 711 * LsdaResult 712 * See_Also: 713 * http://reverseengineering.stackexchange.com/questions/6311/how-to-recover-the-exception-info-from-gcc-except-table-and-eh-handle-sections 714 * http://www.airs.com/blog/archives/464 715 * https://anarcheuz.github.io/2015/02/15/ELF%20internals%20part%202%20-%20exception%20handling/ 716 */ 717 718 LsdaResult scanLSDA(const(ubyte)* lsda, _Unwind_Ptr ip, _Unwind_Exception_Class exceptionClass, 719 bool cleanupsOnly, 720 bool preferHandler, 721 _Unwind_Exception* exceptionObject, 722 out _Unwind_Ptr landingPad, out int handler) 723 { 724 auto p = lsda; 725 if (!p) 726 return LsdaResult.noAction; 727 728 _Unwind_Ptr dw_pe_value(ubyte pe) 729 { 730 switch (pe) 731 { 732 case DW_EH_PE_sdata2: return readUnaligned!(short, true)(p); 733 case DW_EH_PE_udata2: return readUnaligned!(ushort, true)(p); 734 case DW_EH_PE_sdata4: return readUnaligned!(int, true)(p); 735 case DW_EH_PE_udata4: return readUnaligned!(uint, true)(p); 736 case DW_EH_PE_sdata8: return readUnaligned!(long, true)(p); 737 case DW_EH_PE_udata8: return readUnaligned!(ulong, true)(p); 738 case DW_EH_PE_sleb128: return cast(_Unwind_Ptr) sLEB128(&p); 739 case DW_EH_PE_uleb128: return cast(_Unwind_Ptr) uLEB128(&p); 740 case DW_EH_PE_ptr: if (size_t.sizeof == 8) 741 goto case DW_EH_PE_udata8; 742 else 743 goto case DW_EH_PE_udata4; 744 default: 745 terminate(__LINE__); 746 return 0; 747 } 748 } 749 750 ubyte LPstart = *p++; 751 752 _Unwind_Ptr LPbase = 0; 753 if (LPstart != DW_EH_PE_omit) 754 { 755 LPbase = dw_pe_value(LPstart); 756 } 757 758 ubyte TType = *p++; 759 _Unwind_Ptr TTbase = 0; 760 _Unwind_Ptr TToffset = 0; 761 if (TType != DW_EH_PE_omit) 762 { 763 TTbase = uLEB128(&p); 764 TToffset = (p - lsda) + TTbase; 765 } 766 debug (EH_personality) writeln(" TType = x%x, TTbase = x%x", TType, cast(int)TTbase); 767 768 ubyte CallSiteFormat = *p++; 769 770 _Unwind_Ptr CallSiteTableSize = dw_pe_value(DW_EH_PE_uleb128); 771 debug (EH_personality) writeln(" CallSiteFormat = x%x, CallSiteTableSize = x%x", CallSiteFormat, cast(int)CallSiteTableSize); 772 773 _Unwind_Ptr ipoffset = ip - LPbase; 774 debug (EH_personality) writeln("ipoffset = x%x", cast(int)ipoffset); 775 bool noAction = false; 776 auto tt = lsda + TToffset; 777 const(ubyte)* pActionTable = p + CallSiteTableSize; 778 while (1) 779 { 780 if (p >= pActionTable) 781 { 782 if (p == pActionTable) 783 break; 784 fprintf(stderr, "no Call Site Table\n"); 785 786 return LsdaResult.corrupt; 787 } 788 789 _Unwind_Ptr CallSiteStart = dw_pe_value(CallSiteFormat); 790 _Unwind_Ptr CallSiteRange = dw_pe_value(CallSiteFormat); 791 _Unwind_Ptr LandingPad = dw_pe_value(CallSiteFormat); 792 _uleb128_t ActionRecordPtr = uLEB128(&p); 793 794 debug (EH_personality) 795 { 796 writeln(" XT: start = x%x, range = x%x, landing pad = x%x, action = x%x", 797 cast(int)CallSiteStart, cast(int)CallSiteRange, cast(int)LandingPad, cast(int)ActionRecordPtr); 798 } 799 800 if (ipoffset < CallSiteStart) 801 break; 802 803 // The most nested entry will be the last one that ip is in 804 if (ipoffset < CallSiteStart + CallSiteRange) 805 { 806 debug (EH_personality) writeln("\tmatch"); 807 if (ActionRecordPtr) // if saw a catch 808 { 809 if (cleanupsOnly) 810 continue; // ignore catch 811 812 auto h = actionTableLookup(exceptionObject, cast(uint)ActionRecordPtr, pActionTable, tt, TType, exceptionClass, lsda); 813 if (h < 0) 814 { 815 fprintf(stderr, "negative handler\n"); 816 return LsdaResult.corrupt; 817 } 818 if (h == 0) 819 continue; // ignore 820 821 // The catch is good 822 noAction = false; 823 landingPad = LandingPad; 824 handler = h; 825 } 826 else if (LandingPad) // if saw a cleanup 827 { 828 if (preferHandler && handler) // enclosing handler overrides cleanup 829 continue; // keep looking 830 noAction = false; 831 landingPad = LandingPad; 832 handler = 0; // cleanup hides the handler 833 } 834 else // take no action 835 noAction = true; 836 } 837 } 838 839 if (noAction) 840 { 841 assert(!landingPad && !handler); 842 return LsdaResult.noAction; 843 } 844 845 if (landingPad) 846 return handler ? LsdaResult.handler : LsdaResult.cleanup; 847 848 return LsdaResult.notFound; 849 } 850 851 /******************************************** 852 * Look up classType in Action Table. 853 * Params: 854 * exceptionObject = language specific exception information 855 * actionRecordPtr = starting index in Action Table + 1 856 * pActionTable = pointer to start of Action Table 857 * tt = pointer past end of Type Table 858 * TType = encoding of entries in Type Table 859 * exceptionClass = which language threw the exception 860 * lsda = pointer to LSDA table 861 * Returns: 862 * - >=1 means the handler index of the classType 863 * - 0 means classType is not in the Action Table 864 * - <0 means corrupt 865 */ 866 int actionTableLookup(_Unwind_Exception* exceptionObject, uint actionRecordPtr, const(ubyte)* pActionTable, 867 const(ubyte)* tt, ubyte TType, _Unwind_Exception_Class exceptionClass, const(ubyte)* lsda) 868 { 869 debug (EH_personality) 870 { 871 writeln("actionTableLookup(actionRecordPtr = %d, pActionTable = %p, tt = %p)", 872 actionRecordPtr, pActionTable, tt); 873 } 874 assert(pActionTable < tt); 875 876 ClassInfo thrownType; 877 if (exceptionClass == dmdExceptionClass) 878 { 879 thrownType = getClassInfo(exceptionObject, lsda); 880 } 881 882 for (auto ap = pActionTable + actionRecordPtr - 1; 1; ) 883 { 884 assert(pActionTable <= ap && ap < tt); 885 886 auto TypeFilter = sLEB128(&ap); 887 auto apn = ap; 888 auto NextRecordPtr = sLEB128(&ap); 889 890 debug (EH_personality) writeln(" at: TypeFilter = %d, NextRecordPtr = %d", cast(int)TypeFilter, cast(int)NextRecordPtr); 891 892 if (TypeFilter <= 0) // should never happen with DMD generated tables 893 { 894 fprintf(stderr, "TypeFilter = %d\n", cast(int)TypeFilter); 895 return -1; // corrupt 896 } 897 898 /* TypeFilter is negative index from TToffset, 899 * which is where the ClassInfo is stored 900 */ 901 _Unwind_Ptr entry; 902 const(ubyte)* tt2; 903 switch (TType & DW_EH_PE_FORMAT_MASK) 904 { 905 case DW_EH_PE_sdata2: entry = readUnaligned!(short, false)(tt2 = tt - TypeFilter * 2); break; 906 case DW_EH_PE_udata2: entry = readUnaligned!(ushort, false)(tt2 = tt - TypeFilter * 2); break; 907 case DW_EH_PE_sdata4: entry = readUnaligned!(int, false)(tt2 = tt - TypeFilter * 4); break; 908 case DW_EH_PE_udata4: entry = readUnaligned!(uint, false)(tt2 = tt - TypeFilter * 4); break; 909 case DW_EH_PE_sdata8: entry = readUnaligned!(long, false)(tt2 = tt - TypeFilter * 8); break; 910 case DW_EH_PE_udata8: entry = readUnaligned!(ulong, false)(tt2 = tt - TypeFilter * 8); break; 911 case DW_EH_PE_ptr: if (size_t.sizeof == 8) 912 goto case DW_EH_PE_udata8; 913 else 914 goto case DW_EH_PE_udata4; 915 default: 916 fprintf(stderr, "TType = x%x\n", TType); 917 return -1; // corrupt 918 } 919 if (!entry) // the 'catch all' type 920 return -1; // corrupt: should never happen with DMD, which explicitly uses Throwable 921 922 switch (TType & DW_EH_PE_APPL_MASK) 923 { 924 case DW_EH_PE_absptr: 925 break; 926 927 case DW_EH_PE_pcrel: 928 entry += cast(_Unwind_Ptr)tt2; 929 break; 930 931 default: 932 return -1; 933 } 934 if (TType & DW_EH_PE_indirect) 935 entry = *cast(_Unwind_Ptr*)entry; 936 937 ClassInfo ci = cast(ClassInfo)cast(void*)(entry); 938 if (typeid(ci) is typeid(__cpp_type_info_ptr)) 939 { 940 if (exceptionClass == cppExceptionClass || exceptionClass == cppExceptionClass1) 941 { 942 // sti is catch clause type_info 943 auto sti = cast(CppTypeInfo)((cast(__cpp_type_info_ptr)cast(void*)ci).ptr); 944 auto p = getCppPtrToThrownObject(exceptionObject, sti); 945 if (p) // if found 946 { 947 auto eh = CppExceptionHeader.toExceptionHeader(exceptionObject); 948 eh.thrownPtr = p; // for __cxa_begin_catch() 949 return cast(int)TypeFilter; 950 } 951 } 952 } 953 else if (exceptionClass == dmdExceptionClass && _d_isbaseof(thrownType, ci)) 954 return cast(int)TypeFilter; // found it 955 956 if (!NextRecordPtr) 957 return 0; // catch not found 958 959 ap = apn + NextRecordPtr; 960 } 961 assert(false); // All other branches return 962 } 963 964 enum LsdaResult 965 { 966 notFound, // ip was not found in the LSDA - an exception shouldn't have happened 967 foreign, // found a result we cannot handle 968 corrupt, // the tables are corrupt 969 noAction, // found, but no action needed (i.e. no cleanup nor handler) 970 cleanup, // cleanup found (i.e. finally or destructor) 971 handler, // handler found (i.e. a catch) 972 } 973 974 void terminate(uint line) @nogc 975 { 976 printf("dwarfeh(%u) fatal error\n", line); 977 abort(); // unceremoniously exit 978 } 979 980 981 /****************************** C++ Support *****************************/ 982 983 enum _Unwind_Exception_Class cppExceptionClass = 984 (cast(_Unwind_Exception_Class)'G' << 56) | 985 (cast(_Unwind_Exception_Class)'N' << 48) | 986 (cast(_Unwind_Exception_Class)'U' << 40) | 987 (cast(_Unwind_Exception_Class)'C' << 32) | 988 (cast(_Unwind_Exception_Class)'C' << 24) | 989 (cast(_Unwind_Exception_Class)'+' << 16) | 990 (cast(_Unwind_Exception_Class)'+' << 8) | 991 (cast(_Unwind_Exception_Class)0 << 0); 992 993 enum _Unwind_Exception_Class cppExceptionClass1 = cppExceptionClass + 1; 994 995 996 /***************************************** 997 * Get Pointer to Thrown Object if type of thrown object is implicitly 998 * convertible to the catch type. 999 * Params: 1000 * exceptionObject = language specific exception information 1001 * sti = type of catch clause 1002 * Returns: 1003 * null if not caught, pointer to thrown object if caught 1004 */ 1005 void* getCppPtrToThrownObject(_Unwind_Exception* exceptionObject, CppTypeInfo sti) 1006 { 1007 void* p; // pointer to thrown object 1008 if (exceptionObject.exception_class & 1) 1009 p = CppExceptionHeader.toExceptionHeader(exceptionObject).ptr; 1010 else 1011 p = cast(void*)(exceptionObject + 1); // thrown object is immediately after it 1012 1013 const tt = (cast(CppExceptionHeader*)p - 1).typeinfo; 1014 1015 if (tt.__is_pointer_p()) 1016 p = *cast(void**)p; 1017 1018 // Pointer adjustment may be necessary due to multiple inheritance 1019 return (sti is tt || sti.__do_catch(tt, &p, 1)) ? p : null; 1020 } 1021 1022 extern (C++) 1023 { 1024 /** 1025 * Access C++ std::type_info's virtual functions from D, 1026 * being careful to not require linking with libstd++ 1027 * or interfere with core.stdcpp.typeinfo. 1028 * So, give it a different name. 1029 */ 1030 interface CppTypeInfo // map to C++ std::type_info's virtual functions 1031 { 1032 void dtor1(); // consume destructor slot in vtbl[] 1033 void dtor2(); // consume destructor slot in vtbl[] 1034 bool __is_pointer_p() const; 1035 bool __is_function_p() const; 1036 bool __do_catch(const CppTypeInfo, void**, uint) const; 1037 bool __do_upcast(const void*, void**) const; 1038 } 1039 } 1040 1041 /// The C++ version of D's ExceptionHeader wrapper 1042 struct CppExceptionHeader 1043 { 1044 union 1045 { 1046 CppTypeInfo typeinfo; // type that was thrown 1047 void* ptr; // pointer to real exception 1048 } 1049 void* p1; // unreferenced placeholders... 1050 void* p2; 1051 void* p3; 1052 void* p4; 1053 int i1; 1054 int i2; 1055 const(ubyte)* p5; 1056 const(ubyte)* p6; 1057 _Unwind_Ptr p7; 1058 void* thrownPtr; // pointer to thrown object 1059 _Unwind_Exception exception_object; // the unwinder's data 1060 1061 /******************************* 1062 * Convert from pointer to exception_object field to pointer to CppExceptionHeader 1063 * that it is embedded inside of. 1064 * Params: 1065 * eo = pointer to exception_object field 1066 * Returns: 1067 * pointer to CppExceptionHeader that eo points into. 1068 */ 1069 static CppExceptionHeader* toExceptionHeader(_Unwind_Exception* eo) 1070 { 1071 return cast(CppExceptionHeader*)(eo + 1) - 1; 1072 } 1073 }