1 /** 2 * This module contains functions and structures required for 3 * exception handling. 4 */ 5 module rt.eh; 6 7 import ldc.cstdarg; 8 import rt.compiler.util.console; 9 10 // debug = EH_personality; 11 12 // current EH implementation works on x86 13 // if it has a working unwind runtime 14 version(X86) { 15 version(linux) version=X86_UNWIND; 16 version(darwin) version=X86_UNWIND; 17 version(solaris) version=X86_UNWIND; 18 version(freebsd) version=X86_UNWIND; 19 } 20 version(X86_64) { 21 version(linux) version=X86_UNWIND; 22 version(darwin) version=X86_UNWIND; 23 version(solaris) version=X86_UNWIND; 24 version(freebsd) version=X86_UNWIND; 25 } 26 27 //version = HP_LIBUNWIND; 28 29 private extern(C) void abort(); 30 private extern(C) int printf(char*, ...); 31 private extern(C) int vprintf(char*, va_list va); 32 33 // D runtime functions 34 extern(C) { 35 int _d_isbaseof(ClassInfo oc, ClassInfo c); 36 } 37 38 // libunwind headers 39 extern(C) 40 { 41 enum _Unwind_Reason_Code : int 42 { 43 NO_REASON = 0, 44 FOREIGN_EXCEPTION_CAUGHT = 1, 45 FATAL_PHASE2_ERROR = 2, 46 FATAL_PHASE1_ERROR = 3, 47 NORMAL_STOP = 4, 48 END_OF_STACK = 5, 49 HANDLER_FOUND = 6, 50 INSTALL_CONTEXT = 7, 51 CONTINUE_UNWIND = 8 52 } 53 54 enum _Unwind_Action : int 55 { 56 SEARCH_PHASE = 1, 57 CLEANUP_PHASE = 2, 58 HANDLER_PHASE = 3, 59 FORCE_UNWIND = 4 60 } 61 62 alias void* _Unwind_Context_Ptr; 63 64 alias void function(_Unwind_Reason_Code, _Unwind_Exception*) _Unwind_Exception_Cleanup_Fn; 65 66 struct _Unwind_Exception 67 { 68 ulong exception_class; 69 _Unwind_Exception_Cleanup_Fn exception_cleanup; 70 ptrdiff_t private_1; 71 ptrdiff_t private_2; 72 } 73 74 // interface to HP's libunwind from http://www.nongnu.org/libunwind/ 75 version(HP_LIBUNWIND) 76 { 77 // Haven't checked whether and how it has _Unwind_Get{Text,Data}RelBase 78 pragma (msg, "HP_LIBUNWIND interface is out of date and untested"); 79 80 void __libunwind_Unwind_Resume(_Unwind_Exception *); 81 _Unwind_Reason_Code __libunwind_Unwind_RaiseException(_Unwind_Exception *); 82 ptrdiff_t __libunwind_Unwind_GetLanguageSpecificData(_Unwind_Context_Ptr 83 context); 84 ptrdiff_t __libunwind_Unwind_GetIP(_Unwind_Context_Ptr context); 85 ptrdiff_t __libunwind_Unwind_SetIP(_Unwind_Context_Ptr context, 86 ptrdiff_t new_value); 87 ptrdiff_t __libunwind_Unwind_SetGR(_Unwind_Context_Ptr context, int index, 88 ptrdiff_t new_value); 89 ptrdiff_t __libunwind_Unwind_GetRegionStart(_Unwind_Context_Ptr context); 90 91 alias __libunwind_Unwind_Resume _Unwind_Resume; 92 alias __libunwind_Unwind_RaiseException _Unwind_RaiseException; 93 alias __libunwind_Unwind_GetLanguageSpecificData 94 _Unwind_GetLanguageSpecificData; 95 alias __libunwind_Unwind_GetIP _Unwind_GetIP; 96 alias __libunwind_Unwind_SetIP _Unwind_SetIP; 97 alias __libunwind_Unwind_SetGR _Unwind_SetGR; 98 alias __libunwind_Unwind_GetRegionStart _Unwind_GetRegionStart; 99 } 100 else version(X86_UNWIND) 101 { 102 void _Unwind_Resume(_Unwind_Exception*); 103 _Unwind_Reason_Code _Unwind_RaiseException(_Unwind_Exception*); 104 ptrdiff_t _Unwind_GetLanguageSpecificData(_Unwind_Context_Ptr context); 105 ptrdiff_t _Unwind_GetIP(_Unwind_Context_Ptr context); 106 ptrdiff_t _Unwind_SetIP(_Unwind_Context_Ptr context, ptrdiff_t new_value); 107 ptrdiff_t _Unwind_SetGR(_Unwind_Context_Ptr context, int index, 108 ptrdiff_t new_value); 109 ptrdiff_t _Unwind_GetRegionStart(_Unwind_Context_Ptr context); 110 111 size_t _Unwind_GetTextRelBase(_Unwind_Context_Ptr); 112 size_t _Unwind_GetDataRelBase(_Unwind_Context_Ptr); 113 } 114 else 115 { 116 // runtime calls these directly 117 void _Unwind_Resume(_Unwind_Exception*) 118 { 119 console("_Unwind_Resume is not implemented on this platform.\n"); 120 } 121 _Unwind_Reason_Code _Unwind_RaiseException(_Unwind_Exception*) 122 { 123 console("_Unwind_RaiseException is not implemented on this platform.\n"); 124 return _Unwind_Reason_Code.FATAL_PHASE1_ERROR; 125 } 126 } 127 128 } 129 130 // error and exit 131 extern(C) private void fatalerror(char[] format) 132 { 133 printf("Fatal error in EH code: %.*s\n", format.length, format.ptr); 134 abort(); 135 } 136 137 138 // DWARF EH encoding enum 139 // See e.g. http://refspecs.freestandards.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/dwarfext.html 140 private enum : ubyte { 141 DW_EH_PE_omit = 0xff, // value is not present 142 143 // value format 144 DW_EH_PE_absptr = 0x00, // literal pointer 145 DW_EH_PE_uleb128 = 0x01, 146 DW_EH_PE_udata2 = 0x02, // unsigned 2-byte 147 DW_EH_PE_udata4 = 0x03, 148 DW_EH_PE_udata8 = 0x04, 149 DW_EH_PE_sleb128 = 0x09, 150 DW_EH_PE_sdata2 = 0x0a, 151 DW_EH_PE_sdata4 = 0x0b, 152 DW_EH_PE_sdata8 = 0x0c, 153 154 // value meaning 155 DW_EH_PE_pcrel = 0x10, // relative to program counter 156 DW_EH_PE_textrel = 0x20, // relative to .text 157 DW_EH_PE_datarel = 0x30, // relative to .got or .eh_frame_hdr 158 DW_EH_PE_funcrel = 0x40, // relative to beginning of function 159 DW_EH_PE_aligned = 0x50, // is an aligned void* 160 161 // value is a pointer to the actual value 162 // this is a mask on top of one of the above 163 DW_EH_PE_indirect = 0x80 164 } 165 166 // Helpers for reading DWARF data 167 168 // Given an encoding and a context, return the base to which the encoding is 169 // relative 170 private size_t base_of_encoded(_Unwind_Context_Ptr context, ubyte encoding) 171 { 172 if (encoding == DW_EH_PE_omit) 173 return 0; 174 175 switch (encoding & 0x70) // ignore DW_EH_PE_indirect 176 { 177 case DW_EH_PE_absptr, DW_EH_PE_pcrel, DW_EH_PE_aligned: 178 return 0; 179 180 case DW_EH_PE_textrel: return _Unwind_GetTextRelBase(context); 181 case DW_EH_PE_datarel: return _Unwind_GetDataRelBase(context); 182 case DW_EH_PE_funcrel: return _Unwind_GetRegionStart(context); 183 184 default: fatalerror("Unrecognized base for DWARF value"); 185 } 186 assert(0); 187 } 188 189 // Only defined for fixed-size encodings 190 private size_t size_of_encoded(ubyte encoding) 191 { 192 if (encoding == DW_EH_PE_omit) 193 return 0; 194 195 switch (encoding & 0x07) // ignore leb128 196 { 197 case DW_EH_PE_absptr: return (void*).sizeof; 198 case DW_EH_PE_udata2: return 2; 199 case DW_EH_PE_udata4: return 4; 200 case DW_EH_PE_udata8: return 8; 201 202 default: fatalerror("Unrecognized fixed-size DWARF value encoding"); 203 } 204 assert(0); 205 } 206 207 // Actual value readers below: read a value from the given ubyte* into the 208 // output parameter and return the pointer incremented past the value. 209 210 // Like read_encoded_with_base but gets the base from the given context 211 private ubyte* read_encoded(_Unwind_Context_Ptr context, ubyte encoding, ubyte* p, out size_t val) 212 { 213 return read_encoded_with_base(encoding, base_of_encoded(context, encoding), p, val); 214 } 215 216 private ubyte* read_encoded_with_base(ubyte encoding, size_t base, ubyte* p, out size_t val) 217 { 218 if (encoding == DW_EH_PE_aligned) 219 { 220 auto a = cast(size_t)p; 221 a = (a + (void*).sizeof - 1) & -(void*).sizeof; 222 val = *cast(size_t*)a; 223 return cast(ubyte*)(a + (void*).sizeof); 224 } 225 226 union U 227 { 228 size_t ptr; 229 ushort udata2; 230 uint udata4; 231 ulong udata8; 232 short sdata2; 233 int sdata4; 234 long sdata8; 235 } 236 237 auto u = cast(U*)p; 238 239 size_t result; 240 241 switch (encoding & 0x0f) 242 { 243 case DW_EH_PE_absptr: 244 result = u.ptr; 245 p += (void*).sizeof; 246 break; 247 248 case DW_EH_PE_uleb128: 249 { 250 p = get_uleb128(p, result); 251 break; 252 } 253 case DW_EH_PE_sleb128: 254 { 255 ptrdiff_t sleb128; 256 p = get_sleb128(p, sleb128); 257 result = cast(size_t)sleb128; 258 break; 259 } 260 261 case DW_EH_PE_udata2: result = cast(size_t)u.udata2; p += 2; break; 262 case DW_EH_PE_udata4: result = cast(size_t)u.udata4; p += 4; break; 263 case DW_EH_PE_udata8: result = cast(size_t)u.udata8; p += 8; break; 264 case DW_EH_PE_sdata2: result = cast(size_t)u.sdata2; p += 2; break; 265 case DW_EH_PE_sdata4: result = cast(size_t)u.sdata4; p += 4; break; 266 case DW_EH_PE_sdata8: result = cast(size_t)u.sdata8; p += 8; break; 267 268 default: fatalerror("Unrecognized DWARF value encoding format"); 269 } 270 if (result) 271 { 272 if ((encoding & 0x70) == DW_EH_PE_pcrel) 273 result += cast(size_t)u; 274 else 275 result += base; 276 277 if (encoding & DW_EH_PE_indirect) 278 result = *cast(size_t*)result; 279 } 280 val = result; 281 return p; 282 } 283 284 private ubyte* get_uleb128(ubyte* addr, ref size_t res) 285 { 286 res = 0; 287 size_t bitsize = 0; 288 289 // read as long as high bit is set 290 while(*addr & 0x80) { 291 res |= (*addr & 0x7f) << bitsize; 292 bitsize += 7; 293 addr += 1; 294 if(bitsize >= size_t.sizeof*8) 295 fatalerror("tried to read uleb128 that exceeded size of size_t"); 296 } 297 // read last 298 if(bitsize != 0 && *addr >= 1 << size_t.sizeof*8 - bitsize) 299 fatalerror("Fatal error in EH code: tried to read uleb128 that exceeded size of size_t"); 300 res |= (*addr) << bitsize; 301 302 return addr + 1; 303 } 304 305 private ubyte* get_sleb128(ubyte* addr, ref ptrdiff_t res) 306 { 307 res = 0; 308 size_t bitsize = 0; 309 310 // read as long as high bit is set 311 while(*addr & 0x80) { 312 res |= (*addr & 0x7f) << bitsize; 313 bitsize += 7; 314 addr += 1; 315 if(bitsize >= size_t.sizeof*8) 316 fatalerror("tried to read sleb128 that exceeded size of size_t"); 317 } 318 // read last 319 if(bitsize != 0 && *addr >= 1 << size_t.sizeof*8 - bitsize) 320 fatalerror("tried to read sleb128 that exceeded size of size_t"); 321 res |= (*addr) << bitsize; 322 323 // take care of sign 324 if(bitsize < size_t.sizeof*8 && ((*addr) & 0x40)) 325 res |= cast(ptrdiff_t)(-1) ^ ((1 << (bitsize+7)) - 1); 326 327 return addr + 1; 328 } 329 330 331 // exception struct used by the runtime. 332 // _d_throw allocates a new instance and passes the address of its 333 // _Unwind_Exception member to the unwind call. The personality 334 // routine is then able to get the whole struct by looking at the data 335 // surrounding the unwind info. 336 struct _d_exception 337 { 338 Object exception_object; 339 _Unwind_Exception unwind_info; 340 } 341 342 // the 8-byte string identifying the type of exception 343 // the first 4 are for vendor, the second 4 for language 344 //TODO: This may be the wrong way around 345 const char[8] _d_exception_class = "LLDCD1\0\0"; 346 347 348 // 349 // x86 unwind specific implementation of personality function 350 // and helpers 351 // 352 version(X86_UNWIND) 353 { 354 355 // Various stuff we need 356 struct Region 357 { 358 ubyte* callsite_table; 359 ubyte* action_table; 360 361 // Note: classinfo_table points past the end of the table 362 ubyte* classinfo_table; 363 364 ptrdiff_t start; 365 size_t lpStart_base; // landing pad base 366 367 ubyte ttypeEnc; 368 size_t ttype_base; // typeinfo base 369 370 ubyte callSiteEnc; 371 } 372 373 // the personality routine gets called by the unwind handler and is responsible for 374 // reading the EH tables and deciding what to do 375 extern(C) _Unwind_Reason_Code _d_eh_personality(int ver, _Unwind_Action actions, ulong exception_class, _Unwind_Exception* exception_info, _Unwind_Context_Ptr context) 376 { 377 // check ver: the C++ Itanium ABI only allows ver == 1 378 if(ver != 1) 379 return _Unwind_Reason_Code.FATAL_PHASE1_ERROR; 380 381 // check exceptionClass 382 //TODO: Treat foreign exceptions with more respect 383 if((cast(char*)&exception_class)[0..8] != _d_exception_class) 384 return _Unwind_Reason_Code.FATAL_PHASE1_ERROR; 385 386 // find call site table, action table and classinfo table 387 // Note: callsite and action tables do not contain static-length 388 // data and will be parsed as needed 389 390 Region region; 391 392 _d_getLanguageSpecificTables(context, region); 393 if (!region.callsite_table) 394 return _Unwind_Reason_Code.CONTINUE_UNWIND; 395 396 /* 397 find landing pad and action table index belonging to ip by walking 398 the callsite_table 399 */ 400 ubyte* callsite_walker = region.callsite_table; 401 402 // get the instruction pointer 403 // will be used to find the right entry in the callsite_table 404 // -1 because it will point past the last instruction 405 ptrdiff_t ip = _Unwind_GetIP(context) - 1; 406 407 // table entries 408 size_t landing_pad; 409 size_t action_offset; 410 411 while(true) { 412 // if we've gone through the list and found nothing... 413 if(callsite_walker >= region.action_table) 414 return _Unwind_Reason_Code.CONTINUE_UNWIND; 415 416 size_t block_start, block_size; 417 418 callsite_walker = read_encoded(null, region.callSiteEnc, callsite_walker, block_start); 419 callsite_walker = read_encoded(null, region.callSiteEnc, callsite_walker, block_size); 420 callsite_walker = read_encoded(null, region.callSiteEnc, callsite_walker, landing_pad); 421 callsite_walker = get_uleb128(callsite_walker, action_offset); 422 423 debug(EH_personality_verbose) printf("ip=%zx %d %d %zx\n", ip, block_start, block_size, landing_pad); 424 425 // since the list is sorted, as soon as we're past the ip 426 // there's no handler to be found 427 if(ip < region.start + block_start) 428 return _Unwind_Reason_Code.CONTINUE_UNWIND; 429 430 if(landing_pad) 431 landing_pad += region.lpStart_base; 432 433 // if we've found our block, exit 434 if(ip < region.start + block_start + block_size) 435 break; 436 } 437 438 debug(EH_personality) printf("Found correct landing pad %zx and actionOffset %zx\n", landing_pad, action_offset); 439 440 // now we need the exception's classinfo to find a handler 441 // the exception_info is actually a member of a larger _d_exception struct 442 // the runtime allocated. get that now 443 _d_exception* exception_struct = cast(_d_exception*)(cast(ubyte*)exception_info - _d_exception.unwind_info.offsetof); 444 445 // if there's no action offset and no landing pad, continue unwinding 446 if(!action_offset && !landing_pad) 447 return _Unwind_Reason_Code.CONTINUE_UNWIND; 448 449 // if there's no action offset but a landing pad, this is a cleanup handler 450 else if(!action_offset && landing_pad) 451 return _d_eh_install_finally_context(actions, cast(ptrdiff_t)landing_pad, exception_struct, context); 452 453 /* 454 walk action table chain, comparing classinfos using _d_isbaseof 455 */ 456 ubyte* action_walker = region.action_table + action_offset - 1; 457 458 while(true) { 459 ptrdiff_t ti_offset, next_action_offset; 460 461 action_walker = get_sleb128(action_walker, ti_offset); 462 // it is intentional that we not modify action_walker here 463 // next_action_offset is from current action_walker position 464 get_sleb128(action_walker, next_action_offset); 465 466 // negative are 'filters' which we don't use 467 if(ti_offset < 0) 468 fatalerror("Filter actions are unsupported"); 469 470 // zero means cleanup, which we require to be the last action 471 if(ti_offset == 0) { 472 if(next_action_offset != 0) 473 fatalerror("Cleanup action must be last in chain"); 474 return _d_eh_install_finally_context(actions, cast(ptrdiff_t)landing_pad, exception_struct, context); 475 } 476 477 // get classinfo for action and check if the one in the 478 // exception structure is a base 479 size_t typeinfo; 480 auto filter = ti_offset * size_of_encoded(region.ttypeEnc); 481 read_encoded_with_base(region.ttypeEnc, region.ttype_base, region.classinfo_table - filter, typeinfo); 482 483 debug(EH_personality_verbose) 484 printf("classinfo at %zx (enc %zx (size %zx) base %zx ptr %zx)\n", typeinfo, region.ttypeEnc, size_of_encoded(region.ttypeEnc), region.ttype_base, region.classinfo_table - filter); 485 486 auto catch_ci = *cast(ClassInfo*)&typeinfo; 487 488 debug(EH_personality) printf("Comparing catch %s to exception %s\n", catch_ci.name.ptr, exception_struct.exception_object.classinfo.name.ptr); 489 if(_d_isbaseof(exception_struct.exception_object.classinfo, catch_ci)) 490 return _d_eh_install_catch_context(actions, ti_offset, cast(ptrdiff_t)landing_pad, exception_struct, context); 491 492 // we've walked through all actions and found nothing... 493 if(next_action_offset == 0) 494 return _Unwind_Reason_Code.CONTINUE_UNWIND; 495 else 496 action_walker += next_action_offset; 497 } 498 499 fatalerror("reached unreachable"); 500 return _Unwind_Reason_Code.FATAL_PHASE1_ERROR; 501 } 502 503 // These are the register numbers for SetGR that 504 // llvm's eh.exception and eh.selector intrinsics 505 // will pick up. 506 // Hints for these can be found by looking at the 507 // EH_RETURN_DATA_REGNO macro in GCC, careful testing 508 // is required though. 509 version (X86_64) 510 { 511 private int eh_exception_regno = 0; 512 private int eh_selector_regno = 1; 513 } else { 514 private int eh_exception_regno = 0; 515 private int eh_selector_regno = 2; 516 } 517 518 private _Unwind_Reason_Code _d_eh_install_catch_context(_Unwind_Action actions, ptrdiff_t switchval, ptrdiff_t landing_pad, _d_exception* exception_struct, _Unwind_Context_Ptr context) 519 { 520 debug(EH_personality) printf("Found catch clause!\n"); 521 522 if(actions & _Unwind_Action.SEARCH_PHASE) 523 return _Unwind_Reason_Code.HANDLER_FOUND; 524 525 else if(actions & _Unwind_Action.HANDLER_PHASE) 526 { 527 debug(EH_personality) printf("Setting switch value to: %d!\n", switchval); 528 _Unwind_SetGR(context, eh_exception_regno, cast(ptrdiff_t)cast(void*)(exception_struct.exception_object)); 529 _Unwind_SetGR(context, eh_selector_regno, cast(ptrdiff_t)switchval); 530 _Unwind_SetIP(context, landing_pad); 531 return _Unwind_Reason_Code.INSTALL_CONTEXT; 532 } 533 534 fatalerror("reached unreachable"); 535 return _Unwind_Reason_Code.FATAL_PHASE2_ERROR; 536 } 537 538 private _Unwind_Reason_Code _d_eh_install_finally_context(_Unwind_Action actions, ptrdiff_t landing_pad, _d_exception* exception_struct, _Unwind_Context_Ptr context) 539 { 540 // if we're merely in search phase, continue 541 if(actions & _Unwind_Action.SEARCH_PHASE) 542 return _Unwind_Reason_Code.CONTINUE_UNWIND; 543 544 debug(EH_personality) printf("Calling cleanup routine...\n"); 545 546 _Unwind_SetGR(context, eh_exception_regno, cast(ptrdiff_t)exception_struct); 547 _Unwind_SetGR(context, eh_selector_regno, 0); 548 _Unwind_SetIP(context, landing_pad); 549 return _Unwind_Reason_Code.INSTALL_CONTEXT; 550 } 551 552 private void _d_getLanguageSpecificTables(_Unwind_Context_Ptr context, out Region region) 553 { 554 auto data = cast(ubyte*)_Unwind_GetLanguageSpecificData(context); 555 if (!data) 556 return; 557 558 region.start = _Unwind_GetRegionStart(context); 559 560 // Read the C++-style LSDA: this is implementation-defined by GCC but LLVM 561 // outputs the same kind of table 562 563 // Get @LPStart: landing pad offsets are relative to it 564 auto lpStartEnc = *data++; 565 if (lpStartEnc == DW_EH_PE_omit) 566 region.lpStart_base = region.start; 567 else 568 data = read_encoded(context, lpStartEnc, data, region.lpStart_base); 569 570 // Get @TType: the offset to the handler and typeinfo 571 region.ttypeEnc = *data++; 572 if (region.ttypeEnc == DW_EH_PE_omit) 573 // Not sure about this one... 574 fatalerror("@TType must not be omitted from DWARF header"); 575 576 size_t ciOffset; 577 data = get_uleb128(data, ciOffset); 578 region.classinfo_table = data + ciOffset; 579 580 region.ttype_base = base_of_encoded(context, region.ttypeEnc); 581 582 // Get encoding and length of the call site table, which precedes the action 583 // table. 584 region.callSiteEnc = *data++; 585 if (region.callSiteEnc == DW_EH_PE_omit) 586 fatalerror("Call site table encoding must not be omitted from DWARF header"); 587 588 size_t callSiteLength; 589 region.callsite_table = get_uleb128(data, callSiteLength); 590 region.action_table = region.callsite_table + callSiteLength; 591 } 592 593 } // end of x86 Linux specific implementation 594 595 596 extern(C) void _d_throw_exception(Object e) 597 { 598 if (e !is null) 599 { 600 _d_exception* exc_struct = new _d_exception; 601 exc_struct.unwind_info.exception_class = *cast(ulong*)_d_exception_class.ptr; 602 exc_struct.exception_object = e; 603 _Unwind_Reason_Code ret = _Unwind_RaiseException(&exc_struct.unwind_info); 604 console("_Unwind_RaiseException failed with reason code: ")(ret)("\n"); 605 } 606 abort(); 607 } 608 609 extern(C) void _d_eh_resume_unwind(_d_exception* exception_struct) 610 { 611 _Unwind_Resume(&exception_struct.unwind_info); 612 }