/* * msvcrt.dll exception handling * * Copyright 2000 Jon Griffiths * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * NOTES: * * 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 "config.h" #include "wine/port.h" #include #include "windef.h" #include "winbase.h" #include "winreg.h" #include "winternl.h" #include "wine/exception.h" #include "msvcrt.h" #include "msvcrt/setjmp.h" #include "excpt.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); typedef void (*MSVCRT_sig_handler_func)(void); /* VC++ extensions to Win32 SEH */ typedef struct _SCOPETABLE { int previousTryLevel; int (*lpfnFilter)(PEXCEPTION_POINTERS); int (*lpfnHandler)(void); } SCOPETABLE, *PSCOPETABLE; typedef struct _MSVCRT_EXCEPTION_FRAME { EXCEPTION_REGISTRATION_RECORD *prev; void (*handler)(PEXCEPTION_RECORD, PEXCEPTION_REGISTRATION_RECORD, PCONTEXT, PEXCEPTION_RECORD); PSCOPETABLE scopetable; int trylevel; int _ebp; PEXCEPTION_POINTERS xpointers; } MSVCRT_EXCEPTION_FRAME; #define TRYLEVEL_END (-1) /* End of trylevel list */ #if defined(__GNUC__) && defined(__i386__) inline static void call_finally_block( void *code_block, void *base_ptr ) { __asm__ __volatile__ ("movl %1,%%ebp; call *%%eax" \ : : "a" (code_block), "g" (base_ptr)); } inline static DWORD call_filter( void *func, void *arg, void *ebp ) { DWORD ret; __asm__ __volatile__ ("pushl %%ebp; pushl %3; movl %2,%%ebp; call *%%eax; popl %%ebp; popl %%ebp" : "=a" (ret) : "0" (func), "g" (ebp), "g" (arg) : "ecx", "edx", "memory" ); return ret; } #endif static DWORD MSVCRT_nested_handler(PEXCEPTION_RECORD rec, EXCEPTION_REGISTRATION_RECORD* frame, PCONTEXT context WINE_UNUSED, EXCEPTION_REGISTRATION_RECORD** dispatch) { if (rec->ExceptionFlags & 0x6) return ExceptionContinueSearch; *dispatch = frame; return ExceptionCollidedUnwind; } /********************************************************************* * _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 compatibility only */ __ASM_GLOBAL_FUNC(_EH_prolog, "pushl $-1\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_REGISTRATION_RECORD frame) { TRACE("(%p)\n",frame); RtlUnwind( frame, 0, 0, 0 ); } /******************************************************************* * _local_unwind2 (MSVCRT.@) */ void _local_unwind2(MSVCRT_EXCEPTION_FRAME* frame, int trylevel) { MSVCRT_EXCEPTION_FRAME *curframe = frame; EXCEPTION_REGISTRATION_RECORD reg; TRACE("(%p,%d,%d)\n",frame, frame->trylevel, trylevel); /* Register a handler in case of a nested exception */ reg.Handler = (PEXCEPTION_HANDLER)MSVCRT_nested_handler; reg.Prev = NtCurrentTeb()->Tib.ExceptionList; __wine_push_frame(®); while (frame->trylevel != TRYLEVEL_END && frame->trylevel != trylevel) { int 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_REGISTRATION_RECORD frame, PCONTEXT context, PEXCEPTION_REGISTRATION_RECORD* 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; int 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 = call_filter( pScopeTable[trylevel].lpfnFilter, &exceptPtrs, &frame->_ebp ); 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_REGISTRATION_RECORD)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/longjmp implementation */ #ifdef __i386__ #define MSVCRT_JMP_MAGIC 0x56433230 /* ID value for new jump structure */ typedef void (*MSVCRT_unwind_function)(const void*); /* * The signatures of the setjmp/longjmp functions do not match that * declared in the setjmp header so they don't follow the regular naming * convention to avoid conflicts. */ /******************************************************************* * _setjmp (MSVCRT.@) */ DEFINE_REGS_ENTRYPOINT( MSVCRT__setjmp, _MSVCRT__setjmp, 4, 0 ); void _MSVCRT__setjmp(_JUMP_BUFFER *jmp, CONTEXT86* context) { TRACE("(%p)\n",jmp); jmp->Ebp = context->Ebp; jmp->Ebx = context->Ebx; jmp->Edi = context->Edi; jmp->Esi = context->Esi; jmp->Esp = context->Esp; jmp->Eip = context->Eip; jmp->Registration = (unsigned long)NtCurrentTeb()->Tib.ExceptionList; if (jmp->Registration == TRYLEVEL_END) jmp->TryLevel = TRYLEVEL_END; else jmp->TryLevel = ((MSVCRT_EXCEPTION_FRAME*)jmp->Registration)->trylevel; TRACE("returning 0\n"); context->Eax=0; } /******************************************************************* * _setjmp3 (MSVCRT.@) */ DEFINE_REGS_ENTRYPOINT( MSVCRT__setjmp3, _MSVCRT__setjmp3, 8, 0 ); void _MSVCRT__setjmp3(_JUMP_BUFFER *jmp, int nb_args, CONTEXT86* context) { TRACE("(%p,%d)\n",jmp,nb_args); jmp->Ebp = context->Ebp; jmp->Ebx = context->Ebx; jmp->Edi = context->Edi; jmp->Esi = context->Esi; jmp->Esp = context->Esp; jmp->Eip = context->Eip; jmp->Cookie = MSVCRT_JMP_MAGIC; jmp->UnwindFunc = 0; jmp->Registration = (unsigned long)NtCurrentTeb()->Tib.ExceptionList; if (jmp->Registration == TRYLEVEL_END) { jmp->TryLevel = TRYLEVEL_END; } else { void **args = ((void**)context->Esp)+2; if (nb_args > 0) jmp->UnwindFunc = (unsigned long)*args++; if (nb_args > 1) jmp->TryLevel = (unsigned long)*args++; else jmp->TryLevel = ((MSVCRT_EXCEPTION_FRAME*)jmp->Registration)->trylevel; if (nb_args > 2) { size_t size = (nb_args - 2) * sizeof(DWORD); memcpy( jmp->UnwindData, args, min( size, sizeof(jmp->UnwindData) )); } } TRACE("returning 0\n"); context->Eax = 0; } /********************************************************************* * longjmp (MSVCRT.@) */ DEFINE_REGS_ENTRYPOINT( MSVCRT_longjmp, _MSVCRT_longjmp, 8, 0 ); void _MSVCRT_longjmp(_JUMP_BUFFER *jmp, int retval, CONTEXT86* context) { unsigned long cur_frame = 0; TRACE("(%p,%d)\n", jmp, retval); cur_frame=(unsigned long)NtCurrentTeb()->Tib.ExceptionList; TRACE("cur_frame=%lx\n",cur_frame); if (cur_frame != jmp->Registration) _global_unwind2((PEXCEPTION_REGISTRATION_RECORD)jmp->Registration); if (jmp->Registration) { if (!IsBadReadPtr(&jmp->Cookie, sizeof(long)) && jmp->Cookie == MSVCRT_JMP_MAGIC && jmp->UnwindFunc) { MSVCRT_unwind_function unwind_func; unwind_func=(MSVCRT_unwind_function)jmp->UnwindFunc; unwind_func(jmp); } else _local_unwind2((MSVCRT_EXCEPTION_FRAME*)jmp->Registration, jmp->TryLevel); } if (!retval) retval = 1; TRACE("Jump to %lx returning %d\n",jmp->Eip,retval); context->Ebp = jmp->Ebp; context->Ebx = jmp->Ebx; context->Edi = jmp->Edi; context->Esi = jmp->Esi; context->Esp = jmp->Esp; context->Eip = jmp->Eip; context->Eax = retval; } /********************************************************************* * _seh_longjmp_unwind (MSVCRT.@) */ void __stdcall _seh_longjmp_unwind(_JUMP_BUFFER *jmp) { _local_unwind2( (MSVCRT_EXCEPTION_FRAME *)jmp->Registration, jmp->TryLevel ); } #endif /* i386 */ /********************************************************************* * signal (MSVCRT.@) */ void* MSVCRT_signal(int sig, MSVCRT_sig_handler_func func) { FIXME("(%d %p):stub\n", sig, func); return (void*)-1; }