Changeset 482
- Timestamp:
- 01/07/11 22:11:15 (14 years ago)
- Files:
-
- trunk/src/rt/deh.d (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/src/rt/deh.d
r475 r482 170 170 and gives a corrected form, but it's for x86_64 only. 171 171 http://www.microsoft.com/msj/0197/exception/exception.aspx says that it was 172 172 undocumented in 1997. 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 alias int function() fp_t; // function pointer in ambient memory model181 182 180 extern(C) 183 181 { 184 182 extern __gshared DWORD _except_list; // This is just FS:[0] 185 183 } 186 184 187 185 extern(C) 188 186 { 189 187 void _d_setUnhandled(Object); 190 188 void _d_createTrace(Object); 191 189 int _d_isbaseof(ClassInfo b, ClassInfo c); … … 223 221 is a 'unwind' call. All of the intervening finally blocks are run at this time. 224 222 The complicated case is a CollidedException, which happens when a finally block 225 223 throws an exception. The new exception needs to either replace the old one, or 226 224 be chained to the old one. 227 225 228 226 The other complexity comes from the fact that a single function may have 229 227 multiple try/catch/finally blocks. Hence, there's a 'handler table' created for 230 228 each function which uses exceptions. 231 229 +/ 232 230 231 extern(C) 232 { 233 alias 234 EXCEPTION_DISPOSITION function ( 235 EXCEPTION_RECORD *exceptionRecord, 236 DEstablisherFrame *frame, 237 CONTEXT *context, 238 void *dispatcherContext) LanguageSpecificHandler; 239 } 240 241 233 242 // The layout of DEstablisherFrame is the same for C++ 234 243 235 244 struct DEstablisherFrame 236 245 { 237 void *prev;// pointer to previous exception list238 void *handler;// pointer to routine for exception handler239 DWORD table_index; // current index into handler_info[]240 DWORD ebp; // this is EBP of routine246 DEstablisherFrame *prev; // pointer to previous exception list 247 LanguageSpecificHandler handler; // pointer to routine for exception handler 248 DWORD table_index; // current index into handler_info[] 249 DWORD ebp; // this is EBP of routine 241 250 }; 242 251 243 252 struct DHandlerInfo 244 253 { 245 254 int prev_index; // previous table index 246 255 uint cioffset; // offset to DCatchInfo data from start of table (!=0 if try-catch) 247 256 void *finally_code; // pointer to finally code to execute 248 257 // (!=0 if try-finally) 249 258 }; 250 259 … … 294 301 CONTEXT *context, 295 302 void *dispatcherContext) 296 303 { 297 304 DHandlerTable *handlerTable; 298 305 299 306 asm { mov handlerTable,EAX; } 300 307 301 308 if (exceptionRecord.ExceptionFlags & EXCEPTION_UNWIND) 302 309 { 303 310 // Call all the finally blocks in this frame 304 _d_local_unwind(handlerTable, frame, -1 );311 _d_local_unwind(handlerTable, frame, -1, &unwindCollisionExceptionHandler); 305 312 } 306 313 else 307 314 { 308 315 // Jump to catch block if matching one is found 309 316 int ndx,prev_ndx,i; 310 317 DHandlerInfo *phi; 311 318 DCatchInfo *pci; 312 319 DCatchBlock *pcb; 313 320 uint ncatches; // number of catches in the current handler 314 321 Object pti; … … 352 359 // Initialize catch variable 353 360 regebp = cast(int)&frame.ebp; // EBP for this frame 354 361 *cast(Object *)(regebp + (pcb.bpoffset)) = pti; 355 362 356 363 _d_setUnhandled(pti); 357 364 358 365 // Have system call all finally blocks in intervening frames 359 366 _d_global_unwind(frame, exceptionRecord); 360 367 361 368 // Call all the finally blocks skipped in this frame 362 _d_local_unwind(handlerTable, frame, ndx );369 _d_local_unwind(handlerTable, frame, ndx, &searchCollisionExceptionHandler); 363 370 364 371 _d_setUnhandled(null); 365 372 366 373 frame.table_index = prev_ndx; // we are out of this handler 367 374 368 375 // Jump to catch block. Does not return. 369 376 { 370 377 uint catch_esp; 371 fp_t catch_addr; 372 373 catch_addr = cast(fp_t)(pcb.code); 378 alias void function() fp_t; // generic function pointer 379 fp_t catch_addr = cast(fp_t)(pcb.code); 374 380 catch_esp = regebp - handlerTable.espoffset - fp_t.sizeof; 375 381 asm 376 382 { 377 383 mov EAX,catch_esp; 378 384 mov ECX,catch_addr; 379 385 mov [EAX],ECX; 380 386 mov EBP,regebp; 381 387 mov ESP,EAX; // reset stack 382 388 ret; // jump to catch block 383 389 } … … 512 518 */ 513 519 // convert all other exception codes into a Win32Exception 514 520 default: 515 521 pti = new Error("Win32 Exception"); 516 522 break; 517 523 } 518 524 _d_createTrace(pti); 519 525 return pti; 520 526 } 521 527 522 /+ 523 These next two functions are necessary for dealing with collided exceptions: 524 when an exception has been thrown during unwinding. This happens for example 525 when a throw statement was encountered inside a finally clause. 526 +/ 528 /* 529 These next two functions are necessary for dealing with collided exceptions: 530 when an exception has been thrown during unwinding. This happens for example 531 when a throw statement was encountered inside a finally clause. 532 533 'frame' is the stack pointer giving the state we were in, when we made 534 the call to RtlUnwind. 535 When we return ExceptionCollidedUnwind, the OS initiates a new SEARCH 536 phase, using the new exception, and it begins this search from the frame we 537 provide in the 'dispatcherContext' output parameter. 538 We change the target frame pointer, by changing dispatcherContext. After this, we'll be 539 back at the start of a SEARCH phase, so we need cancel all existing operations. 540 There are two types of possible collisions. 541 (1) collision during a local unwind. That is, localunwind was called during the 542 SEARCH phase (without going through an additional call to RtlUnwind). 543 We need to cancel the original search pass, so we'll restart from 'frame'. 544 (2) collision during a global unwind. That is, localunwind was called from the UNWIND phase. 545 We need to cancel the unwind pass, AND we need to cancel the search pass that initiated it. 546 So, we need to restart from 'frame.prev'. 547 */ 548 549 extern (C) 550 EXCEPTION_DISPOSITION searchCollisionExceptionHandler( 551 EXCEPTION_RECORD *exceptionRecord, 552 DEstablisherFrame *frame, 553 CONTEXT *context, 554 void *dispatcherContext) 555 { 556 if (!(exceptionRecord.ExceptionFlags & EXCEPTION_UNWIND)) 557 return EXCEPTION_DISPOSITION.ExceptionContinueSearch; 558 559 // An exception has been thrown during unwinding. 560 // It happened during the SEARCH phase. 561 // We need to cancel the original search pass, so we'll restart from 'frame'. 562 *(cast(void **)dispatcherContext) = frame; 563 return EXCEPTION_DISPOSITION.ExceptionCollidedUnwind; 564 } 527 565 528 566 extern(C) 529 567 EXCEPTION_DISPOSITION unwindCollisionExceptionHandler( 530 568 EXCEPTION_RECORD *exceptionRecord, 531 569 DEstablisherFrame *frame, 532 570 CONTEXT *context, 533 571 void *dispatcherContext) 534 572 { 535 573 if (!(exceptionRecord.ExceptionFlags & EXCEPTION_UNWIND)) 536 574 return EXCEPTION_DISPOSITION.ExceptionContinueSearch; 537 // An exception has been thrown during unwinding (eg, a throw statement538 // was encountered inside a finally clause).539 // The target for unwinding needs to change.540 // Based on the code for RtlUnwind in http://www.microsoft.com/msj/0197/exception/exception.aspx,541 // the dispatcherContext is used to set the EXCEPTION_REGISTRATION to be used from now on.542 *(cast( DEstablisherFrame **)dispatcherContext) = frame;575 576 // An exception has been thrown during unwinding. 577 // It happened during the UNWIND phase. 578 // We need to cancel the unwind pass, AND we need to cancel the search 579 // pass that initiated the unwind. So, we need to restart from 'frame.prev'. 580 *(cast(void **)dispatcherContext) = frame.prev; 543 581 return EXCEPTION_DISPOSITION.ExceptionCollidedUnwind; 544 582 } 545 583 546 584 /************************************** 547 585 * Call finally blocks in the current stack frame until stop_index. 548 586 * This is roughly equivalent to _local_unwind() for C in \src\win32\ehsup.c 549 587 */ 550 588 extern(C) 551 589 void _d_local_unwind(DHandlerTable *handler_table, 552 DEstablisherFrame *frame, int stop_index )590 DEstablisherFrame *frame, int stop_index, LanguageSpecificHandler collisionHandler) 553 591 { 554 592 DHandlerInfo *phi; 555 593 DCatchInfo *pci; 556 594 int i; 557 595 // Set up a special exception handler to catch double-fault exceptions. 558 596 asm 559 597 { 560 598 push dword ptr -1; 561 599 push dword ptr 0; 562 push offset unwindCollisionExceptionHandler;600 push collisionHandler; 563 601 push dword ptr FS:_except_list; 564 602 mov FS:_except_list,ESP; 565 603 } 566 604 for (i = frame.table_index; i != -1 && i != stop_index; i = phi.prev_index) 567 605 { 568 606 phi = &handler_table.handler_info[i]; 569 607 if (phi.finally_code) 570 608 { 571 609 // Note that it is unnecessary to adjust the ESP, as the finally block 572 610 // accesses all items on the stack as relative to EBP.
