Changeset 475
- Timestamp:
- 01/05/11 22:09:15 (14 years ago)
- Files:
-
- trunk/src/rt/deh.d (modified) (9 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/src/rt/deh.d
r459 r475 15 15 import core.sys.windows.windows; 16 16 //import core.stdc.stdio; 17 17 18 18 enum EXCEPTION_DISPOSITION { 19 19 ExceptionContinueExecution, 20 20 ExceptionContinueSearch, 21 21 ExceptionNestedException, 22 22 ExceptionCollidedUnwind 23 23 } 24 24 25 /+ 25 26 enum { 26 27 EXCEPTION_EXECUTE_HANDLER = 1, 27 28 EXCEPTION_CONTINUE_SEARCH = 0, 28 29 EXCEPTION_CONTINUE_EXECUTION = -1 29 30 } 31 +/ 30 32 31 33 extern(Windows) 32 34 { 33 35 void RaiseException(DWORD, DWORD, DWORD, void *); 34 36 } 35 37 36 38 // used in EXCEPTION_RECORD 37 39 enum : DWORD { 38 40 STATUS_WAIT_0 = 0, 39 41 STATUS_ABANDONED_WAIT_0 = 0x00000080, … … 161 163 162 164 enum EXCEPTION_UNWIND = 6; // Flag to indicate if the system is unwinding 163 165 164 166 /* Windows Kernel function to initiate a system unwind. 165 167 166 168 Documentation for this function is severely lacking. 167 169 http://www.nynaeve.net/?p=99 states that the MSDN documentation is incorrect, 168 170 and gives a corrected form, but it's for x86_64 only. 169 171 http://www.microsoft.com/msj/0197/exception/exception.aspx says that it was 170 172 undocumented in 1997. 171 Presumably DMC reverse-engineered it, because it got the signature of this172 function wrong!!173 173 The pExceptRec is what will be passed to the language specific handler. 174 174 According to MSJ, the targetIp value is unused on Win32. 175 175 The 'valueForEAX' parameter should always be 0. 176 176 */ 177 177 extern(Windows) 178 178 void RtlUnwind(void *targetFrame, void *targetIp, EXCEPTION_RECORD *pExceptRec, void *valueForEAX); 179 179 180 180 alias int function() fp_t; // function pointer in ambient memory model 181 181 182 182 extern(C) 183 183 { 184 extern __gshared DWORD _except_list; 184 extern __gshared DWORD _except_list; // This is just FS:[0] 185 185 } 186 186 187 187 extern(C) 188 188 { 189 189 void _d_setUnhandled(Object); 190 190 void _d_createTrace(Object); 191 191 int _d_isbaseof(ClassInfo b, ClassInfo c); 192 192 } 193 194 /+ 195 196 Implementation of Structured Exception Handling in DMD-Windows 197 198 Every function which uses exception handling (a 'frame') has a thunk created 199 for it. This thunk is the 'language-specific handler'. 200 The thunks are created in the DMD backend, in nteh_framehandler() in nteh.c. 201 These thunks are of the form: 202 MOV EAX,&scope_table 203 JMP __d_framehandler 204 FS:[0] contains a singly linked list of all active handlers (they'll all be 205 thunks). The list is created on the stack. 206 At the end of this list is _except_handler3, a function in the DMC library. 207 Its signature is: 208 209 extern(C) 210 EXCEPTION_DISPOSITION _except_handler3(EXCEPTION_RECORD *eRecord, 211 DEstablisherFrame * frame,CONTEXT *context,void *dispatchercontext); 212 213 It may be unnecessary. I think it is included for compatibility with MSVC 214 exceptions? 215 216 Documentation of Windows SEH is hard to find. Here is a brief explanation: 217 218 When an exception is raised, the OS calls each handler in the FS:[0] list in 219 turn, looking for a catch block. It continues moving down the list, as long as 220 each handler indicates that it has not caught the exception. When a handler is 221 ready to catch the exception, it calls the OS function RtlUnwind. 222 This calls each function in the FS:[0] list again, this time indicating that it 223 is a 'unwind' call. All of the intervening finally blocks are run at this time. 224 The complicated case is a CollidedException, which happens when a finally block 225 throws an exception. The new exception needs to either replace the old one, or 226 be chained to the old one. 227 228 The other complexity comes from the fact that a single function may have 229 multiple try/catch/finally blocks. Hence, there's a 'handler table' created for 230 each function which uses exceptions. 231 +/ 193 232 194 233 // The layout of DEstablisherFrame is the same for C++ 195 234 196 235 struct DEstablisherFrame 197 236 { 198 237 void *prev; // pointer to previous exception list 199 238 void *handler; // pointer to routine for exception handler 200 239 DWORD table_index; // current index into handler_info[] 201 240 DWORD ebp; // this is EBP of routine 202 241 }; … … 227 266 }; 228 267 229 268 // Create one of these for each try-catch 230 269 struct DCatchInfo 231 270 { 232 271 uint ncatches; // number of catch blocks 233 272 DCatchBlock catch_block[1]; // data for each catch block 234 273 }; 235 274 236 275 // Macro to make our own exception code 237 template MAKE_EXCEPTION_CODE(int severity, int facility, int exception){ 276 template MAKE_EXCEPTION_CODE(int severity, int facility, int exception) 277 { 238 278 enum int MAKE_EXCEPTION_CODE = (((severity) << 30) | (1 << 29) | (0 << 28) | ((facility) << 16) | (exception)); 239 279 } 240 280 enum int STATUS_DIGITAL_MARS_D_EXCEPTION = MAKE_EXCEPTION_CODE!(3,'D',1); 241 281 242 282 243 283 /*********************************** 244 284 * The frame handler, this is called for each frame that has been registered 245 285 * in the OS except_list. 246 286 * Input: 247 287 * EAX the handler table for the frame 248 288 */ 249 289 extern(C) 250 290 EXCEPTION_DISPOSITION _d_framehandler( 251 EXCEPTION_RECORD *exception _record,292 EXCEPTION_RECORD *exceptionRecord, 252 293 DEstablisherFrame *frame, 253 294 CONTEXT *context, 254 void *dispatcher _context)255 { 256 DHandlerTable *handler _table;257 258 asm { mov handler _table,EAX; }259 260 if (exception _record.ExceptionFlags & EXCEPTION_UNWIND)295 void *dispatcherContext) 296 { 297 DHandlerTable *handlerTable; 298 299 asm { mov handlerTable,EAX; } 300 301 if (exceptionRecord.ExceptionFlags & EXCEPTION_UNWIND) 261 302 { 262 303 // Call all the finally blocks in this frame 263 _d_local_unwind(handler _table, frame, -1);304 _d_local_unwind(handlerTable, frame, -1); 264 305 } 265 306 else 266 307 { 267 308 // Jump to catch block if matching one is found 268 309 int ndx,prev_ndx,i; 269 310 DHandlerInfo *phi; 270 311 DCatchInfo *pci; 271 312 DCatchBlock *pcb; 272 313 uint ncatches; // number of catches in the current handler 273 314 Object pti; 274 315 ClassInfo ci; 275 316 276 317 ci = null; // only compute it if we need it 277 318 278 319 // walk through handler table, checking each handler 279 320 // with an index smaller than the current table_index 280 321 for (ndx = frame.table_index; ndx != -1; ndx = prev_ndx) 281 322 { 282 phi = &handler _table.handler_info[ndx];323 phi = &handlerTable.handler_info[ndx]; 283 324 prev_ndx = phi.prev_index; 284 325 if (phi.cioffset) 285 326 { 286 327 // this is a catch handler (no finally) 287 pci = cast(DCatchInfo *)(cast(ubyte *)handler _table + phi.cioffset);328 pci = cast(DCatchInfo *)(cast(ubyte *)handlerTable + phi.cioffset); 288 329 ncatches = pci.ncatches; 289 330 for (i = 0; i < ncatches; i++) 290 331 { 291 332 pcb = &pci.catch_block[i]; 292 333 293 334 if (!ci) 294 335 { 295 336 // This code must match the translation code 296 if (exception _record.ExceptionCode == STATUS_DIGITAL_MARS_D_EXCEPTION)337 if (exceptionRecord.ExceptionCode == STATUS_DIGITAL_MARS_D_EXCEPTION) 297 338 { 298 // printf("ei[0] = %p\n", exception _record.ExceptionInformation[0]);299 ci = (**(cast(ClassInfo **)(exception _record.ExceptionInformation[0])));339 // printf("ei[0] = %p\n", exceptionRecord.ExceptionInformation[0]); 340 ci = (**(cast(ClassInfo **)(exceptionRecord.ExceptionInformation[0]))); 300 341 } 301 342 else 302 343 ci = Throwable.typeinfo; 303 344 } 304 345 if (_d_isbaseof(ci, pcb.type)) 305 346 { 306 347 // Matched the catch type, so we've found the handler. 307 348 int regebp; 308 349 309 pti = _d_translate_se_to_d_exception(exception _record);350 pti = _d_translate_se_to_d_exception(exceptionRecord); 310 351 311 352 // Initialize catch variable 312 353 regebp = cast(int)&frame.ebp; // EBP for this frame 313 354 *cast(Object *)(regebp + (pcb.bpoffset)) = pti; 314 355 315 356 _d_setUnhandled(pti); 316 357 317 358 // Have system call all finally blocks in intervening frames 318 _d_global_unwind(frame, exception _record);359 _d_global_unwind(frame, exceptionRecord); 319 360 320 361 // Call all the finally blocks skipped in this frame 321 _d_local_unwind(handler _table, frame, ndx);362 _d_local_unwind(handlerTable, frame, ndx); 322 363 323 364 _d_setUnhandled(null); 324 365 325 366 frame.table_index = prev_ndx; // we are out of this handler 326 367 327 368 // Jump to catch block. Does not return. 328 369 { 329 370 uint catch_esp; 330 371 fp_t catch_addr; 331 372 332 373 catch_addr = cast(fp_t)(pcb.code); 333 catch_esp = regebp - handler _table.espoffset - fp_t.sizeof;374 catch_esp = regebp - handlerTable.espoffset - fp_t.sizeof; 334 375 asm 335 376 { 336 377 mov EAX,catch_esp; 337 378 mov ECX,catch_addr; 338 379 mov [EAX],ECX; 339 380 mov EBP,regebp; 340 381 mov ESP,EAX; // reset stack 341 382 ret; // jump to catch block 342 383 } 343 384 } … … 349 390 return EXCEPTION_DISPOSITION.ExceptionContinueSearch; 350 391 } 351 392 352 393 /*********************************** 353 394 * Exception filter for use in __try..__except block 354 395 * surrounding call to Dmain() 355 396 */ 356 397 357 398 int _d_exception_filter(EXCEPTION_POINTERS *eptrs, 358 399 int retval, 359 Object *exception _object)360 { 361 *exception _object = _d_translate_se_to_d_exception(eptrs.ExceptionRecord);400 Object *exceptionObject) 401 { 402 *exceptionObject = _d_translate_se_to_d_exception(eptrs.ExceptionRecord); 362 403 return retval; 363 404 } 364 405 365 406 /*********************************** 366 407 * Throw a D object. 367 408 */ 368 409 extern(C) 369 410 void _d_throwc(Object h) 370 411 { 371 412 //printf("_d_throw(h = %p, &h = %p)\n", h, &h); … … 374 415 //_d_setUnhandled(h); 375 416 RaiseException(STATUS_DIGITAL_MARS_D_EXCEPTION, 376 417 EXCEPTION_NONCONTINUABLE, 377 418 1, cast(void *)&h); 378 419 } 379 420 380 421 /*********************************** 381 422 * Converts a Windows Structured Exception code to a D Exception Object. 382 423 */ 383 424 384 Object _d_translate_se_to_d_exception(EXCEPTION_RECORD *exception _record)425 Object _d_translate_se_to_d_exception(EXCEPTION_RECORD *exceptionRecord) 385 426 { 386 427 Object pti; 387 428 // BUG: what if _d_newclass() throws an out of memory exception? 388 429 389 switch (exception _record.ExceptionCode) {430 switch (exceptionRecord.ExceptionCode) { 390 431 case STATUS_DIGITAL_MARS_D_EXCEPTION: 391 432 // Generated D exception 392 pti = cast(Object)cast(void *)(exception _record.ExceptionInformation[0]);433 pti = cast(Object)cast(void *)(exceptionRecord.ExceptionInformation[0]); 393 434 break; 394 435 395 436 case STATUS_INTEGER_DIVIDE_BY_ZERO: 396 437 pti = new Error("Integer Divide by Zero"); 397 438 break; 398 439 399 440 case STATUS_FLOAT_DIVIDE_BY_ZERO: 400 441 pti = new Error("Float Divide by Zero"); 401 442 break; 402 443 … … 434 475 435 476 case STATUS_FLOAT_UNDERFLOW: 436 477 pti = new Error("Floating Point Underflow"); 437 478 break; 438 479 439 480 case STATUS_FLOAT_STACK_CHECK: 440 481 pti = new Error("Floating Point Stack Check"); 441 482 break; 442 483 443 484 case STATUS_PRIVILEGED_INSTRUCTION: 444 if (*(cast(ubyte *)(exception _record.ExceptionAddress))==0xF4) { // HLT485 if (*(cast(ubyte *)(exceptionRecord.ExceptionAddress))==0xF4) { // HLT 445 486 pti = new Error("assert(0) or HLT instruction"); 446 487 } else { 447 488 pti = new Error("Privileged Instruction"); 448 489 } 449 490 break; 450 491 451 492 case STATUS_ILLEGAL_INSTRUCTION: 452 493 pti = new Error("Illegal Instruction"); 453 494 break; 454 495 … … 475 516 break; 476 517 } 477 518 _d_createTrace(pti); 478 519 return pti; 479 520 } 480 521 481 522 /+ 482 523 These next two functions are necessary for dealing with collided exceptions: 483 524 when an exception has been thrown during unwinding. This happens for example 484 525 when a throw statement was encountered inside a finally clause. 485 486 A description of the equivalent situation for x86_64 is given at487 http://www.nynaeve.net/?p=107, which states:488 This, in effect, requires that the compiler stop the current unwind operation489 and transfer control to the target location (which is usually within the490 function that contained the __finally that the programmer jumped out of).491 Nevertheless, the compiler still has to deal with the fact that it has been492 called in the context of an unwind operation, and as such it needs a way493 to 'break out' of the unwind call stack. This is done by executing a494 'local unwind', or an unwind to a location within the current function.495 In order to do this, the compiler calls a small, runtime-supplied helper496 function known as local_unwind. This function is described below, and is essentially497 an extremely thin wrapper around RtlUnwindEx that, in practice, adds no value other498 than providing some default argument values (and scratch space on the stack499 for RtlUnwindEx to use to store a CONTEXT structure).500 [snip brief asm code]501 When the compiler calls local_unwind as a result of the programmer502 breaking out of a __finally block in some fashion, then execution will503 eventually end up in RtlUnwindEx. From there, RtlUnwindEx eventually504 detects the operation as a collided unwind, once it unwinds past the505 original call to the original unwind handler that started the new unwind506 operation via local_unwind.507 508 526 +/ 509 527 510 528 extern(C) 511 529 EXCEPTION_DISPOSITION unwindCollisionExceptionHandler( 512 530 EXCEPTION_RECORD *ExceptionRecord, 513 531 DEstablisherFrame *frame, 514 532 CONTEXT *context, 515 533 void *dispatcherContext) 516 534 { 517 535 if (!(ExceptionRecord.ExceptionFlags & EXCEPTION_UNWIND)) … … 577 595 } 578 596 579 597 /+ According to http://www.microsoft.com/msj/0197/exception/exception.aspx, 580 598 global unwind is just a thin wrapper around RtlUnwind. 581 599 __global_unwind(void * pRegistFrame) 582 600 { 583 601 _RtlUnwind( pRegistFrame, 584 602 &__ret_label, 585 603 0, 0 ); 586 604 __ret_label: 605 } 606 Apparently Win32 doesn't use the return address anyway. 587 607 588 608 This code seems to be calling RtlUnwind( pFrame, &__retlabel, eRecord, 0); 589 Apparently Win32 doesn't use the return address anyway.590 }591 592 609 +/ 593 610 extern(C) 594 611 int _d_global_unwind(DEstablisherFrame *pFrame, EXCEPTION_RECORD *eRecord) 595 612 { 596 613 asm { 597 614 naked; 598 615 push EBP; 599 616 mov EBP,ESP; 600 617 push ECX; 601 618 push EBX; … … 636 653 637 654 /*********************************** 638 655 * The frame handler, this is called for each frame that has been registered 639 656 * in the OS except_list. 640 657 * Input: 641 658 * EAX the handler table for the frame 642 659 */ 643 660 644 661 extern(C) 645 662 EXCEPTION_DISPOSITION _d_monitor_handler( 646 EXCEPTION_RECORD *exception _record,663 EXCEPTION_RECORD *exceptionRecord, 647 664 DEstablisherFrame *frame, 648 665 CONTEXT *context, 649 void *dispatcher _context)650 { 651 if (exception _record.ExceptionFlags & EXCEPTION_UNWIND)666 void *dispatcherContext) 667 { 668 if (exceptionRecord.ExceptionFlags & EXCEPTION_UNWIND) 652 669 { 653 670 _d_monitorexit(cast(Object)cast(void *)frame.table_index); 654 671 } 655 672 else 656 673 { 657 674 } 658 675 return EXCEPTION_DISPOSITION.ExceptionContinueSearch; 659 676 } 660 677 661 678 /***********************************
