/* $Id: exceptions.cpp,v 1.74 2004-01-20 13:41:10 sandervl Exp $ */ /* * Win32 Exception functions for OS/2 * * Ported Wine exception handling code * * Copyright 1998 Sander van Leeuwen (OS/2 port) * * * Project Odin Software License can be found in LICENSE.TXT * * * (dlls\ntdll\exception.c) * * Copyright 1999 Turchanov Sergey * Copyright 1999 Alexandre Julliard * * (win32\except.c) * * Win32 exception functions * * Copyright (c) 1996 Onno Hovers, (onno@stack.urc.tue.nl) * * Notes: * What really happens behind the scenes of those new * __try{...}__except(..){....} and * __try{...}__finally{...} * statements is simply not documented by Microsoft. There could be different * reasons for this: * One reason could be that they try to hide the fact that exception * handling in Win32 looks almost the same as in OS/2 2.x. * Another reason could be that Microsoft does not want others to write * binary compatible implementations of the Win32 API (like us). * * Whatever the reason, THIS SUCKS!! Ensuring portabilty or future * compatability may be valid reasons to keep some things undocumented. * But exception handling is so basic to Win32 that it should be * documented! * * Fixmes: * -Most functions need better parameter checking. * -I do not know how to handle exceptions within an exception handler. * or what is done when ExceptionNestedException is returned from an * exception handler * -Real exceptions are not yet implemented. only the exception functions * are implemented. A real implementation needs some new code in * loader/signal.c. There would also be a need for showing debugging * information in UnhandledExceptionFilter. * */ #define INCL_MISC #define INCL_BASE #define INCL_WIN #define INCL_WINBUTTONS #include //Odin32 OS/2 api wrappers #include #include #include #include #include #include #include "exceptions.h" #include "exceptutil.h" #include #include "mmap.h" #include #include #include "oslibexcept.h" #include "oslibmem.h" #include "exceptstackdump.h" #include "hmthread.h" #include "WinImageBase.h" #include "WinDllBase.h" #include "WinExeBase.h" /* Really lazy! But, including wincon.h means lot's of missing COORD. */ #define CTRL_C_EVENT 0 //#include #define CTRL_BREAK_EVENT 1 //#include #include "console.h" #include "initterm.h" #define DBG_LOCALLOG DBG_exceptions #include "dbglocal.h" #include <_ras.h> #ifdef WITH_KLIB /* quick and dirty - don't wanna mess with includes. */ typedef enum { enmRead, enmWrite, enmUnknown } ENMACCESS; BOOL _Optlink kHeapDbgException(void * pvAccess, ENMACCESS enmAccess, void * pvIP, void * pvOS); #endif /* Exception record for handling exceptions happening inside exception handlers */ typedef struct { WINEXCEPTION_FRAME frame; WINEXCEPTION_FRAME *prevFrame; } EXC_NESTED_FRAME; //Global Process Unhandled exception filter static LPTOP_LEVEL_EXCEPTION_FILTER CurrentUnhExceptionFlt = NULL; static UINT CurrentErrorMode = 0; static PEXCEPTION_HANDLER StartupCodeHandler = NULL; extern "C" { PWINEXCEPTION_FRAME GetExceptionRecord(ULONG offset, ULONG segment); LONG WIN32API UnhandledExceptionFilter(PWINEXCEPTION_POINTERS lpexpExceptionInfo); void KillWin32Process(void); static void sprintfException(PEXCEPTIONREPORTRECORD pERepRec, PEXCEPTIONREGISTRATIONRECORD pERegRec, PCONTEXTRECORD pCtxRec, PVOID p, PSZ szTrapDump); int __cdecl __seh_handler(PVOID pRec, PVOID pFrame, PVOID pContext, PVOID pVoid); int __cdecl __seh_handler_win32(PWINEXCEPTION_RECORD pRec, PWINEXCEPTION_FRAME pFrame, PCONTEXTRECORD pContext, PVOID pVoid); PWINEXCEPTION_FRAME __cdecl __seh_get_prev_frame_win32(PWINEXCEPTION_FRAME pFrame); #ifdef DEBUG static void PrintWin32ExceptionChain(PWINEXCEPTION_FRAME pframe); #else #define PrintWin32ExceptionChain(a) #endif /***************************************************************************** * Name : UINT SetErrorMode * Purpose : * Parameters: UINT fuErrorMode * Variables : * Result : * Remark : * Status : * * Author : Sander van Leeuwen [Tue, 1999/07/01 09:00] *****************************************************************************/ UINT WIN32API SetErrorMode(UINT fuErrorMode) { UINT oldmode = CurrentErrorMode; dprintf(("KERNEL32: SetErrorMode(%08xh)\n", fuErrorMode)); CurrentErrorMode = fuErrorMode; if(fuErrorMode & SEM_FAILCRITICALERRORS || fuErrorMode & SEM_NOOPENFILEERRORBOX) DosError(FERR_DISABLEHARDERR); else DosError(FERR_ENABLEHARDERR); // SEM_NOGPFAULTERRORBOX and SEM_NOALIGNMENTFAULTEXCEPT --> UnhandledExceptionFilter() return(oldmode); } #ifdef __EMX__ static inline WINEXCEPTION_FRAME * EXC_push_frame( WINEXCEPTION_FRAME *frame ) { // TODO: rewrite in assembly TEB *teb = GetThreadTEB(); frame->Prev = (PWINEXCEPTION_FRAME)teb->except; teb->except = frame; return frame->Prev; } static inline WINEXCEPTION_FRAME * EXC_pop_frame( WINEXCEPTION_FRAME *frame ) { // TODO: rewrite in assembly TEB *teb = GetThreadTEB(); teb->except = frame->Prev; return frame->Prev; } #endif /***************************************************************************** * Name : VOID __cdecl OS2RaiseException * Purpose : Unwinds exception handlers (heavily influenced by Wine) * Parameters: ... * Variables : * Result : * Remark : * Status : * * Author : Sander van Leeuwen [Tue, 1999/07/01 09:00] *****************************************************************************/ void WIN32API RtlRaiseException(WINEXCEPTION_RECORD *rec, WINCONTEXT *context); VOID __cdecl OS2RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags, DWORD cArguments, DWORD *lpArguments, DWORD eip, DWORD esp, DWORD ebp, DWORD flags, DWORD eax, DWORD ebx, DWORD ecx, DWORD edx, DWORD edi, DWORD esi, DWORD cs, DWORD ds, DWORD es, DWORD fs, DWORD gs, DWORD ss) { WINEXCEPTION_RECORD record; WINEXCEPTION_POINTERS ExceptionInfo; WINCONTEXT context; int rc; int i; dprintf(("KERNEL32: RaiseException(%08xh)\n", dwExceptionCode)); memset(&record, 0, sizeof(record)); /* compose an exception record */ record.ExceptionCode = dwExceptionCode; record.ExceptionFlags = dwExceptionFlags; record.ExceptionRecord = NULL; record.NumberParameters = cArguments; record.ExceptionAddress = (LPVOID)eip; memset(&context, 0, sizeof(context)); context.ContextFlags = WINCONTEXT_FULL; //segments, integer, control context.SegGs = gs; context.SegFs = fs; context.SegEs = es; context.SegDs = ds; context.Edi = edi; context.Esi = esi; context.Ebx = ebx; context.Edx = edx; context.Ecx = ecx; context.Eax = eax; context.Ebp = ebp; context.Eip = eip; context.SegCs = cs; context.EFlags = flags; context.Esp = esp; context.SegSs = ss; if(lpArguments) { for(i=0; iExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND)) return ExceptionContinueSearch; /* We shouldn't get here so we store faulty frame in dispatcher */ *(PWINEXCEPTION_FRAME*)dispatcher = ((EXC_NESTED_FRAME*)frame)->prevFrame; return ExceptionNestedException; } /******************************************************************* * EXC_UnwindHandler * * Handler for exceptions happening inside an unwind handler. */ static DWORD WIN32API EXC_UnwindHandler( WINEXCEPTION_RECORD *rec, WINEXCEPTION_FRAME *frame, // WINCONTEXT *context, WINEXCEPTION_FRAME **dispatcher ) WINCONTEXT *context, LPVOID dispatcher ) { if (!(rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))) return ExceptionContinueSearch; /* We shouldn't get here so we store faulty frame in dispatcher */ *(PWINEXCEPTION_FRAME*)dispatcher = ((EXC_NESTED_FRAME*)frame)->prevFrame; return ExceptionCollidedUnwind; } #ifndef __EMX__ extern "C" DWORD EXC_CallHandler( WINEXCEPTION_RECORD *record, WINEXCEPTION_FRAME *frame, WINCONTEXT *context, WINEXCEPTION_FRAME **dispatcher, PEXCEPTION_HANDLER handler, PEXCEPTION_HANDLER nested_handler); #else /******************************************************************* * EXC_CallHandler * * Call an exception handler, setting up an exception frame to catch exceptions * happening during the handler execution. * WARNING: * Please do not change the first 4 parameters order in any way - some exceptions handlers * rely on Base Pointer (EBP) to have a fixed position related to the exception frame */ static DWORD EXC_CallHandler( WINEXCEPTION_RECORD *record, WINEXCEPTION_FRAME *frame, WINCONTEXT *context, WINEXCEPTION_FRAME **dispatcher, PEXCEPTION_HANDLER handler, PEXCEPTION_HANDLER nested_handler) { EXC_NESTED_FRAME newframe; DWORD ret; newframe.frame.Handler = nested_handler; newframe.prevFrame = frame; EXC_push_frame( &newframe.frame ); dprintf(("KERNEL32: Calling handler at %p code=%lx flags=%lx\n", handler, record->ExceptionCode, record->ExceptionFlags)); ret = handler( record, frame, context, dispatcher ); dprintf(("KERNEL32: Handler returned %lx\n", ret)); EXC_pop_frame( &newframe.frame ); return ret; } #endif //****************************************************************************** //****************************************************************************** DWORD RtlDispatchException(WINEXCEPTION_RECORD *pRecord, WINCONTEXT *pContext) { PWINEXCEPTION_FRAME pFrame, pPrevFrame, dispatch, nested_frame; int rc; // get chain of exception frames rc = ExceptionContinueSearch; nested_frame = NULL; TEB *winteb = GetThreadTEB(); pFrame = (PWINEXCEPTION_FRAME)winteb->except; dprintf(("KERNEL32: RtlDispatchException entered")); PrintWin32ExceptionChain(pFrame); // walk the exception chain while( (pFrame != NULL) && ((ULONG)((ULONG)pFrame & 0xFFFFF000) != 0xFFFFF000) ) { pPrevFrame = __seh_get_prev_frame_win32(pFrame); dprintf(("KERNEL32: RtlDispatchException - pframe=%08X, pframe->Prev=%08X", pFrame, pPrevFrame)); if (pFrame == pPrevFrame) { dprintf(("KERNEL32: RtlDispatchException - Invalid exception frame!!")); return 0; } dispatch=0; /* Check frame address */ if (((void*)pFrame < winteb->stack_low) || ((void*)(pFrame+1) > winteb->stack_top) || (int)pFrame & 3) { dprintf(("Invalid stack! low=%08X, top=%08X, pframe = %08X", winteb->stack_low, winteb->stack_top, pFrame)); pRecord->ExceptionFlags |= EH_STACK_INVALID; break; } /* call handler */ if (pFrame->Handler) { rc = EXC_CallHandler(pRecord, pFrame, pContext, &dispatch, pFrame->Handler, EXC_RaiseHandler); } else { dprintf(("pFrame->Handler is NULL!!!!!")); rc = ExceptionContinueSearch; } PrintWin32ExceptionChain(pFrame); if (pFrame == nested_frame) { /* no longer nested */ nested_frame = NULL; pRecord->ExceptionFlags &= ~EH_NESTED_CALL; } switch(rc) { case ExceptionContinueExecution: if (!(pRecord->ExceptionFlags & EH_NONCONTINUABLE)) { dprintf(("KERNEL32: RtlDispatchException returns %#x (ContinueExecution)", rc)); return rc; } break; case ExceptionContinueSearch: break; case ExceptionNestedException: if (nested_frame < dispatch) nested_frame = dispatch; pRecord->ExceptionFlags |= EH_NESTED_CALL; break; default: break; } dprintf(("KERNEL32: RtlDispatchException - going from frame %08X to previous frame %08X", pFrame, pPrevFrame)); if (pFrame == pPrevFrame) { dprintf(("KERNEL32: RtlDispatchException - Invalid exception frame!!")); break; } pFrame = pPrevFrame; } dprintf(("KERNEL32: RtlDispatchException returns %#x", rc)); PrintWin32ExceptionChain(pFrame); return rc; } /***************************************************************************** * Name : int __cdecl OS2RtlUnwind * Purpose : Unwinds exception handlers (heavily influenced by Wine) * Parameters: ... * Variables : * Result : * Remark : * Status : * * Author : Sander van Leeuwen [Tue, 1999/07/01 09:00] *****************************************************************************/ int __cdecl OS2RtlUnwind(PWINEXCEPTION_FRAME pEndFrame, LPVOID unusedEip, PWINEXCEPTION_RECORD pRecord, DWORD returnEax, DWORD eip, DWORD esp, DWORD ebp, DWORD flags, DWORD eax, DWORD ebx, DWORD ecx, DWORD edx, DWORD edi, DWORD esi, DWORD cs, DWORD ds, DWORD es, DWORD fs, DWORD gs, DWORD ss) { PWINEXCEPTION_FRAME frame, prevFrame, dispatch; WINEXCEPTION_RECORD record; //, newrec; WINCONTEXT context; DWORD rc; dprintf(("KERNEL32: RtlUnwind pEndFrame=%08X, unusedEip=%08X, pRecord=%08X, returnEax=%#x\n", pEndFrame, unusedEip, pRecord, returnEax)); TEB *winteb = GetThreadTEB(); if (!winteb) { dprintf(("KERNEL32: RtlUnwind TEB is NULL\n")); DebugInt3(); return 0; } memset(&context, 0, sizeof(context)); context.ContextFlags = WINCONTEXT_FULL; //segments, integer, control context.SegGs = gs; context.SegFs = fs; context.SegEs = es; context.SegDs = ds; context.Edi = edi; context.Esi = esi; context.Ebx = ebx; context.Edx = edx; context.Ecx = ecx; context.Eax = returnEax; context.Ebp = ebp; context.Eip = eip; context.SegCs = cs; context.EFlags = flags; context.Esp = esp; context.SegSs = ss; /* build an exception record, if we do not have one */ if(!pRecord) { memset(&record, 0, sizeof(record)); record.ExceptionCode = STATUS_UNWIND; record.ExceptionFlags = 0; record.ExceptionRecord = NULL; record.ExceptionAddress = (LPVOID)eip; record.NumberParameters = 0; pRecord = &record; } if(pEndFrame) pRecord->ExceptionFlags |= EH_UNWINDING; else pRecord->ExceptionFlags |= EH_UNWINDING | EH_EXIT_UNWIND; /* get chain of exception frames */ frame = (PWINEXCEPTION_FRAME)winteb->except; PrintWin32ExceptionChain(frame); while (((ULONG)((ULONG)frame & 0xFFFFF000) != 0xFFFFF000) && (frame != pEndFrame)) { prevFrame = __seh_get_prev_frame_win32(frame); /* Check frame address */ if (pEndFrame && (frame > pEndFrame)) { #if 0 // TODO newrec.ExceptionCode = STATUS_INVALID_UNWIND_TARGET; newrec.ExceptionFlags = EH_NONCONTINUABLE; newrec.ExceptionRecord = pRecord; newrec.NumberParameters = 0; RtlRaiseException(&newrec, NULL); #else dprintf(("KERNEL32: RtlUnwind terminating thread (invalid target).\n")); DosExit(EXIT_THREAD, 0); #endif } if (((void*)frame < winteb->stack_low) || ((void*)(frame+1) > winteb->stack_top) || (int)frame & 3) { #if 0 // TODO newrec.ExceptionCode = STATUS_BAD_STACK; newrec.ExceptionFlags = EH_NONCONTINUABLE; newrec.ExceptionRecord = pRecord; newrec.NumberParameters = 0; #else dprintf(("KERNEL32: RtlUnwind terminating thread (bad stack).\n")); DosExit(EXIT_THREAD, 0); #endif } /* Call handler */ dprintf(("KERNEL32: RtlUnwind - calling exception handler %08X", frame->Handler)); if(frame->Handler) { // ensure the Win32 FS (may be accessed directly in the handler) DWORD oldsel = SetReturnFS(winteb->teb_sel); rc = EXC_CallHandler(pRecord, frame, &context, &dispatch, frame->Handler, EXC_UnwindHandler ); // restore FS SetFS(oldsel); } else { dprintf(("pFrame->Handler is NULL!!!!!")); rc = ExceptionContinueSearch; } dprintf(("KERNEL32: RtlUnwind - handler returned %#x", rc)); switch (rc) { case ExceptionContinueSearch: break; case ExceptionCollidedUnwind: frame = dispatch; break; default: #if 0 // TODO newrec.ExceptionCode = STATUS_INVALID_DISPOSITION; newrec.ExceptionFlags = EH_NONCONTINUABLE; newrec.ExceptionRecord = pRecord; newrec.NumberParameters = 0; #else dprintf(("KERNEL32: RtlUnwind terminating thread (invalid disposition).\n")); DosExit(EXIT_THREAD, 0); #endif break; } dprintf(("KERNEL32: RtlUnwind (before)- frame=%08X, frame->Prev=%08X", frame, prevFrame)); winteb->except = (void*)prevFrame; frame = prevFrame; dprintf(("KERNEL32: RtlUnwind (after) - frame=%08X, frame->Prev=%08X", frame, ((ULONG)((ULONG)frame & 0xFFFFF000) != 0xFFFFF000) ? __seh_get_prev_frame_win32(frame) : (void*)0xFFFFFFFF)); } dprintf(("KERNEL32: RtlUnwind returning.\n")); PrintWin32ExceptionChain(frame); return(0); } /***************************************************************************** * Name : LONG WIN32API UnhandledExceptionFilter * Purpose : * Parameters: ... * Variables : * Result : * Remark : * Status : * * Author : Sander van Leeuwen [Tue, 1999/07/01 09:00] *****************************************************************************/ LONG WIN32API UnhandledExceptionFilter(PWINEXCEPTION_POINTERS lpexpExceptionInfo) { char szModName[16]; char message[128]; ULONG iObj; ULONG offObj; HMODULE hmod; DWORD rc; dprintf(("KERNEL32: Default UnhandledExceptionFilter, CurrentErrorMode=%X", CurrentErrorMode)); // We must not care about ErrorMode here!! The app expects that its own // UnhandledExceptionFilter will be cared even if it never touched ErrorMode. if(CurrentUnhExceptionFlt) // && !(CurrentErrorMode & (SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX))) { dprintf(("KERNEL32: Calling user UnhandledExceptionFilter")); rc = CurrentUnhExceptionFlt(lpexpExceptionInfo); if(rc != WINEXCEPTION_CONTINUE_SEARCH) return rc; } if (DosQueryModFromEIP(&hmod, &iObj, sizeof(szModName), szModName, &offObj, (ULONG)lpexpExceptionInfo->ExceptionRecord->ExceptionAddress)) sprintf(message, "Unhandled exception 0x%08lx at address 0x%08lx. (DQMFEIP rc=%d)", lpexpExceptionInfo->ExceptionRecord->ExceptionCode, lpexpExceptionInfo->ExceptionRecord->ExceptionAddress); else { if (iObj == -1) { /* fault in DosAllocMem allocated memory, hence PE loader.. */ Win32ImageBase * pMod; if (WinExe && WinExe->insideModule((ULONG)lpexpExceptionInfo->ExceptionRecord->ExceptionAddress)) pMod = WinExe; else pMod = Win32DllBase::findModuleByAddr((ULONG)lpexpExceptionInfo->ExceptionRecord->ExceptionAddress); if (pMod != NULL) { szModName[0] = '\0'; strncat(szModName, pMod->getModuleName(), sizeof(szModName) - 1); iObj = 0xFF; offObj = (ULONG)lpexpExceptionInfo->ExceptionRecord->ExceptionAddress - (ULONG)pMod->getInstanceHandle(); } } sprintf(message, "Unhandled exception 0x%08lx at address 0x%08lx.\r" "Mod: %s obj: 0x%2lx off:0x%08lx", lpexpExceptionInfo->ExceptionRecord->ExceptionCode, lpexpExceptionInfo->ExceptionRecord->ExceptionAddress, szModName, iObj, offObj); } /* This is very dangerous. Can hang PM. rc = WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, message, "Application Error", 0, MB_ABORTRETRYIGNORE | MB_ERROR); switch (rc) { case MBID_IGNORE: return WINEXCEPTION_CONTINUE_EXECUTION; case MBID_ABORT: case MBID_RETRY: default: return WINEXCEPTION_EXECUTE_HANDLER; } */ return WINEXCEPTION_EXECUTE_HANDLER; } /***************************************************************************** * Name : LPTOP_LEVEL_EXCEPTION_FILTER WIN32API SetUnhandledExceptionFilter * Purpose : * Parameters: ... * Variables : * Result : * Remark : * Status : * * Author : Sander van Leeuwen [Tue, 1999/07/01 09:00] *****************************************************************************/ LPTOP_LEVEL_EXCEPTION_FILTER WIN32API SetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter) { LPTOP_LEVEL_EXCEPTION_FILTER old = CurrentUnhExceptionFlt; dprintf(("KERNEL32: SetUnhandledExceptionFilter to %08X\n", lpTopLevelExceptionFilter)); CurrentUnhExceptionFlt = lpTopLevelExceptionFilter; return(old); } /***************************************************************************** * Name : KillWin32Process * Purpose : * Parameters: ... * Variables : * Result : * Remark : * Status : * * Author : Sander van Leeuwen [Tue, 1999/07/01 09:00] *****************************************************************************/ //****************************************************************************** VOID WIN32API ExitProcess(DWORD exitcode); //****************************************************************************** void KillWin32Process(void) { static BOOL fEntry = FALSE; if(!fExitProcess && fEntry == FALSE) { fEntry = TRUE; ExitProcess(666); return; } //Restore original OS/2 TIB selector RestoreOS2FS(); DosExit(EXIT_PROCESS, 666); } //***************************************************************************** //***************************************************************************** void KillWin32Thread(void) { // ExitThread(666); //Restore original OS/2 TIB selector RestoreOS2FS(); DosExit(EXIT_THREAD, 666); } //***************************************************************************** //***************************************************************************** static void sprintfException(PEXCEPTIONREPORTRECORD pERepRec, PEXCEPTIONREGISTRATIONRECORD pERegRec, PCONTEXTRECORD pCtxRec, PVOID p, PSZ szTrapDump) { if(pERepRec->ExceptionNum == XCPT_GUARD_PAGE_VIOLATION) { strcpy(szTrapDump, "Guard Page Violation"); return; } PCSZ pszExceptionName = ""; /* points to name/type excpt */ APIRET rc = XCPT_CONTINUE_SEARCH; /* excpt-dep. code */ BOOL fExcptSoftware = FALSE; /* software/hardware gen. exceptn */ BOOL fExcptFatal = TRUE; /* fatal exception ? */ BOOL fExcptPortable = TRUE; /* portability of exception */ PPIB pPIB; /* process information block */ PTIB pTIB; /* thread information block */ ULONG ulModule; /* module number */ ULONG ulObject; /* object number within the module */ CHAR szModule[260]; /* buffer for the module name */ ULONG ulOffset; /* offset within the object within the module */ char szLineException[512]; char szLineExceptionType[128]; szLineException[0] = 0; /* initialize */ szLineExceptionType[0] = 0; /* initialize */ switch(pERepRec->ExceptionNum) /* take according action */ { /* portable, non-fatal software-generated exceptions */ case XCPT_GUARD_PAGE_VIOLATION: strcpy(szLineException, "Guard Page Violation"); sprintf(szLineExceptionType, "R/W %08xh at %08xh.", pERepRec->ExceptionInfo[0], pERepRec->ExceptionInfo[1]); fExcptSoftware = TRUE; fExcptFatal = FALSE; rc = XCPT_CONTINUE_EXECUTION; break; case XCPT_UNABLE_TO_GROW_STACK: strcpy(szLineException, "Unable To Grow Stack"); fExcptSoftware = TRUE; fExcptFatal = FALSE; rc = XCPT_CONTINUE_EXECUTION; break; /* portable, fatal, hardware-generated exceptions */ case XCPT_ACCESS_VIOLATION: strcpy(szLineException, "Access Violation"); switch (pERepRec->ExceptionInfo[0]) { case XCPT_READ_ACCESS: sprintf (szLineExceptionType, "Read Access at address %08xh", pERepRec->ExceptionInfo[1]); break; case XCPT_WRITE_ACCESS: sprintf (szLineExceptionType, "Write Access at address %08x", pERepRec->ExceptionInfo[1]); break; case XCPT_SPACE_ACCESS: sprintf (szLineExceptionType, "Space Access at selector %08x", pERepRec->ExceptionInfo[1]); break; case XCPT_LIMIT_ACCESS: strcpy (szLineExceptionType, "Limit Access"); break; case XCPT_UNKNOWN_ACCESS: strcpy (szLineExceptionType, "Unknown Access"); break; default: strcpy (szLineExceptionType, "(Invalid Access Code)"); break; } break; case XCPT_INTEGER_DIVIDE_BY_ZERO: strcpy(szLineException, "Division By Zero (Integer)"); break; case XCPT_FLOAT_DIVIDE_BY_ZERO: strcpy(szLineException, "Division By Zero (Float)"); break; case XCPT_FLOAT_INVALID_OPERATION: strcpy(szLineException, "Invalid Floating Point Operation"); break; case XCPT_ILLEGAL_INSTRUCTION: strcpy(szLineException, "Illegal Instruction"); break; case XCPT_PRIVILEGED_INSTRUCTION: strcpy(szLineException, "Privileged Instruction"); break; case XCPT_INTEGER_OVERFLOW: strcpy(szLineException, "Integer Overflow"); break; case XCPT_FLOAT_OVERFLOW: strcpy(szLineException, "Floating Point Overflow"); break; case XCPT_FLOAT_UNDERFLOW: strcpy(szLineException, "Floating Point Underflow"); break; case XCPT_FLOAT_DENORMAL_OPERAND: strcpy(szLineException, "Floating Point Denormal Operand"); break; case XCPT_FLOAT_INEXACT_RESULT: strcpy(szLineException, "Floating Point Inexact Result"); break; case XCPT_FLOAT_STACK_CHECK: strcpy(szLineException, "Floating Point Stack Check"); break; case XCPT_DATATYPE_MISALIGNMENT: strcpy(szLineException, "Datatype Misalignment"); sprintf(szLineExceptionType, "R/W %08x alignment %08x at %08x.", pERepRec->ExceptionInfo[0], pERepRec->ExceptionInfo[1], pERepRec->ExceptionInfo[2]); break; case XCPT_BREAKPOINT: strcpy(szLineException, "Breakpoint (DEBUG)"); break; case XCPT_SINGLE_STEP: strcpy(szLineException, "Single Step (DEBUG)"); break; /* portable, fatal, software-generated exceptions */ case XCPT_IN_PAGE_ERROR: strcpy(szLineException, "In Page Error"); sprintf(szLineExceptionType, "at %08x.", pERepRec->ExceptionInfo[0]); fExcptSoftware = TRUE; break; case XCPT_PROCESS_TERMINATE: strcpy(szLineException, "Process Termination"); fExcptSoftware = TRUE; break; case XCPT_ASYNC_PROCESS_TERMINATE: strcpy(szLineException, "Process Termination (async)"); sprintf(szLineExceptionType, "terminating thread TID=%u", pERepRec->ExceptionInfo[0]); fExcptSoftware = TRUE; break; case XCPT_NONCONTINUABLE_EXCEPTION: strcpy(szLineException, "Noncontinuable Exception"); fExcptSoftware = TRUE; break; case XCPT_INVALID_DISPOSITION: strcpy(szLineException, "Invalid Disposition"); fExcptSoftware = TRUE; break; /* non-portable, fatal exceptions */ case XCPT_INVALID_LOCK_SEQUENCE: strcpy(szLineException, "Invalid Lock Sequence"); fExcptSoftware = TRUE; fExcptPortable = FALSE; break; case XCPT_ARRAY_BOUNDS_EXCEEDED: strcpy(szLineException, "Array Bounds Exceeded"); fExcptSoftware = TRUE; fExcptPortable = FALSE; break; /* unwind operation exceptions */ case XCPT_UNWIND: strcpy(szLineException, "Unwind Exception"); fExcptSoftware = TRUE; fExcptPortable = FALSE; break; case XCPT_BAD_STACK: strcpy(szLineException, "Unwind Exception, Bad Stack"); fExcptSoftware = TRUE; fExcptPortable = FALSE; break; case XCPT_INVALID_UNWIND_TARGET: strcpy(szLineException, "Unwind Exception, Invalid Target"); fExcptSoftware = TRUE; fExcptPortable = FALSE; break; /* fatal signal exceptions */ case XCPT_SIGNAL: strcpy(szLineException, "Signal"); sprintf(szLineExceptionType, "Signal Number = %08x", pERepRec->ExceptionInfo[0]); fExcptSoftware = TRUE; fExcptPortable = FALSE; switch (pERepRec->ExceptionInfo[0]) /* resolve signal information */ { case XCPT_SIGNAL_INTR: strcpy(szLineException, "Signal (Interrupt)"); break; case XCPT_SIGNAL_KILLPROC: strcpy(szLineException, "Signal (Kill Process)"); break; case XCPT_SIGNAL_BREAK: strcpy(szLineException, "Signal (Break)"); break; } break; default: strcpy(szLineException, "(unknown exception code)"); sprintf(szLineExceptionType, "Exception Code = %08x", pERepRec->ExceptionNum); break; } sprintf(szTrapDump, "---[Exception Information]------------\n %s (", szLineException); if (fExcptSoftware == TRUE) /* software or hardware generated ? */ strcat (szTrapDump, "software generated,"); else strcat (szTrapDump, "hardware generated,"); if (fExcptPortable == TRUE) /* portable exception ? */ strcat (szTrapDump, "portable,"); else strcat (szTrapDump, "non-portable,"); if (fExcptFatal == TRUE) /* fatal exception ? */ strcat (szTrapDump, "fatal"); else strcat (szTrapDump, "non-fatal"); strcat(szTrapDump, ")\n"); /* add trailing brace */ if (szLineExceptionType[0]) sprintf(szTrapDump + strlen(szTrapDump), " %s\n", szLineExceptionType); sprintf(szLineException, " Exception Address = %08x ", pERepRec->ExceptionAddress); strcat(szTrapDump, szLineException); rc = DosQueryModFromEIP(&ulModule, &ulObject, sizeof(szModule), szModule, &ulOffset, (ULONG)pERepRec->ExceptionAddress); if(rc == NO_ERROR && ulObject != -1) { sprintf(szLineException, "<%8.8s> (%04X) obj %04X:%08X", szModule, ulModule, ulObject + 1, ulOffset); #ifdef RAS char szSYMInfo[260]; DosQueryModuleName(ulModule, sizeof(szModule), szModule); int namelen = strlen(szModule); if(namelen > 3) { strcpy(szModule + namelen - 3, "SYM"); dbgGetSYMInfo(szModule, ulObject, ulOffset, szSYMInfo, sizeof (szSYMInfo), FALSE); strcat(szLineException, " "); strcat(szLineException, szSYMInfo); } #else strcat(szLineException, "\n"); #endif strcat(szTrapDump, szLineException); } else { /* fault in DosAllocMem allocated memory, hence PE loader.. */ Win32ImageBase * pMod; if (WinExe && WinExe->insideModule((ULONG)pERepRec->ExceptionAddress)) pMod = WinExe; else pMod = Win32DllBase::findModuleByAddr((ULONG)pERepRec->ExceptionAddress); if (pMod != NULL) { szModule[0] = '\0'; strncat(szModule, pMod->getModuleName(), sizeof(szModule) - 1); ulObject = 0xFF; ulOffset = (ULONG)pERepRec->ExceptionAddress - (ULONG)pMod->getInstanceHandle(); sprintf(szLineException, "<%8.8s> (%04X) obj %04X:%08X", szModule, ulModule, ulObject, ulOffset); } else sprintf(szLineException, "\n"); strcat(szTrapDump, szLineException); } rc = DosGetInfoBlocks (&pTIB, &pPIB); if (rc == NO_ERROR) { sprintf(szLineException, " Thread: Ordinal TID: %u, TID: %u, Priority: %04xh\n", pTIB->tib_ordinal, pTIB->tib_ptib2->tib2_ultid, pTIB->tib_ptib2->tib2_ulpri); strcat(szTrapDump, szLineException); sprintf(szLineException, " Process: PID: %u, Parent: %u, Status: %u\n", pPIB->pib_ulpid, pPIB->pib_ulppid, pPIB->pib_flstatus); strcat(szTrapDump, szLineException); } if (pCtxRec->ContextFlags & CONTEXT_CONTROL) { /* check flags */ sprintf(szLineException, " SS:ESP=%04x:%08x EFLAGS=%08x\n", pCtxRec->ctx_SegSs, pCtxRec->ctx_RegEsp, pCtxRec->ctx_EFlags); strcat(szTrapDump, szLineException); sprintf(szLineException, " CS:EIP=%04x:%08x EBP =%08x\n", pCtxRec->ctx_SegCs, pCtxRec->ctx_RegEip, pCtxRec->ctx_RegEbp); strcat(szTrapDump, szLineException); } if (pCtxRec->ContextFlags & CONTEXT_INTEGER) { /* check flags */ sprintf(szLineException, " EAX=%08x EBX=%08x ESI=%08x\n", pCtxRec->ctx_RegEax, pCtxRec->ctx_RegEbx, pCtxRec->ctx_RegEsi); strcat(szTrapDump, szLineException); sprintf(szLineException, " ECX=%08x EDX=%08x EDI=%08x\n", pCtxRec->ctx_RegEcx, pCtxRec->ctx_RegEdx, pCtxRec->ctx_RegEdi); strcat(szTrapDump, szLineException); } if (pCtxRec->ContextFlags & CONTEXT_SEGMENTS) { /* check flags */ sprintf(szLineException, " DS=%04x ES=%08x FS=%04x GS=%04x\n", pCtxRec->ctx_SegDs, pCtxRec->ctx_SegEs, pCtxRec->ctx_SegFs, pCtxRec->ctx_SegGs); strcat(szTrapDump, szLineException); } if (pCtxRec->ContextFlags & CONTEXT_FLOATING_POINT) /* check flags */ { ULONG ulCounter; /* temporary local counter for fp stack */ sprintf(szLineException, " Env[0]=%08x Env[1]=%08x Env[2]=%08x Env[3]=%08x\n", pCtxRec->ctx_env[0], pCtxRec->ctx_env[1], pCtxRec->ctx_env[2], pCtxRec->ctx_env[3]); strcat(szTrapDump, szLineException); sprintf(szLineException, " Env[4]=%08x Env[5]=%08x Env[6]=%08x\n", pCtxRec->ctx_env[4], pCtxRec->ctx_env[5], pCtxRec->ctx_env[6]); strcat(szTrapDump, szLineException); for (ulCounter = 0; ulCounter < 8; /* see TOOLKIT\INCLUDE\BSEEXPT.H, _CONTEXT structure */ ulCounter ++) { sprintf(szLineException, " FP-Stack[%u] losig=%08x hisig=%08x signexp=%04x\n", ulCounter, pCtxRec->ctx_stack[0].losig, pCtxRec->ctx_stack[0].hisig, pCtxRec->ctx_stack[0].signexp); strcat(szTrapDump, szLineException); } } sprintf(szLineException, "---[End Of Exception Information]-----\n"); strcat(szTrapDump, szLineException); } /***************************************************************************** * Name : void static dprintfException * Purpose : log the exception to win32os2.log * Parameters: ... * Variables : * Result : * Remark : * Status : * * Author : Patrick Haller [Tue, 1999/07/01 09:00] *****************************************************************************/ #ifdef DEBUG static void dprintfException(PEXCEPTIONREPORTRECORD pERepRec, PEXCEPTIONREGISTRATIONRECORD pERegRec, PCONTEXTRECORD pCtxRec, PVOID p, BOOL fSEH) { char szTrapDump[2048]; szTrapDump[0] = '\0'; sprintfException(pERepRec, pERegRec, pCtxRec, p, szTrapDump); #ifdef RAS RasLog (szTrapDump); #endif /* now dump the information to the logfile */ dprintf(("OS2ExceptionHandler: fSEH=%d\n%s", fSEH, szTrapDump)); } #else // DEBUG #define dprintfException(a,b,c,d,e) do {} while (0) #endif // DEBUG //***************************************************************************** static char szExceptionLogFileName[CCHMAXPATH] = ""; static BOOL fExceptionLoggging = TRUE; //***************************************************************************** //Override filename of exception log (expects full path) //***************************************************************************** void WIN32API SetCustomExceptionLog(LPSTR lpszLogName) { strcpy(szExceptionLogFileName, lpszLogName); } //***************************************************************************** //***************************************************************************** void WIN32API SetExceptionLogging(BOOL fEnable) { fExceptionLoggging = fEnable; } //***************************************************************************** //***************************************************************************** static void logException(PEXCEPTIONREPORTRECORD pERepRec, PEXCEPTIONREGISTRATIONRECORD pERegRec, PCONTEXTRECORD pCtxRec, PVOID p) { APIRET rc; HFILE hFile; ULONG ulAction, ulBytesWritten; if(fExceptionLoggging == FALSE) { return; } if(szExceptionLogFileName[0] == 0) { strcpy(szExceptionLogFileName, kernel32Path); strcat(szExceptionLogFileName, "\\except.log"); } rc = DosOpen(szExceptionLogFileName, /* File path name */ &hFile, /* File handle */ &ulAction, /* Action taken */ 0L, /* File primary allocation */ 0L, /* File attribute */ OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS, /* Open function type */ OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYNONE, 0L); /* No extended attribute */ if(rc == NO_ERROR) { DosSetFilePtr(hFile, 0, FILE_END, &ulBytesWritten); if(WinExe) { LPSTR lpszExeName; lpszExeName = WinExe->getModuleName(); if(lpszExeName) { DosWrite(hFile, (PVOID)"\n", 2, &ulBytesWritten); DosWrite(hFile, lpszExeName, strlen(lpszExeName), &ulBytesWritten); DosWrite(hFile, (PVOID)"\n", 2, &ulBytesWritten); } } LPSTR lpszTime; time_t curtime; curtime = time(NULL); lpszTime = asctime(localtime(&curtime)); if(lpszTime) { DosWrite(hFile, lpszTime, strlen(lpszTime), &ulBytesWritten); } char szTrapDump[2048]; szTrapDump[0] = '\0'; sprintfException(pERepRec, pERegRec, pCtxRec, p, szTrapDump); #ifdef RAS RasLog (szTrapDump); #endif DosWrite(hFile, szTrapDump, strlen(szTrapDump), &ulBytesWritten); DosClose(hFile); } } /***************************************************************************** * Name : ERR _System OS2ExceptionHandler * Purpose : * Parameters: ... * Variables : * Result : * Remark : * Status : * * Author : Patrick Haller [Tue, 1999/07/01 09:00] *****************************************************************************/ /* from PPC DDK */ #ifndef XCPT_CONTINUE_STOP #define XCPT_CONTINUE_STOP 0x00716668 #endif // borrowed from ntddk.h extern "C" void WIN32API RtlUnwind( LPVOID, LPVOID, LPVOID,DWORD); // Assembly wrapper for clearing the direction flag before calling our real // exception handler ULONG APIENTRY OS2ExceptionHandler(PEXCEPTIONREPORTRECORD pERepRec, PEXCEPTIONREGISTRATIONRECORD pERegRec, PCONTEXTRECORD pCtxRec, PVOID p); ULONG APIENTRY OS2ExceptionHandler2ndLevel(PEXCEPTIONREPORTRECORD pERepRec, PEXCEPTIONREGISTRATIONRECORD pERegRec, PCONTEXTRECORD pCtxRec, PVOID p) { // we need special processing when reused from ___seh_handler BOOL fSEH = pERegRec->ExceptionHandler == (_ERR *)__seh_handler; #ifdef DEBUG //SvL: Check if exception inside debug fprintf -> if so, clear lock so // next dprintf won't wait forever int prevlock = LogException(ENTER_EXCEPTION); #endif // @@VP20040507: no need to sprintf every exception // //Print exception name & exception type // //Not for a guard page exception as sprintfException uses a lot of stack // //and can trigger nested guard page exceptions (crash) // if(pERepRec->ExceptionNum != XCPT_GUARD_PAGE_VIOLATION) { // sprintfException(pERepRec, pERegRec, pCtxRec, p, szTrapDump); // } // We have to disable unwinding of the Win32 exception handlers because // experiments have shown that when running under SMP kernel the stack // becomes corrupt at the time when unwinding takes place: attempts to // to follow the exception chain crash when accessing one of the the // previous frame. Although it may seem that performing unwinding here // (when we are definitely above any Win32 exception frames installed by // the application so that the OS could theoretically already discard lower // stack areas) is wrong, it's not the case of the crash. First, doing the // very same thing from the SEH handler (see comments in sehutil.s), i.e. // when the given Win32 stack frame (and all previous ones) is definitely // alive, crahes due to the same stack corruption too. Second, when running // under UNI kernel, BOTH approaches don't crash (i.e. the stack is fine). // This looks like the SMP kernel doesn't manage the stack right during // exception handling :( See also http://svn.netlabs.org/odin32/ticket/37. // // Note that disabling unwinding also breaks support for performing // setjmp()/lonjmp() that crosses the bounds of the __try {} block. #if 0 if (pERepRec->fHandlerFlags & EH_UNWINDING) { // unwind the appropriate portion of the Win32 exception chain if (pERepRec->fHandlerFlags & EH_EXIT_UNWIND) { dprintf(("KERNEL32: OS2ExceptionHandler (fSEH=%d): EH_EXIT_UNWIND, " "unwinding all the Win32 exception chain", fSEH)); RtlUnwind(NULL, 0, 0, 0); } else { dprintf(("KERNEL32: OS2ExceptionHandler (fSEH=%d): EH_UNWINDING, " "unwinding the Win32 exception chain up to 0x%p", fSEH, pERegRec)); // find a Win32 exception frame closest to the OS/2 one (pERegRec) // and unwind up to the previous one (to unwind the closest frame // itself too as we are definitely jumping out of it) TEB *winteb = GetThreadTEB(); PWINEXCEPTION_FRAME frame = (PWINEXCEPTION_FRAME)winteb->except; while (frame != NULL && ((ULONG)frame)!= 0xFFFFFFFF && ((ULONG)frame) <= ((ULONG)pERegRec)) frame = __seh_get_prev_frame_win32(frame); if (((ULONG)frame) == 0xFFFFFFFF) frame = NULL; RtlUnwind(frame, 0, 0, 0); } goto continuesearch; } #endif /* Access violation at a known location */ switch(pERepRec->ExceptionNum) { case XCPT_FLOAT_DENORMAL_OPERAND: case XCPT_FLOAT_DIVIDE_BY_ZERO: case XCPT_FLOAT_INEXACT_RESULT: // case XCPT_FLOAT_INVALID_OPERATION: case XCPT_FLOAT_OVERFLOW: case XCPT_FLOAT_STACK_CHECK: case XCPT_FLOAT_UNDERFLOW: dprintfException(pERepRec, pERegRec, pCtxRec, p, fSEH); dprintf(("KERNEL32: OS2ExceptionHandler (fSEH=%d): FPU exception\n", fSEH)); if((!fIsOS2Image || fForceWin32TIB || fSEH) && !fExitProcess) //Only for real win32 apps or when forced { if(OSLibDispatchException(pERepRec, pERegRec, pCtxRec, p, fSEH) == FALSE) { pCtxRec->ctx_env[0] |= 0x1F; pCtxRec->ctx_stack[0].losig = 0; pCtxRec->ctx_stack[0].hisig = 0; pCtxRec->ctx_stack[0].signexp = 0; } else { dprintf(("KERNEL32: OS2ExceptionHandler (fSEH=%d): fix and continue\n", fSEH)); goto continueexecution; } } dprintf(("KERNEL32: OS2ExceptionHandler (fSEH=%d): continue search\n", fSEH)); goto continuesearch; case XCPT_PROCESS_TERMINATE: case XCPT_ASYNC_PROCESS_TERMINATE: #if 0 dprintfException(pERepRec, pERegRec, pCtxRec, p, fSEH); PrintExceptionChain(); // fall through #endif case XCPT_UNWIND: // Note that printing through ODINCRT (VACRT) when handling XCPT_UNWIND // is known to create deadlocks goto continuesearch; case XCPT_ACCESS_VIOLATION: { Win32MemMap *map; BOOL fWriteAccess = FALSE; ULONG offset, accessflag; #ifdef WITH_KLIB if ( pERepRec->ExceptionInfo[0] == XCPT_READ_ACCESS || pERepRec->ExceptionInfo[0] == XCPT_WRITE_ACCESS || pERepRec->ExceptionInfo[0] == XCPT_EXECUTE_ACCESS || pERepRec->ExceptionInfo[0] == XCPT_UNKNOWN_ACCESS ) { ENMACCESS enmAccess = enmRead; switch (pERepRec->ExceptionInfo[0]) { case XCPT_WRITE_ACCESS: enmAccess = enmWrite; break; case XCPT_UNKNOWN_ACCESS: enmAccess = enmUnknown; break; } if (kHeapDbgException((void*)pERepRec->ExceptionInfo[1], enmAccess, pERepRec->ExceptionAddress, pERepRec)) goto continueexecution; } #endif if(pERepRec->ExceptionInfo[1] == 0 && pERepRec->ExceptionInfo[1] == XCPT_DATA_UNKNOWN) { goto continueFail; } //------------->>> WARNING: potentially dangerous workaround!! /* Some apps set ES = FS and Odin doesn't like that! */ /* Note: maybe we could even check for ES != DS? But maybe */ /* that might cause more harm than good... */ if (pCtxRec->ContextFlags & CONTEXT_SEGMENTS) if (pCtxRec->ctx_SegEs == pCtxRec->ctx_SegFs) { /* Let's just reset ES to the DS value and hope it's okay */ pCtxRec->ctx_SegEs = pCtxRec->ctx_SegDs; goto continueexecution; } switch(pERepRec->ExceptionInfo[0]) { case XCPT_READ_ACCESS: accessflag = MEMMAP_ACCESS_READ; break; case XCPT_WRITE_ACCESS: accessflag = MEMMAP_ACCESS_WRITE; fWriteAccess = TRUE; break; case XCPT_EXECUTE_ACCESS: accessflag = MEMMAP_ACCESS_EXECUTE; break; default: goto continueFail; } map = Win32MemMapView::findMapByView(pERepRec->ExceptionInfo[1], &offset, accessflag); if(map == NULL) { Win32MemMapNotify *map; map = Win32MemMapNotify::findMapByView(pERepRec->ExceptionInfo[1], &offset, accessflag); if(!map) goto continueFail; BOOL ret = map->notify(pERepRec->ExceptionInfo[1], offset, fWriteAccess); if(ret == TRUE) goto continueexecution; goto continueFail; } BOOL ret = map->commitPage(pERepRec->ExceptionInfo[1], offset, fWriteAccess); map->Release(); if(ret == TRUE) goto continueexecution; //no break; } continueFail: /* * vladest: OK, try to implement write AUTOCOMMIT * last chance after MMAP commit is failed */ if (XCPT_ACCESS_VIOLATION == pERepRec->ExceptionNum && (/*pERepRec->ExceptionInfo[0] == XCPT_READ_ACCESS ||*/ pERepRec->ExceptionInfo[0] == XCPT_WRITE_ACCESS) && pERepRec->ExceptionInfo[1] != XCPT_DATA_UNKNOWN) { ULONG offset, accessflag; DosQueryMem((PVOID) pERepRec->ExceptionInfo[1], &offset, &accessflag); dprintf(("KERNEL32: OS2ExceptionHandler (fSEH=%d): failed address info 0x%X size 0x%X. flag %X\n", fSEH, pERepRec->ExceptionInfo[1], offset, accessflag)); /* check for valid address */ if (!pERepRec->ExceptionInfo[1] || pERepRec->ExceptionInfo[1] == 0xAAAAAAAA || !offset || offset == 0xAAAAAAAA) goto CrashAndBurn; /* memory committed, but no write access */ if (accessflag & PAG_GUARD) accessflag &=~PAG_GUARD; /* set memory aligned on page size and with size counting alignment */ ULONG rc = DosSetMem((PVOID) (pERepRec->ExceptionInfo[1] & 0xFFFFF000), offset + (pERepRec->ExceptionInfo[1] - (pERepRec->ExceptionInfo[1] & 0xFFFFF000)), accessflag | PAG_WRITE | PAG_COMMIT); dprintf(("KERNEL32: OS2ExceptionHandler (fSEH=%d): commiting 0x%X size 0x%X. RC: %i\n", fSEH, pERepRec->ExceptionInfo[1] & 0xFFFFF000, offset + (pERepRec->ExceptionInfo[1] - (pERepRec->ExceptionInfo[1] & 0xFFFFF000)), rc)); if (NO_ERROR == rc) goto continueexecution; else goto CrashAndBurn; } ////#define DEBUGSTACK #ifdef DEBUGSTACK if(pCtxRec->ContextFlags & CONTEXT_CONTROL) { ULONG *stackptr; APIRET rc; int i; ULONG ulOffset, ulModule, ulObject; CHAR szModule[CCHMAXPATH]; stackptr = (ULONG *)pCtxRec->ctx_RegEsp; dprintf(("Stack DUMP:")); for(i=0;i<16;i++) { rc = DosQueryModFromEIP(&ulModule, &ulObject, sizeof(szModule), szModule, &ulOffset, (ULONG)*stackptr); if (rc == NO_ERROR) dprintf(("0x%8x: 0x%8x %s (#%u), obj #%u:%08x", stackptr, *stackptr, szModule, ulModule, ulObject, ulOffset)); else dprintf(("0x%8x: 0x%8x", stackptr, *stackptr)); stackptr++; } dprintf(("Stack DUMP END")); } #endif goto CrashAndBurn; case XCPT_INVALID_LOCK_SEQUENCE: { TEB *teb = GetThreadTEB(); USHORT *eip = (USHORT *)pCtxRec->ctx_RegEip; if(teb && eip && *eip == SETTHREADCONTEXT_INVALID_LOCKOPCODE) { //Is this a pending SetThreadContext exception? //(see detailed description in the HMDeviceThreadClass::SetThreadContext method) if(teb->o.odin.context.ContextFlags) { dprintfException(pERepRec, pERegRec, pCtxRec, p, fSEH); //NOTE: This will not work properly in case multiple threads execute this code dprintf(("KERNEL32: OS2ExceptionHandler (fSEH=%d): Changing thread registers (SetThreadContext)!!", fSEH)); if(teb->o.odin.context.ContextFlags & WINCONTEXT_CONTROL) { pCtxRec->ctx_RegEbp = teb->o.odin.context.Ebp; pCtxRec->ctx_RegEip = teb->o.odin.context.Eip; //// pCtxRec->ctx_SegCs = teb->o.odin.context.SegCs; pCtxRec->ctx_EFlags = teb->o.odin.context.EFlags; pCtxRec->ctx_RegEsp = teb->o.odin.context.Esp; //// pCtxRec->ctx_SegSs = teb->o.odin.context.SegSs; } if(teb->o.odin.context.ContextFlags & WINCONTEXT_INTEGER) { pCtxRec->ctx_RegEdi = teb->o.odin.context.Edi; pCtxRec->ctx_RegEsi = teb->o.odin.context.Esi; pCtxRec->ctx_RegEbx = teb->o.odin.context.Ebx; pCtxRec->ctx_RegEdx = teb->o.odin.context.Edx; pCtxRec->ctx_RegEcx = teb->o.odin.context.Ecx; pCtxRec->ctx_RegEax = teb->o.odin.context.Eax; } if(teb->o.odin.context.ContextFlags & WINCONTEXT_SEGMENTS) { pCtxRec->ctx_SegGs = teb->o.odin.context.SegGs; //// pCtxRec->ctx_SegFs = teb->o.odin.context.SegFs; pCtxRec->ctx_SegEs = teb->o.odin.context.SegEs; pCtxRec->ctx_SegDs = teb->o.odin.context.SegDs; } if(teb->o.odin.context.ContextFlags & WINCONTEXT_FLOATING_POINT) { //TODO: First 7 dwords the same? memcpy(pCtxRec->ctx_env, &teb->o.odin.context.FloatSave, sizeof(pCtxRec->ctx_env)); memcpy(pCtxRec->ctx_stack, &teb->o.odin.context.FloatSave.RegisterArea, sizeof(pCtxRec->ctx_stack)); } USHORT *lpAlias = (USHORT *)((char *)teb->o.odin.lpAlias + teb->o.odin.dwAliasOffset); *lpAlias = teb->o.odin.savedopcode; //Clear SetThreadContext markers teb->o.odin.context.ContextFlags = 0; OSLibDosFreeMem(teb->o.odin.lpAlias); teb->o.odin.lpAlias = NULL; teb->o.odin.dwAliasOffset = 0; //restore the original priority (we boosted it to ensure this thread was scheduled first) SetThreadPriority(teb->o.odin.hThread, GetThreadPriority(teb->o.odin.hThread)); goto continueexecution; } else DebugInt3(); //oh, oh!!!!! } //no break; } case XCPT_PRIVILEGED_INSTRUCTION: case XCPT_ILLEGAL_INSTRUCTION: case XCPT_BREAKPOINT: case XCPT_ARRAY_BOUNDS_EXCEEDED: case XCPT_DATATYPE_MISALIGNMENT: case XCPT_INTEGER_DIVIDE_BY_ZERO: case XCPT_INTEGER_OVERFLOW: case XCPT_SINGLE_STEP: case XCPT_IN_PAGE_ERROR: CrashAndBurn: //SvL: TODO: this may not always be the right thing to do //MN: If EH_NESTED_CALL is set, an exception occurred during the execution // of this exception handler. We better bail out ASAP or we'll likely // recurse infinitely until we run out of stack space!! if (pERepRec->fHandlerFlags & EH_NESTED_CALL) goto continuesearch; #if defined(DEBUG) dprintfException(pERepRec, pERegRec, pCtxRec, p, fSEH); if(!fExitProcess && (pCtxRec->ContextFlags & CONTEXT_CONTROL)) { dbgPrintStack(pERepRec, pERegRec, pCtxRec, p); } #endif if((!fIsOS2Image || fForceWin32TIB || fSEH) && !fExitProcess) //Only for real win32 apps or when forced { if(OSLibDispatchException(pERepRec, pERegRec, pCtxRec, p, fSEH) == TRUE) { dprintf(("KERNEL32: OS2ExceptionHandler (fSEH=%d): fix and continue\n", fSEH)); goto continueexecution; } else { dprintf(("KERNEL32: OS2ExceptionHandler (fSEH=%d): continue search\n", fSEH)); goto continuesearch; } } else { if(fExitProcess) { PPIB pPIB; PTIB pTIB; APIRET rc; rc = DosGetInfoBlocks (&pTIB, &pPIB); if(rc == NO_ERROR) { dprintf(("KERNEL32: OS2ExceptionHandler (fSEH=%d): Continue and kill thread", fSEH)); pCtxRec->ctx_RegEip = (pTIB->tib_ptib2->tib2_ultid != 1) ? (ULONG)KillWin32Thread : (ULONG)KillWin32Process; pCtxRec->ctx_RegEsp = pCtxRec->ctx_RegEsp + 0x10; pCtxRec->ctx_RegEax = pERepRec->ExceptionNum; pCtxRec->ctx_RegEbx = pCtxRec->ctx_RegEip; goto continueexecution; } } goto continuesearch; //pass on to OS/2 RTL or app exception handler } //Log fatal exception here logException(pERepRec, pERegRec, pCtxRec, p); dprintf(("KERNEL32: OS2ExceptionHandler (fSEH=%d): Continue and kill\n", fSEH)); pCtxRec->ctx_RegEip = (ULONG)KillWin32Process; pCtxRec->ctx_RegEsp = pCtxRec->ctx_RegEsp + 0x10; pCtxRec->ctx_RegEax = pERepRec->ExceptionNum; pCtxRec->ctx_RegEbx = pCtxRec->ctx_RegEip; goto continueexecution; case XCPT_GUARD_PAGE_VIOLATION: { //NOTE:!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //Don't print anything here -> fatal hang if exception occurred //inside fprintf //NOTE:!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TEB *teb = GetThreadTEB(); DWORD stacktop, stackbottom; if(teb == NULL) { goto continuesearch; } stacktop = (DWORD)teb->stack_top; stackbottom = (DWORD)teb->stack_low; stackbottom = stackbottom & ~0xFFF; //round down to page boundary stacktop = stacktop & ~0xFFF; // Make sure we detect a stack overflow condition before the system does if ((!fIsOS2Image || fForceWin32TIB || fSEH) && //Only for real win32 apps or when forced pERepRec->ExceptionInfo[1] >= stackbottom && pERepRec->ExceptionInfo[1] < stacktop ) { // this is a guard page exception for the thread stack // In order to imitate Windows behavior, we must raise // EXCEPTION_STACK_OVERFLOW in the Win32 context in two cases: // 1. When we run out of stack. // 2. When a guard page not immediately following the committed // stack area is touched. APIRET rc; BOOL bRaise = FALSE; // round down to page boundary ULONG ulAddress = pERepRec->ExceptionInfo[1] & ~0xFFF; if (ulAddress == stackbottom + PAGE_SIZE) { // we are about to run out of stack bRaise = TRUE; } else { // check if it's an adjacent guard page used to grow stack ULONG cbSize = ~0, ulMemFlags; rc = DosQueryMem((PVOID)ulAddress, &cbSize, &ulMemFlags); if (rc) { dprintf(("ERROR: DosQueryMem old guard page failed with rc %d", rc)); goto continueGuardException; } if (ulAddress + cbSize == stacktop) { // yes, we must pass it on to the system to do the grow magic goto continuesearch; } bRaise = TRUE; } if (bRaise) { if (!fExitProcess) { EXCEPTIONREPORTRECORD recoutofstack; recoutofstack = *pERepRec; recoutofstack.ExceptionNum = XCPT_UNABLE_TO_GROW_STACK; recoutofstack.fHandlerFlags = 0; recoutofstack.NestedExceptionReportRecord = NULL; recoutofstack.cParameters = 0; if(OSLibDispatchException(&recoutofstack, pERegRec, pCtxRec, p, fSEH) == TRUE) { goto continueexecution; } } } } else { // Throw EXCEPTION_GUARD_PAGE_VIOLATION in the Win32 context if((!fIsOS2Image || fForceWin32TIB || fSEH) && !fExitProcess) //Only for real win32 apps or when forced { if(OSLibDispatchException(pERepRec, pERegRec, pCtxRec, p, fSEH) == TRUE) { goto continueexecution; } } //check for memory map guard page exception Win32MemMap *map; BOOL fWriteAccess = FALSE, ret; ULONG offset, accessflag; switch(pERepRec->ExceptionInfo[0]) { case XCPT_READ_ACCESS: accessflag = MEMMAP_ACCESS_READ; break; case XCPT_WRITE_ACCESS: accessflag = MEMMAP_ACCESS_WRITE; fWriteAccess = TRUE; break; default: goto continueGuardException; } map = Win32MemMapView::findMapByView(pERepRec->ExceptionInfo[1], &offset, accessflag); if(map) { ret = map->commitGuardPage(pERepRec->ExceptionInfo[1], offset, fWriteAccess); map->Release(); if(ret == TRUE) goto continueexecution; } } continueGuardException: goto continuesearch; } case XCPT_UNABLE_TO_GROW_STACK: { //SvL: XCPT_UNABLE_TO_GROW_STACK is typically nested (failed guard page // exception), so don't ignore them // We should no longer receive those!! // @@VP20040507: Isn't this a bit dangerous to call dprintfon such exception //#ifdef DEBUG // dprintfException(pERepRec, pERegRec, pCtxRec, p, fSEH); //#endif goto continuesearch; } /* * In OS/2 VIO Ctrl-C and Ctrl-Break is special stuff which comes in * thru the exception handler. In Win32 CUI they are handled by a * ControlCtrlEvent procedure. So, if we receive one of those signals * we assume that this is a VIO program and let the handlers handle this. * (If they want to.) */ case XCPT_SIGNAL: { //This is not a reliable way to distinguish between Ctrl-C & Ctrl-Break BOOL breakPressed = WinGetKeyState(HWND_DESKTOP,VK_BREAK) & 0x8000; switch (pERepRec->ExceptionInfo[0]) { case XCPT_SIGNAL_BREAK: breakPressed = TRUE; //no break case XCPT_SIGNAL_INTR: dprintfException(pERepRec, pERegRec, pCtxRec, p, fSEH); if (InternalGenerateConsoleCtrlEvent((breakPressed) ? CTRL_BREAK_EVENT : CTRL_C_EVENT, 0)) { DosAcknowledgeSignalException(pERepRec->ExceptionInfo[0]); goto continueexecution; } goto continuesearch; case XCPT_SIGNAL_KILLPROC: /* resolve signal information */ goto continuesearch; } goto CrashAndBurn; } default: //non-continuable exceptions dprintfException(pERepRec, pERegRec, pCtxRec, p, fSEH); goto continuesearch; } continuesearch: #ifdef DEBUG LogException(LEAVE_EXCEPTION, prevlock); #endif return XCPT_CONTINUE_SEARCH; continueexecution: #ifdef DEBUG LogException(LEAVE_EXCEPTION, prevlock); #endif return XCPT_CONTINUE_EXECUTION; } /***************************************************************************** * Name : void OS2SetExceptionHandler * Purpose : Sets the main thread exception handler in FS:[0] (original OS/2 FS selector) * Parameters: exceptframe: pointer to exception handler frame on stack (2 ULONGs) * Variables : * Result : * Remark : * Status : * * Author : Sander van Leeuwen [Sun, 1999/08/21 12:16] *****************************************************************************/ void SYSTEM OS2SetExceptionHandler(void *exceptframe) { PEXCEPTIONREGISTRATIONRECORD pExceptRec = (PEXCEPTIONREGISTRATIONRECORD)exceptframe; pExceptRec->prev_structure = (PEXCEPTIONREGISTRATIONRECORD)0; pExceptRec->ExceptionHandler = OS2ExceptionHandler; /* disable trap popups */ // DosError(FERR_DISABLEEXCEPTION | FERR_DISABLEHARDERR); DosSetExceptionHandler(pExceptRec); dprintf(("OS2SetExceptionHandler: exception chain %x", pExceptRec)); #ifdef DEBUG_ENABLELOG_LEVEL2 PrintExceptionChain(); #endif } //***************************************************************************** // Set exception handler if our handler has not yet been registered //***************************************************************************** void WIN32API ODIN_SetExceptionHandler(void *pExceptionRegRec) { BOOL fFound = FALSE; USHORT sel = RestoreOS2FS(); PEXCEPTIONREGISTRATIONRECORD pExceptRec = (PEXCEPTIONREGISTRATIONRECORD)QueryExceptionChain(); while(pExceptRec != 0 && (ULONG)pExceptRec != -1) { if(pExceptRec->ExceptionHandler == OS2ExceptionHandler) { fFound = TRUE; break; } pExceptRec = pExceptRec->prev_structure; } if(!fFound) { OS2SetExceptionHandler(pExceptionRegRec); } SetFS(sel); } //***************************************************************************** // Remove exception handler if it was registered previously // //***************************************************************************** void WIN32API ODIN_UnsetExceptionHandler(void *pExceptionRegRec) { USHORT sel = RestoreOS2FS(); PEXCEPTIONREGISTRATIONRECORD pExceptRec = (PEXCEPTIONREGISTRATIONRECORD)QueryExceptionChain(); BOOL fFound = FALSE; while(pExceptRec != 0 && (ULONG)pExceptRec != -1) { if(pExceptRec == pExceptionRegRec) { fFound = TRUE; break; } pExceptRec = pExceptRec->prev_structure; } #ifdef DEBUG pExceptRec = (PEXCEPTIONREGISTRATIONRECORD)QueryExceptionChain(); if(fFound && pExceptRec != (PEXCEPTIONREGISTRATIONRECORD)pExceptionRegRec) { dprintf(("ERROR: ODIN_UnsetExceptionHandler: INSIDE!!!: exc rec %p, head %p\n", pExceptionRegRec, pExceptRec)); PrintExceptionChain (); } #endif if(fFound) { OS2UnsetExceptionHandler(pExceptionRegRec); } SetFS(sel); } //***************************************************************************** //***************************************************************************** #ifdef DEBUG void PrintExceptionChain() { USHORT sel = RestoreOS2FS(); PEXCEPTIONREGISTRATIONRECORD pExceptRec = (PEXCEPTIONREGISTRATIONRECORD)QueryExceptionChain(); dprintf(("OS/2 Exception chain:")); while(pExceptRec != 0 && (ULONG)pExceptRec != -1) { char szBuf[512]; ULONG ulModule; ULONG ulObject, ulOffset; CHAR szModule[260]; *szBuf = '\0'; APIRET rc = DosQueryModFromEIP(&ulModule, &ulObject, sizeof(szModule), szModule, &ulOffset, (ULONG)pExceptRec->ExceptionHandler); if(rc == NO_ERROR && ulObject != -1) { sprintf(szBuf, " <%8.8s> (%04X) obj %04X:%08X", szModule, ulModule, ulObject + 1, ulOffset); #ifdef RAS char szSYMInfo[260]; DosQueryModuleName(ulModule, sizeof(szModule), szModule); int namelen = strlen(szModule); if(namelen > 3) { strcpy(szModule + namelen - 3, "SYM"); dbgGetSYMInfo(szModule, ulObject, ulOffset, szSYMInfo, sizeof (szSYMInfo), TRUE); strcat(szBuf, " "); strcat(szBuf, szSYMInfo); // remove trailing \n or space szBuf[strlen(szBuf) - 1] = '\0'; } #endif } else { *szBuf = '\0'; } dprintf((" record %08X, prev %08X, handler %08X%s", pExceptRec, pExceptRec->prev_structure, pExceptRec->ExceptionHandler, szBuf)); pExceptRec = pExceptRec->prev_structure; } dprintf(("END of OS/2 Exception chain.")); SetFS(sel); } //***************************************************************************** //***************************************************************************** void PrintWin32ExceptionChain(PWINEXCEPTION_FRAME pFrame) { dprintf(("Win32 exception chain:")); while ((pFrame != NULL) && ((ULONG)pFrame != 0xFFFFFFFF)) { PWINEXCEPTION_FRAME pPrevFrame = __seh_get_prev_frame_win32(pFrame); dprintf((" Record at %08X, Prev at %08X, handler at %08X%s", pFrame, pPrevFrame, pFrame->Handler, pFrame->Handler == (PEXCEPTION_HANDLER)__seh_handler ? " (SEH)" : pFrame->Handler == (PEXCEPTION_HANDLER)__seh_handler_win32 ? " (SEH Win32)" : "")); if (pFrame == pPrevFrame) { dprintf(("Chain corrupted! Record at %08X pointing to itself!", pFrame)); break; } pFrame = pPrevFrame; } dprintf(("END of Win32 exception chain.")); } #endif /***************************************************************************** * Name : void OS2UnsetExceptionHandler * Purpose : Removes the main thread exception handler in FS:[0] (original OS/2 FS selector) * Parameters: exceptframe: pointer to exception handler frame on stack (2 ULONGs) * Variables : * Result : * Remark : * Status : * * Author : Sander van Leeuwen [Sun, 1999/08/21 12:16] *****************************************************************************/ void SYSTEM OS2UnsetExceptionHandler(void *exceptframe) { PEXCEPTIONREGISTRATIONRECORD pExceptRec = (PEXCEPTIONREGISTRATIONRECORD)exceptframe; DosUnsetExceptionHandler(pExceptRec); dprintf(("OS2UnsetExceptionHandler: exception chain %x", pExceptRec)); #ifdef DEBUG_ENABLELOG_LEVEL2 PrintExceptionChain(); #endif } //***************************************************************************** //***************************************************************************** int _System CheckCurFS() { USHORT sel = RestoreOS2FS(); PEXCEPTIONREGISTRATIONRECORD pExceptRec; if(sel == 0x150b) { SetFS(sel); return FALSE; } pExceptRec = (PEXCEPTIONREGISTRATIONRECORD)QueryExceptionChain(); if(pExceptRec->ExceptionHandler != OS2ExceptionHandler) { SetFS(sel); return FALSE; } SetFS(sel); return TRUE; } //***************************************************************************** //***************************************************************************** } // extern "C"