LONG PAL_RunFilters(PEXCEPTION_POINTERS pException) { PPAL_EXCEPTION_REGISTRATION pRegistration = PAL_GetBottommostRegistration(); LONG filterResult = EXCEPTION_CONTINUE_SEARCH; BOOL foundHandler= FALSE; while ((pRegistration != NULL) && !foundHandler) { char *thrownTypeStr = NULL; switch (pRegistration->typeOfHandler) { case PALExceptFilter: filterResult = (pRegistration->Handler) (pException, pRegistration->pvFilterParameter); switch (filterResult) { case EXCEPTION_EXECUTE_HANDLER: pRegistration->dwFlags |= PAL_EXCEPTION_FLAGS_UNWINDTARGET; foundHandler = TRUE; break; case EXCEPTION_CONTINUE_SEARCH: pRegistration->dwFlags &= ~PAL_EXCEPTION_FLAGS_UNWINDTARGET; break; case EXCEPTION_CONTINUE_EXECUTION: foundHandler = true; break; } break; case PALExcept: filterResult = (LONG)(SIZE_T)pRegistration->Handler; switch (filterResult) { case EXCEPTION_EXECUTE_HANDLER: pRegistration->dwFlags |= PAL_EXCEPTION_FLAGS_UNWINDTARGET; foundHandler = TRUE; break; case EXCEPTION_CONTINUE_SEARCH: pRegistration->dwFlags &= ~PAL_EXCEPTION_FLAGS_UNWINDTARGET; break; case EXCEPTION_CONTINUE_EXECUTION: foundHandler = TRUE; break; } break; case PALFinally: break; case CatchTyped: // The exception code must be right if (pException->ExceptionRecord->ExceptionCode != PAL_CPP_EXCEPTION_CODE) break; // Type thrown must end in "Exception *" thrownTypeStr = (char *) pException->ExceptionRecord->ExceptionInformation[0]; if (strcmp(thrownTypeStr + strlen(thrownTypeStr) - strlen("Exception *"), "Exception *") != 0) { pRegistration->dwFlags &= ~PAL_EXCEPTION_FLAGS_UNWINDTARGET; break; } // fallthru case CatchAll: pRegistration->dwFlags |= PAL_EXCEPTION_FLAGS_UNWINDTARGET; foundHandler = TRUE; filterResult = EXCEPTION_EXECUTE_HANDLER; break; } if (!foundHandler) pRegistration = pRegistration->Next; } return filterResult; };
/*++ Function: PAL_EndTryHelper Abstract: Clean up the SEH stack when leaving a try block, and jump to the next exception frame if we're unwinding Parameters : PPAL_EXCEPTION_REGISTRATION pRegistration : current exception frame int ExceptionCode : 0 if no exception occurred, nonzero if an exception occurred and this is a stack unwind Always returns 0 that can be used to prevent compiler optimizations on setjmp/longjmp --*/ int PALAPI PAL_EndTryHelper( IN OUT PPAL_EXCEPTION_REGISTRATION pRegistration, IN int ExceptionCode) { #if !DISABLE_EXCEPTIONS if( ExceptionCode == 0 ) { /* If ExceptionCode is zero, there was no exception, so we only need to pop the exception frame. */ SEH_TLS_INFO *seh_info = pthread_getspecific((DWORD) pSEHInfo); if( seh_info == NULL && (seh_info=SEHCreateThreadInfo()) == NULL) { ERROR("Cannot get thread info\n"); goto done; } if( (pRegistration == NULL) || (pRegistration != seh_info->bottom_frame) ) { ASSERT("Exception registration pointers don't match!\n"); goto done; } seh_info->bottom_frame = pRegistration->Next; } else if( pRegistration->dwFlags & PAL_EXCEPTION_FLAGS_UNWINDTARGET ) { /* Stop unwinding if this is the target frame */ /* If there was an exception, the current frame has already been popped before the longjmp */ if( (pRegistration == NULL) || (pRegistration->Next != PAL_GetBottommostRegistration()) ) { ASSERT("Exception registration pointers don't match!\n"); goto done; } TRACE("Exception handled by frame %p. Lowest frame is now %p\n", pRegistration, pRegistration->Next); } else { SEH_TLS_INFO *seh_info; TRACE("Unwinding in progress; looking for next frame\n"); /* If there was an exception, the current frame has already been popped before the longjmp */ if( (pRegistration == NULL) || (pRegistration->Next != PAL_GetBottommostRegistration()) ) { ASSERT("Exception registration pointers don't match!\n"); goto done; } SEHUnwind(); TRACE("Reached top of exception stack! Exception was not " "handled, terminating the process.\n"); if(!PALIsInitialized()) { /* The only way we can get here is if PAL_Terminate is currently running, but we haven't yet restored the default signal handlers. This isn't a problem if an exception handler accepts the exception, but if we reach here,TerminateProcess will have to use the "non-initialized" code path : save status and exit, without cleaning up shared memory etc. Not good, but no choice, so we indicate the problem but proceed anyway. */ WARN("Exception was raised during PAL termination!\n"); } seh_info = SEHGetThreadInfo(); /* exit code is exception code */ if(NULL == seh_info) { WARN("SEH thread data not available, no exception code available! " "using fake exit code\n"); TerminateProcess(GetCurrentProcess(), -1); } TerminateProcess(GetCurrentProcess(), seh_info->current_exception.ExceptionCode); } done: #endif // !DISABLE_EXCEPTIONS return 0; }
/*++ Function : SEHRaiseException Raise an exception given a specified exception information. Parameters : PEXCEPTION_POINTERS lpExceptionPointers : specification of exception to raise. int signal_code : signal that caused the exception, if applicable; 0 otherwise (no return value) --*/ void SEHRaiseException( PEXCEPTION_POINTERS lpExceptionPointers, int signal_code ) { PPAL_EXCEPTION_REGISTRATION frame; LPTOP_LEVEL_EXCEPTION_FILTER top_filter; LONG default_action; SEH_TLS_INFO *seh_info; LONG handler_retval; if(!lpExceptionPointers) { ASSERT("Invalid exception record!\n"); return; } TRACE("Raising exception %#x (record is %p)\n", lpExceptionPointers->ExceptionRecord->ExceptionCode, lpExceptionPointers); seh_info = SEHGetThreadInfo(); if(NULL == seh_info) { ASSERT("Couldn't get SEH thread data!\n"); } else { /* save copy of exception record for access after longjmp */ memcpy(&seh_info->current_exception, lpExceptionPointers->ExceptionRecord, sizeof seh_info->current_exception); } seh_info->signal_code = signal_code; /* Call exception filters until one returns EXCEPTION_EXECUTE_HANDLER */ TRACE("Looking for exception handler...\n"); frame = PAL_GetBottommostRegistration(); while( frame ) { if (frame->Handler == NULL ) { TRACE("Exception frame %p is a try/finally block; skipping.\n", frame); frame = frame->Next; continue; } if((LONG_PTR)frame->Handler != EXCEPTION_EXECUTE_HANDLER) { /* reset ENTRY nesting level back to zero while inside the callback... */ #if !_NO_DEBUG_MESSAGES_ { int old_level; old_level = DBG_change_entrylevel(0); #endif /* !_NO_DEBUG_MESSAGES_ */ /* since we're calling the application handler, let's reset to safe state so exception in the filter could be handled properly */ SEHSetSafeState(TRUE); handler_retval = frame->Handler(lpExceptionPointers, frame->pvFilterParameter); SEHSetSafeState(FALSE); /* ...and set nesting level back to what it was */ #if !_NO_DEBUG_MESSAGES_ DBG_change_entrylevel(old_level); } #endif /* !_NO_DEBUG_MESSAGES_ */ } else { handler_retval = (LONG)frame->Handler; } if (frame->dwFlags & PAL_EXCEPTION_FLAGS_UNWINDTARGET) { ASSERT("PAL_EXCEPTION_FLAGS_UNWINDTARGET is set\n"); } switch(handler_retval) { case EXCEPTION_EXECUTE_HANDLER: frame->dwFlags |= PAL_EXCEPTION_FLAGS_UNWINDTARGET; break; case EXCEPTION_CONTINUE_SEARCH: frame->dwFlags &= ~PAL_EXCEPTION_FLAGS_UNWINDTARGET; break; case EXCEPTION_CONTINUE_EXECUTION: TRACE("Filter returned EXCEPTION_CONTINUE_EXECUTION"); return; default: /* Filter has returned an invalid value */ ASSERT("Filter for frame %p has returned an invalid value!\n", frame); break; } if( frame->dwFlags & PAL_EXCEPTION_FLAGS_UNWINDONLY) { memcpy(frame->ReservedForPAL, lpExceptionPointers->ExceptionRecord, min(sizeof(EXCEPTION_RECORD), PAL_TRY_LOCAL_SIZE)); } if( frame->dwFlags & PAL_EXCEPTION_FLAGS_UNWINDTARGET) { TRACE("Filter for frame %p has accepted the exception.\n", frame); break; } TRACE("Filter for frame %p has refused the exception.\n", frame); frame = frame->Next; } /* once we get here we can assume the exception stack is (reasonably) valid, so we can allow signals to be handled again */ SEHSetSafeState(TRUE); /* If a handler is found, begin unwinding by longjmp()ing to the first termination handler */ if( frame ) { SEHUnwind(); ASSERT("SEHUnwind returned, even though an exception handler had " "accepted the exception!\n"); } /* No handler found : start default handling */ TRACE("No handler found for exception. Using default behavior.\n"); /* Call application-defined top-level filter. */ top_filter = pTopFilter; if(top_filter) { TRACE("Calling application-defined top-level filter\n"); /* reset ENTRY nesting level back to zero while inside the callback... */ #if !_NO_DEBUG_MESSAGES_ { int old_level; old_level = DBG_change_entrylevel(0); #endif /* !_NO_DEBUG_MESSAGES_ */ default_action = top_filter(lpExceptionPointers); /* ...and set nesting level back to what it was */ #if !_NO_DEBUG_MESSAGES_ DBG_change_entrylevel(old_level); } #endif /* !_NO_DEBUG_MESSAGES_ */ switch( default_action ) { case EXCEPTION_CONTINUE_SEARCH: break; case EXCEPTION_EXECUTE_HANDLER: break; default: ASSERT("Application-defined top-level exception filter returned " "invalid value %d\n", default_action); default_action = EXCEPTION_CONTINUE_SEARCH; break; } } else { default_action = EXCEPTION_CONTINUE_SEARCH; } WARN("Exception %08x not handled; process will be terminated.\n", lpExceptionPointers->ExceptionRecord->ExceptionCode); SEHUnwind(); /* Exception not handled, no termination handlers to execute : terminate */ TRACE("Reached top of exception stack! Exception was not handled, " "terminating the process.\n"); if(!PALIsInitialized()) { /* The only way we can get here is if PAL_Terminate is currently running, but we haven't yet restored the default signal handlers. This isn't a problem if an exception handler accepts the exception, but if we reach here, TerminateProcess will have to use the "non-initialized" code path : save status and exit, wihtout cleaning up shared memory etc. Not good, but no choice, so we indicate the problem but proceed anyway. */ WARN("Exception was raised during PAL termination!\n"); } else if(0 != signal_code) { // Terminate unconditionally. The application is not in // a safe state. PROCCleanupProcess(TRUE); #if HAVE_MACH_EXCEPTIONS SEHCleanupExceptionPort(); #else SEHCleanupSignals(); #endif /* signal handlers uninstalled : let the signal be raised again */ return; } /* exit code is exception code */ TerminateProcess(GetCurrentProcess(), lpExceptionPointers->ExceptionRecord->ExceptionCode); ASSERT("TerminateProcess() returned!\n"); }
static void SEHUnwind() { PAL_EXCEPTION_REGISTRATION *jmp_frame; EXCEPTION_POINTERS ep; LONG retval; SEH_TLS_INFO *seh_info; jmp_frame = PAL_GetBottommostRegistration(); while(NULL != jmp_frame) { /* finally blocks have no filter */ if( jmp_frame->Handler == NULL ) { TRACE("Found termination handler frame (%p)\n", jmp_frame); break; } if(jmp_frame->dwFlags & PAL_EXCEPTION_FLAGS_UNWINDONLY) { jmp_frame->dwFlags &= ~PAL_EXCEPTION_FLAGS_UNWINDTARGET; PAL_SetBottommostRegistration(jmp_frame); ep.ExceptionRecord = (PEXCEPTION_RECORD) jmp_frame->ReservedForPAL; ep.ExceptionRecord->ExceptionFlags |= EXCEPTION_UNWINDING; ep.ContextRecord = NULL; /* reset ENTRY nesting level back to zero while inside the callback... */ #if !_NO_DEBUG_MESSAGES_ { int old_level; old_level = DBG_change_entrylevel(0); #endif /* !_NO_DEBUG_MESSAGES_ */ retval = jmp_frame->Handler(&ep, jmp_frame->pvFilterParameter); /* ...and set nesting level back to what it was, (if callback returned) */ #if !_NO_DEBUG_MESSAGES_ DBG_change_entrylevel(old_level); } #endif /* !_NO_DEBUG_MESSAGES_ */ if(EXCEPTION_CONTINUE_SEARCH!=retval) { ASSERT("Win32 EH filter returned %d instead of " "EXCEPTION_CONTINUE_SEARCH (%d)!\n", retval, EXCEPTION_CONTINUE_SEARCH); break; /* because what else can we do? */ } if (jmp_frame->dwFlags & PAL_EXCEPTION_FLAGS_UNWINDTARGET) { ASSERT("PAL_EXCEPTION_FLAGS_UNWINDTARGET is set\n"); } } else if( jmp_frame->dwFlags & PAL_EXCEPTION_FLAGS_UNWINDTARGET ) { TRACE("Found unwind target frame (%p)\n", jmp_frame); break; } jmp_frame = jmp_frame->Next; } if (jmp_frame) { TRACE("Jumping to exception frame %p\n", jmp_frame); /* remove the target frame from the chain : if there's an exception within the handler, we don't want it to be sent back to the same handler */ PAL_SetBottommostRegistration(jmp_frame->Next); /* reset ENTRY nesting level back to zero before longjmping */ #if !_NO_DEBUG_MESSAGES_ { int old_level; old_level = DBG_change_entrylevel(0); #endif /* !_NO_DEBUG_MESSAGES_ */ siglongjmp((LPVOID)jmp_frame->ReservedForPAL,1); #if !_NO_DEBUG_MESSAGES_ } #endif /* !_NO_DEBUG_MESSAGES_ */ } PAL_SetBottommostRegistration(NULL); seh_info = SEHGetThreadInfo(); /* exit code is exception code */ if(NULL == seh_info) { WARN("SEH thread data not available, no exception code available! " "using fake exit code\n"); TerminateProcess(GetCurrentProcess(),-1); } if(0 == seh_info->signal_code) { TerminateProcess(GetCurrentProcess(), seh_info->current_exception.ExceptionCode); } else { int signal_code; TRACE("unhandled signal : shutting down the PAL and re-raising the " "signal\n"); signal_code = seh_info->signal_code; // Terminate unconditionally. The application is not in a // safe state. PROCCleanupProcess(TRUE); #if HAVE_MACH_EXCEPTIONS SEHCleanupExceptionPort(); #else SEHCleanupSignals(); #endif /* signal handlers now uninstalled : raise the signal again */ kill(gPID, signal_code); } }