1 /* GDC -- D front-end for GCC 2 Copyright (C) 2004 David Friedman 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation; either version 2 of the License, or 7 (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License 15 along with this program; if not, write to the Free Software 16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 */ 18 /* 19 This code is based on the libstdc++ exception handling routines. 20 */ 21 22 module rt.compiler.gdc.gcc.deh; 23 24 private import rt.compiler.gdc.gcc.unwind; 25 private import rt.compiler.gdc.gcc.unwind_pe; 26 private import rt.compiler.gdc.gcc.builtins; 27 28 private import tango.stdc.stdlib; 29 30 // "GNUCD__\0" 31 const _Unwind_Exception_Class GDC_Exception_Class = 0x005f5f4443554e47L; 32 33 struct Phase1Info 34 { 35 int handlerSwitchValue; 36 ubyte *languageSpecificData; 37 _Unwind_Ptr landingPad; 38 } 39 40 struct OurUnwindException { 41 42 // Cache parsed handler data from the personality routine Phase 1 43 // for Phase 2. 44 Phase1Info cache; 45 46 void save(_Unwind_Context* context, ref Phase1Info info) 47 { 48 cache = info; 49 } 50 void restore(ref Phase1Info info) 51 { 52 info = cache; 53 } 54 55 Object obj; 56 57 /* The exception object must be directly behind unwindHeader. (See 58 IRState::exceptionObject.) */ 59 static assert(unwindHeader.offsetof - obj.offsetof == obj.sizeof); 60 61 // The generic exception header 62 _Unwind_Exception unwindHeader; 63 64 static OurUnwindException * fromHeader(_Unwind_Exception * p_ue) { 65 return cast(OurUnwindException *) 66 (cast(void*) p_ue - OurUnwindException.unwindHeader.offsetof); 67 } 68 } 69 70 // D doesn't define these, so they are private for now. 71 private void dehTerminate() 72 { 73 // replaces std::terminate and terminating with a specific handler 74 abort(); 75 } 76 private void dehUnexpected() 77 { 78 } 79 private void dehBeginCatch(_Unwind_Exception *exc) 80 { 81 // nothing 82 } 83 84 // This is called by the unwinder. 85 86 private extern (C) void 87 _gdc_cleanupException(_Unwind_Reason_Code code, _Unwind_Exception *exc) 88 { 89 // If we haven't been caught by a foreign handler, then this is 90 // some sort of unwind error. In that case just die immediately. 91 // _Unwind_DeleteException in the HP-UX IA64 libunwind library 92 // returns _URC_NO_REASON and not _URC_FOREIGN_EXCEPTION_CAUGHT 93 // like the GCC _Unwind_DeleteException function does. 94 if (code != _URC_FOREIGN_EXCEPTION_CAUGHT && code != _URC_NO_REASON) 95 dehTerminate(); // __terminate (header->terminateHandler); 96 97 OurUnwindException * p = OurUnwindException.fromHeader( exc ); 98 delete p; 99 } 100 101 // This is called by compiler-generated code for throw statements. 102 extern (C) public void 103 _d_throw(Object obj) 104 { 105 OurUnwindException * exc = new OurUnwindException; 106 exc.obj = obj; 107 static if ( is(typeof(exc.unwindHeader.exception_class = GDC_Exception_Class)) ) 108 exc.unwindHeader.exception_class = GDC_Exception_Class; 109 else 110 exc.unwindHeader.exception_class[] = GDC_Exception_Class[]; 111 exc.unwindHeader.exception_cleanup = & _gdc_cleanupException; 112 113 version (GNU_SjLj_Exceptions) { 114 _Unwind_SjLj_RaiseException (&exc.unwindHeader); 115 } else { 116 _Unwind_RaiseException (&exc.unwindHeader); 117 } 118 119 // Some sort of unwinding error. Note that terminate is a handler. 120 dehBeginCatch (&exc.unwindHeader); 121 dehTerminate(); // std::terminate (); 122 } 123 124 extern (C) int _d_isbaseof(ClassInfo, ClassInfo); 125 126 // rethrow? 127 128 // extern(C) alias personalityImpl ...; would be nice 129 version (GNU_SjLj_Exceptions) { 130 extern (C) _Unwind_Reason_Code __gdc_personality_sj0(int iversion, 131 _Unwind_Action actions, 132 _Unwind_Exception_Class exception_class, 133 _Unwind_Exception *ue_header, 134 _Unwind_Context *context) { 135 return personalityImpl(iversion, actions, 136 exception_class != GDC_Exception_Class, 137 ue_header, context); 138 } 139 140 private int __builtin_eh_return_data_regno(int x) { return x; } 141 } else { 142 extern (C) _Unwind_Reason_Code __gdc_personality_v0(int iversion, 143 _Unwind_Action actions, 144 _Unwind_Exception_Class exception_class, 145 _Unwind_Exception *ue_header, 146 _Unwind_Context *context) { 147 return personalityImpl(iversion, actions, 148 exception_class != GDC_Exception_Class, 149 ue_header, context); 150 } 151 } 152 153 private _Unwind_Reason_Code personalityImpl(int iversion, 154 _Unwind_Action actions, 155 bool foreign_exception, 156 _Unwind_Exception *ue_header, 157 _Unwind_Context *context) 158 { 159 enum Found 160 { 161 nothing, 162 terminate, 163 cleanup, 164 handler 165 } 166 167 Found found_type; 168 lsda_header_info info; 169 OurUnwindException * xh = OurUnwindException.fromHeader(ue_header); 170 //ubyte *language_specific_data; 171 ubyte *p; 172 ubyte *action_record; 173 //int handler_switch_value; 174 _Unwind_Ptr /*landing_pad, */ip; 175 Phase1Info phase1; 176 177 if (iversion != 1) 178 return _URC_FATAL_PHASE1_ERROR; 179 180 // Shortcut for phase 2 found handler for domestic exception. 181 if (actions == (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME) 182 && ! foreign_exception) { 183 184 xh.restore(phase1); 185 found_type = (phase1.landingPad == 0 ? Found.terminate : Found.handler); 186 goto install_context; 187 } 188 189 phase1.languageSpecificData = cast(ubyte *) _Unwind_GetLanguageSpecificData( context ); 190 191 // If no LSDA, then there are no handlers or cleanups. 192 if ( ! phase1.languageSpecificData ) 193 { 194 return _URC_CONTINUE_UNWIND; 195 } 196 197 // Parse the LSDA header 198 p = parse_lsda_header(context, phase1.languageSpecificData, & info); 199 info.ttype_base = base_of_encoded_value(info.ttype_encoding, context); 200 ip = _Unwind_GetIP(context) - 1; 201 phase1.landingPad = 0; 202 action_record = null; 203 phase1.handlerSwitchValue = 0; 204 205 version (GNU_SjLj_Exceptions) { 206 // The given "IP" is an index into the call-site table, with two 207 // exceptions -- -1 means no-action, and 0 means terminate. But 208 // since we're using uleb128 values, we've not got random access 209 // to the array. 210 if (cast(int) ip < 0) 211 return _URC_CONTINUE_UNWIND; 212 else if (ip == 0) 213 { 214 // Fall through to set found_terminate. 215 } 216 else 217 { 218 _Unwind_Word cs_lp, cs_action; 219 do 220 { 221 p = read_uleb128 (p, &cs_lp); 222 p = read_uleb128 (p, &cs_action); 223 } 224 while (--ip); 225 226 // Can never have null landing pad for sjlj -- that would have 227 // been indicated by a -1 call site index. 228 phase1.landingPad = cs_lp + 1; 229 if (cs_action) 230 action_record = info.action_table + cs_action - 1; 231 goto found_something; 232 } 233 } else { 234 while (p < info.action_table) { 235 _Unwind_Ptr cs_start, cs_len, cs_lp; 236 _Unwind_Word cs_action; 237 238 // Note that all call-site encodings are "absolute" displacements. 239 p = read_encoded_value (null, info.call_site_encoding, p, &cs_start); 240 p = read_encoded_value (null, info.call_site_encoding, p, &cs_len); 241 p = read_encoded_value (null, info.call_site_encoding, p, &cs_lp); 242 p = read_uleb128 (p, &cs_action); 243 244 // The table is sorted, so if we've passed the ip, stop. 245 if (ip < info.Start + cs_start) 246 p = info.action_table; 247 else if (ip < info.Start + cs_start + cs_len) 248 { 249 if (cs_lp) 250 phase1.landingPad = info.LPStart + cs_lp; 251 if (cs_action) 252 action_record = info.action_table + cs_action - 1; 253 goto found_something; 254 } 255 } 256 } 257 258 // If ip is not present in the table, call terminate. This is for 259 // a destructor inside a cleanup, or a library routine the compiler 260 // was not expecting to throw. 261 found_type = Found.terminate; 262 goto do_something; 263 264 found_something: 265 if (phase1.landingPad == 0) 266 { 267 // If ip is present, and has a null landing pad, there are 268 // no cleanups or handlers to be run. 269 found_type = Found.nothing; 270 } 271 else if (action_record == null) 272 { 273 // If ip is present, has a non-null landing pad, and a null 274 // action table offset, then there are only cleanups present. 275 // Cleanups use a zero switch value, as set above. 276 found_type = Found.cleanup; 277 } 278 else 279 { 280 // Otherwise we have a catch handler or exception specification. 281 282 _Unwind_Sword ar_filter, ar_disp; 283 ClassInfo throw_type, catch_type; 284 bool saw_cleanup = false; 285 bool saw_handler = false; 286 287 // During forced unwinding, we only run cleanups. With a foreign 288 // exception class, there's no exception type. 289 // ??? What to do about GNU Java and GNU Ada exceptions. 290 291 if ((actions & _UA_FORCE_UNWIND) 292 || foreign_exception) 293 throw_type = null; 294 else 295 throw_type = xh.obj.classinfo; 296 297 while (1) 298 { 299 p = action_record; 300 p = read_sleb128 (p, &ar_filter); 301 read_sleb128 (p, &ar_disp); 302 303 if (ar_filter == 0) 304 { 305 // Zero filter values are cleanups. 306 saw_cleanup = true; 307 } 308 else if (ar_filter > 0) 309 { 310 // Positive filter values are handlers. 311 catch_type = get_classinfo_entry(& info, ar_filter); 312 313 // Null catch type is a catch-all handler; we can catch foreign 314 // exceptions with this. Otherwise we must match types. 315 // D Note: will be performing dynamic cast twice, potentially 316 // Once here and once at the landing pad .. unless we cached 317 // here and had a begin_catch call. 318 if (! catch_type 319 || (throw_type 320 && _d_isbaseof( throw_type, catch_type ))) 321 { 322 saw_handler = true; 323 break; 324 } 325 } 326 else 327 { 328 // D Note: we don't have these... 329 break; 330 /* 331 // Negative filter values are exception specifications. 332 // ??? How do foreign exceptions fit in? As far as I can 333 // see we can't match because there's no __cxa_exception 334 // object to stuff bits in for __cxa_call_unexpected to use. 335 // Allow them iff the exception spec is non-empty. I.e. 336 // a throw() specification results in __unexpected. 337 if (throw_type 338 ? ! check_exception_spec (&info, throw_type, thrown_ptr, 339 ar_filter) 340 : empty_exception_spec (&info, ar_filter)) 341 { 342 saw_handler = true; 343 break; 344 } 345 */ 346 } 347 348 if (ar_disp == 0) 349 break; 350 action_record = p + ar_disp; 351 } 352 353 if (saw_handler) 354 { 355 phase1.handlerSwitchValue = ar_filter; 356 found_type = Found.handler; 357 } 358 else 359 found_type = (saw_cleanup ? Found.cleanup : Found.nothing); 360 } 361 362 do_something: 363 if (found_type == Found.nothing) 364 { 365 return _URC_CONTINUE_UNWIND; 366 } 367 368 if (actions & _UA_SEARCH_PHASE) 369 { 370 if (found_type == Found.cleanup) 371 { 372 return _URC_CONTINUE_UNWIND; 373 } 374 375 // For domestic exceptions, we cache data from phase 1 for phase 2. 376 if (! foreign_exception) 377 xh.save(context, phase1); 378 return _URC_HANDLER_FOUND; 379 } 380 381 install_context: 382 if ( (actions & _UA_FORCE_UNWIND) 383 || foreign_exception) { 384 385 if (found_type == Found.terminate) { 386 dehTerminate(); 387 } else if (phase1.handlerSwitchValue < 0) { 388 dehUnexpected(); 389 } 390 391 } else { 392 if (found_type == Found.terminate) 393 { 394 dehBeginCatch (&xh.unwindHeader); 395 dehTerminate(); // __terminate (xh.terminateHandler); 396 } 397 } 398 399 static if (is(typeof(__builtin_extend_pointer))) 400 /* For targets with pointers smaller than the word size, we must extend the 401 pointer, and this extension is target dependent. */ 402 _Unwind_SetGR (context, __builtin_eh_return_data_regno (0), 403 __builtin_extend_pointer (&xh.unwindHeader)); 404 else 405 _Unwind_SetGR (context, __builtin_eh_return_data_regno (0), 406 cast(_Unwind_Ptr) &xh.unwindHeader); 407 _Unwind_SetGR (context, __builtin_eh_return_data_regno (1), 408 phase1.handlerSwitchValue); 409 _Unwind_SetIP (context, phase1.landingPad); 410 411 return _URC_INSTALL_CONTEXT; 412 } 413 414 struct lsda_header_info 415 { 416 _Unwind_Ptr Start; 417 _Unwind_Ptr LPStart; 418 _Unwind_Ptr ttype_base; 419 ubyte *TType; 420 ubyte *action_table; 421 ubyte ttype_encoding; 422 ubyte call_site_encoding; 423 } 424 425 private ubyte * 426 parse_lsda_header (_Unwind_Context *context, ubyte *p, 427 lsda_header_info *info) 428 { 429 _Unwind_Word tmp; 430 ubyte lpstart_encoding; 431 432 info.Start = (context ? _Unwind_GetRegionStart (context) : 0); 433 434 // Find @LPStart, the base to which landing pad offsets are relative. 435 lpstart_encoding = *p++; 436 if (lpstart_encoding != DW_EH_PE_omit) 437 p = read_encoded_value (context, lpstart_encoding, p, &info.LPStart); 438 else 439 info.LPStart = info.Start; 440 441 // Find @TType, the base of the handler and exception spec type data. 442 info.ttype_encoding = *p++; 443 if (info.ttype_encoding != DW_EH_PE_omit) 444 { 445 p = read_uleb128 (p, &tmp); 446 info.TType = p + tmp; 447 } 448 else 449 info.TType = null; 450 451 // The encoding and length of the call-site table; the action table 452 // immediately follows. 453 info.call_site_encoding = *p++; 454 p = read_uleb128 (p, &tmp); 455 info.action_table = p + tmp; 456 457 return p; 458 } 459 460 private ClassInfo 461 get_classinfo_entry (lsda_header_info *info, _Unwind_Word i) 462 { 463 _Unwind_Ptr ptr; 464 465 i *= size_of_encoded_value (info.ttype_encoding); 466 read_encoded_value_with_base (info.ttype_encoding, info.ttype_base, 467 info.TType - i, &ptr); 468 469 return cast(ClassInfo)cast(void *)(ptr); 470 }