| /* |
| * msvcrt.dll exception handling |
| * |
| * Copyright 2000 Jon Griffiths |
| * |
| * See http://www.microsoft.com/msj/0197/exception/exception.htm, |
| * but don't believe all of it. |
| * |
| * FIXME: Incomplete support for nested exceptions/try block cleanup. |
| */ |
| #include "ntddk.h" |
| #include "wine/exception.h" |
| #include "thread.h" |
| #include "msvcrt.h" |
| |
| DEFAULT_DEBUG_CHANNEL(msvcrt); |
| |
| typedef void (*MSVCRT_sig_handler_func)(void); |
| |
| /* VC++ extensions to Win32 SEH */ |
| typedef struct _SCOPETABLE |
| { |
| DWORD previousTryLevel; |
| int (*lpfnFilter)(PEXCEPTION_POINTERS); |
| int (*lpfnHandler)(void); |
| } SCOPETABLE, *PSCOPETABLE; |
| |
| typedef struct _MSVCRT_EXCEPTION_FRAME |
| { |
| EXCEPTION_FRAME *prev; |
| void (*handler)(PEXCEPTION_RECORD, PEXCEPTION_FRAME, |
| PCONTEXT, PEXCEPTION_RECORD); |
| PSCOPETABLE scopetable; |
| DWORD trylevel; |
| int _ebp; |
| PEXCEPTION_POINTERS xpointers; |
| } MSVCRT_EXCEPTION_FRAME; |
| |
| #define TRYLEVEL_END 0xff /* End of trylevel list */ |
| |
| #if defined(__GNUC__) && defined(__i386__) |
| |
| #define CALL_FINALLY_BLOCK(code_block, base_ptr) \ |
| __asm__ __volatile__ ("movl %0,%%eax; movl %1,%%ebp; call *%%eax" \ |
| : : "g" (code_block), "g" (base_ptr)) |
| |
| static DWORD MSVCRT_nested_handler(PEXCEPTION_RECORD rec, |
| struct __EXCEPTION_FRAME* frame, |
| PCONTEXT context WINE_UNUSED, |
| struct __EXCEPTION_FRAME** dispatch) |
| { |
| if (rec->ExceptionFlags & 0x6) |
| return ExceptionContinueSearch; |
| *dispatch = frame; |
| return ExceptionCollidedUnwind; |
| } |
| #endif |
| |
| |
| /********************************************************************* |
| * _XcptFilter (MSVCRT.@) |
| */ |
| int _XcptFilter(int ex, PEXCEPTION_POINTERS ptr) |
| { |
| FIXME("(%d,%p)semi-stub\n", ex, ptr); |
| return UnhandledExceptionFilter(ptr); |
| } |
| |
| /********************************************************************* |
| * _EH_prolog (MSVCRT.@) |
| */ |
| #ifdef __i386__ |
| /* Provided for VC++ binary compatability only */ |
| __ASM_GLOBAL_FUNC(_EH_prolog, |
| "pushl $0xff\n\t" |
| "pushl %eax\n\t" |
| "pushl %fs:0\n\t" |
| "movl %esp, %fs:0\n\t" |
| "movl 12(%esp), %eax\n\t" |
| "movl %ebp, 12(%esp)\n\t" |
| "leal 12(%esp), %ebp\n\t" |
| "pushl %eax\n\t" |
| "ret"); |
| #endif |
| |
| /******************************************************************* |
| * _global_unwind2 (MSVCRT.@) |
| */ |
| void _global_unwind2(PEXCEPTION_FRAME frame) |
| { |
| TRACE("(%p)\n",frame); |
| RtlUnwind( frame, 0, 0, 0 ); |
| } |
| |
| /******************************************************************* |
| * _local_unwind2 (MSVCRT.@) |
| */ |
| void _local_unwind2(MSVCRT_EXCEPTION_FRAME* frame, |
| DWORD trylevel) |
| { |
| MSVCRT_EXCEPTION_FRAME *curframe = frame; |
| DWORD curtrylevel = 0xfe; |
| EXCEPTION_FRAME reg; |
| |
| TRACE("(%p,%ld,%ld)\n",frame, frame->trylevel, trylevel); |
| |
| /* Register a handler in case of a nested exception */ |
| reg.Handler = (PEXCEPTION_HANDLER)MSVCRT_nested_handler; |
| reg.Prev = NtCurrentTeb()->except; |
| __wine_push_frame(®); |
| |
| while (frame->trylevel != TRYLEVEL_END && frame->trylevel != trylevel) |
| { |
| curtrylevel = frame->scopetable[frame->trylevel].previousTryLevel; |
| curframe = frame; |
| curframe->trylevel = curtrylevel; |
| if (!frame->scopetable[curtrylevel].lpfnFilter) |
| { |
| ERR("__try block cleanup not implemented - expect crash!\n"); |
| /* FIXME: Remove current frame, set ebp, call |
| * frame->scopetable[curtrylevel].lpfnHandler() |
| */ |
| } |
| } |
| __wine_pop_frame(®); |
| TRACE("unwound OK\n"); |
| } |
| |
| /********************************************************************* |
| * _except_handler2 (MSVCRT.@) |
| */ |
| int _except_handler2(PEXCEPTION_RECORD rec, |
| PEXCEPTION_FRAME frame, |
| PCONTEXT context, |
| PEXCEPTION_FRAME* dispatcher) |
| { |
| FIXME("exception %lx flags=%lx at %p handler=%p %p %p stub\n", |
| rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, |
| frame->Handler, context, dispatcher); |
| return ExceptionContinueSearch; |
| } |
| |
| /********************************************************************* |
| * _except_handler3 (MSVCRT.@) |
| */ |
| int _except_handler3(PEXCEPTION_RECORD rec, |
| MSVCRT_EXCEPTION_FRAME* frame, |
| PCONTEXT context, void* dispatcher) |
| { |
| #if defined(__GNUC__) && defined(__i386__) |
| long retval, trylevel; |
| EXCEPTION_POINTERS exceptPtrs; |
| PSCOPETABLE pScopeTable; |
| |
| TRACE("exception %lx flags=%lx at %p handler=%p %p %p semi-stub\n", |
| rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, |
| frame->handler, context, dispatcher); |
| |
| __asm__ __volatile__ ("cld"); |
| |
| if (rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND)) |
| { |
| /* Unwinding the current frame */ |
| _local_unwind2(frame, TRYLEVEL_END); |
| return ExceptionContinueSearch; |
| } |
| else |
| { |
| /* Hunting for handler */ |
| exceptPtrs.ExceptionRecord = rec; |
| exceptPtrs.ContextRecord = context; |
| *((DWORD *)frame-1) = (DWORD)&exceptPtrs; |
| trylevel = frame->trylevel; |
| pScopeTable = frame->scopetable; |
| |
| while (trylevel != TRYLEVEL_END) |
| { |
| if (pScopeTable[trylevel].lpfnFilter) |
| { |
| TRACE("filter = %p\n", pScopeTable[trylevel].lpfnFilter); |
| |
| retval = pScopeTable[trylevel].lpfnFilter(&exceptPtrs); |
| |
| TRACE("filter returned %s\n", retval == EXCEPTION_CONTINUE_EXECUTION ? |
| "CONTINUE_EXECUTION" : retval == EXCEPTION_EXECUTE_HANDLER ? |
| "EXECUTE_HANDLER" : "CONTINUE_SEARCH"); |
| |
| if (retval == EXCEPTION_CONTINUE_EXECUTION) |
| return ExceptionContinueExecution; |
| |
| if (retval == EXCEPTION_EXECUTE_HANDLER) |
| { |
| /* Unwind all higher frames, this one will handle the exception */ |
| _global_unwind2((PEXCEPTION_FRAME)frame); |
| _local_unwind2(frame, trylevel); |
| |
| /* Set our trylevel to the enclosing block, and call the __finally |
| * code, which won't return |
| */ |
| frame->trylevel = pScopeTable->previousTryLevel; |
| TRACE("__finally block %p\n",pScopeTable[trylevel].lpfnHandler); |
| CALL_FINALLY_BLOCK(pScopeTable[trylevel].lpfnHandler, frame->_ebp); |
| ERR("Returned from __finally block - expect crash!\n"); |
| } |
| } |
| trylevel = pScopeTable->previousTryLevel; |
| } |
| } |
| #else |
| TRACE("exception %lx flags=%lx at %p handler=%p %p %p stub\n", |
| rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, |
| frame->handler, context, dispatcher); |
| #endif |
| return ExceptionContinueSearch; |
| } |
| |
| /********************************************************************* |
| * _abnormal_termination (MSVCRT.@) |
| */ |
| int _abnormal_termination(void) |
| { |
| FIXME("(void)stub\n"); |
| return 0; |
| } |
| |
| /******************************************************************* |
| * _setjmp (MSVCRT.@) |
| */ |
| int MSVCRT__setjmp(LPDWORD* jmpbuf) |
| { |
| FIXME(":(%p): stub\n",jmpbuf); |
| return 0; |
| } |
| |
| /******************************************************************* |
| * _setjmp3 (MSVCRT.@) |
| */ |
| int __cdecl MSVCRT__setjmp3(LPDWORD *jmpbuf, int x) |
| { |
| FIXME(":(%p %x): stub\n",jmpbuf,x); |
| return 0; |
| } |
| |
| /********************************************************************* |
| * longjmp (MSVCRT.@) |
| */ |
| void MSVCRT_longjmp(jmp_buf env, int val) |
| { |
| FIXME("MSVCRT_longjmp semistub, expect crash\n"); |
| longjmp(env, val); |
| } |
| |
| /********************************************************************* |
| * signal (MSVCRT.@) |
| */ |
| void* MSVCRT_signal(int sig, MSVCRT_sig_handler_func func) |
| { |
| FIXME("(%d %p):stub\n", sig, func); |
| return (void*)-1; |
| } |