| /* | |
| * 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 <stdarg.h> | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "winreg.h" | |
| #include "winternl.h" | |
| #include "wine/exception.h" | |
| #include "msvcrt.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, EXCEPTION_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), "r" (ebp), "r" (arg) | |
| : "ecx", "edx", "memory" ); | |
| return ret; | |
| } | |
| #endif | |
| static DWORD MSVCRT_nested_handler(PEXCEPTION_RECORD rec, | |
| EXCEPTION_REGISTRATION_RECORD* frame, | |
| PCONTEXT context, | |
| 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(EXCEPTION_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, | |
| EXCEPTION_REGISTRATION_RECORD* frame, | |
| PCONTEXT context, | |
| EXCEPTION_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((EXCEPTION_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(struct MSVCRT___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(struct MSVCRT___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(struct MSVCRT___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((EXCEPTION_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(struct MSVCRT___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; | |
| } |