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