/* * msvcrt.dll exception handling * * Copyright 2000 Jon Griffiths * Copyright 2005 Juan Lang * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * FIXME: Incomplete support for nested exceptions/try block cleanup. */ #include "config.h" #include "wine/port.h" #include #include "ntstatus.h" #define WIN32_NO_STATUS #include "windef.h" #include "winbase.h" #include "winternl.h" #include "wine/exception.h" #include "msvcrt.h" #include "excpt.h" #include "wincon.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(seh); static MSVCRT_security_error_handler security_error_handler; /* 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; typedef struct { int gs_cookie_offset; ULONG gs_cookie_xor; int eh_cookie_offset; ULONG eh_cookie_xor; SCOPETABLE entries[1]; } SCOPETABLE_V4; #define TRYLEVEL_END (-1) /* End of trylevel list */ #if defined(__GNUC__) && defined(__i386__) static inline void call_finally_block( void *code_block, void *base_ptr ) { __asm__ __volatile__ ("movl %1,%%ebp; call *%%eax" : : "a" (code_block), "g" (base_ptr)); } static inline int call_filter( int (*func)(PEXCEPTION_POINTERS), void *arg, void *ebp ) { int 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; } static inline int call_unwind_func( int (*func)(void), void *ebp ) { int ret; __asm__ __volatile__ ("pushl %%ebp\n\t" "pushl %%ebx\n\t" "pushl %%esi\n\t" "pushl %%edi\n\t" "movl %2,%%ebp\n\t" "call *%0\n\t" "popl %%edi\n\t" "popl %%esi\n\t" "popl %%ebx\n\t" "popl %%ebp" : "=a" (ret) : "0" (func), "r" (ebp) : "ecx", "edx", "memory" ); return ret; } #endif #ifdef __i386__ static const SCOPETABLE_V4 *get_scopetable_v4( MSVCRT_EXCEPTION_FRAME *frame, ULONG_PTR cookie ) { return (const SCOPETABLE_V4 *)((ULONG_PTR)frame->scopetable ^ cookie); } static DWORD MSVCRT_nested_handler(PEXCEPTION_RECORD rec, EXCEPTION_REGISTRATION_RECORD* frame, PCONTEXT context, EXCEPTION_REGISTRATION_RECORD** dispatch) { if (!(rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))) return ExceptionContinueSearch; *dispatch = frame; return ExceptionCollidedUnwind; } /********************************************************************* * _EH_prolog (MSVCRT.@) */ /* Provided for VC++ binary compatibility only */ __ASM_GLOBAL_FUNC(_EH_prolog, __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") /* skip ret addr */ "pushl $-1\n\t" __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") "pushl %eax\n\t" __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") "pushl %fs:0\n\t" __ASM_CFI(".cfi_adjust_cfa_offset 4\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" __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") "ret") static void msvcrt_local_unwind2(MSVCRT_EXCEPTION_FRAME* frame, int trylevel, void *ebp) { EXCEPTION_REGISTRATION_RECORD reg; TRACE("(%p,%d,%d)\n",frame, frame->trylevel, trylevel); /* Register a handler in case of a nested exception */ reg.Handler = MSVCRT_nested_handler; reg.Prev = NtCurrentTeb()->Tib.ExceptionList; __wine_push_frame(®); while (frame->trylevel != TRYLEVEL_END && frame->trylevel != trylevel) { int level = frame->trylevel; frame->trylevel = frame->scopetable[level].previousTryLevel; if (!frame->scopetable[level].lpfnFilter) { TRACE( "__try block cleanup level %d handler %p ebp %p\n", level, frame->scopetable[level].lpfnHandler, ebp ); call_unwind_func( frame->scopetable[level].lpfnHandler, ebp ); } } __wine_pop_frame(®); TRACE("unwound OK\n"); } static void msvcrt_local_unwind4( ULONG *cookie, MSVCRT_EXCEPTION_FRAME* frame, int trylevel, void *ebp ) { EXCEPTION_REGISTRATION_RECORD reg; const SCOPETABLE_V4 *scopetable = get_scopetable_v4( frame, *cookie ); TRACE("(%p,%d,%d)\n",frame, frame->trylevel, trylevel); /* Register a handler in case of a nested exception */ reg.Handler = MSVCRT_nested_handler; reg.Prev = NtCurrentTeb()->Tib.ExceptionList; __wine_push_frame(®); while (frame->trylevel != -2 && frame->trylevel != trylevel) { int level = frame->trylevel; frame->trylevel = scopetable->entries[level].previousTryLevel; if (!scopetable->entries[level].lpfnFilter) { TRACE( "__try block cleanup level %d handler %p ebp %p\n", level, scopetable->entries[level].lpfnHandler, ebp ); call_unwind_func( scopetable->entries[level].lpfnHandler, ebp ); } } __wine_pop_frame(®); TRACE("unwound OK\n"); } /******************************************************************* * _local_unwind2 (MSVCRT.@) */ void CDECL _local_unwind2(MSVCRT_EXCEPTION_FRAME* frame, int trylevel) { msvcrt_local_unwind2( frame, trylevel, &frame->_ebp ); } /******************************************************************* * _local_unwind4 (MSVCRT.@) */ void CDECL _local_unwind4( ULONG *cookie, MSVCRT_EXCEPTION_FRAME* frame, int trylevel ) { msvcrt_local_unwind4( cookie, frame, trylevel, &frame->_ebp ); } /******************************************************************* * _global_unwind2 (MSVCRT.@) */ void CDECL _global_unwind2(EXCEPTION_REGISTRATION_RECORD* frame) { TRACE("(%p)\n",frame); RtlUnwind( frame, 0, 0, 0 ); } /********************************************************************* * _except_handler2 (MSVCRT.@) */ int CDECL _except_handler2(PEXCEPTION_RECORD rec, EXCEPTION_REGISTRATION_RECORD* frame, PCONTEXT context, EXCEPTION_REGISTRATION_RECORD** dispatcher) { FIXME("exception %x flags=%x at %p handler=%p %p %p stub\n", rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, frame->Handler, context, dispatcher); return ExceptionContinueSearch; } /********************************************************************* * _except_handler3 (MSVCRT.@) */ int CDECL _except_handler3(PEXCEPTION_RECORD rec, MSVCRT_EXCEPTION_FRAME* frame, PCONTEXT context, void* dispatcher) { int retval, trylevel; EXCEPTION_POINTERS exceptPtrs; PSCOPETABLE pScopeTable; TRACE("exception %x flags=%x 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 */ msvcrt_local_unwind2(frame, TRYLEVEL_END, &frame->_ebp); TRACE("unwound current frame, returning ExceptionContinueSearch\n"); 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) { TRACE( "level %d prev %d filter %p\n", trylevel, pScopeTable[trylevel].previousTryLevel, pScopeTable[trylevel].lpfnFilter ); if (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); msvcrt_local_unwind2(frame, trylevel, &frame->_ebp); /* Set our trylevel to the enclosing block, and call the __finally * code, which won't return */ frame->trylevel = pScopeTable[trylevel].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[trylevel].previousTryLevel; } } TRACE("reached TRYLEVEL_END, returning ExceptionContinueSearch\n"); return ExceptionContinueSearch; } /********************************************************************* * _except_handler4_common (MSVCRT.@) */ int CDECL _except_handler4_common( ULONG *cookie, void (*check_cookie)(void), EXCEPTION_RECORD *rec, MSVCRT_EXCEPTION_FRAME *frame, CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher ) { int retval, trylevel; EXCEPTION_POINTERS exceptPtrs; const SCOPETABLE_V4 *scope_table = get_scopetable_v4( frame, *cookie ); TRACE( "exception %x flags=%x at %p handler=%p %p %p cookie=%x scope table=%p cookies=%d/%x,%d/%x\n", rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, frame->handler, context, dispatcher, *cookie, scope_table, scope_table->gs_cookie_offset, scope_table->gs_cookie_xor, scope_table->eh_cookie_offset, scope_table->eh_cookie_xor ); /* FIXME: no cookie validation yet */ if (rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND)) { /* Unwinding the current frame */ msvcrt_local_unwind4( cookie, frame, -2, &frame->_ebp ); TRACE("unwound current frame, returning ExceptionContinueSearch\n"); return ExceptionContinueSearch; } else { /* Hunting for handler */ exceptPtrs.ExceptionRecord = rec; exceptPtrs.ContextRecord = context; *((DWORD *)frame-1) = (DWORD)&exceptPtrs; trylevel = frame->trylevel; while (trylevel != -2) { TRACE( "level %d prev %d filter %p\n", trylevel, scope_table->entries[trylevel].previousTryLevel, scope_table->entries[trylevel].lpfnFilter ); if (scope_table->entries[trylevel].lpfnFilter) { retval = call_filter( scope_table->entries[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); msvcrt_local_unwind4( cookie, frame, trylevel, &frame->_ebp ); /* Set our trylevel to the enclosing block, and call the __finally * code, which won't return */ frame->trylevel = scope_table->entries[trylevel].previousTryLevel; TRACE("__finally block %p\n",scope_table->entries[trylevel].lpfnHandler); call_finally_block(scope_table->entries[trylevel].lpfnHandler, &frame->_ebp); ERR("Returned from __finally block - expect crash!\n"); } } trylevel = scope_table->entries[trylevel].previousTryLevel; } } TRACE("reached -2, returning ExceptionContinueSearch\n"); return ExceptionContinueSearch; } /* * setjmp/longjmp implementation */ #define MSVCRT_JMP_MAGIC 0x56433230 /* ID value for new jump structure */ typedef void (__stdcall *MSVCRT_unwind_function)(const struct MSVCRT___JUMP_BUFFER *); /* define an entrypoint for setjmp/setjmp3 that stores the registers in the jmp buf */ /* and then jumps to the C backend function */ #define DEFINE_SETJMP_ENTRYPOINT(name) \ __ASM_GLOBAL_FUNC( name, \ "movl 4(%esp),%ecx\n\t" /* jmp_buf */ \ "movl %ebp,0(%ecx)\n\t" /* jmp_buf.Ebp */ \ "movl %ebx,4(%ecx)\n\t" /* jmp_buf.Ebx */ \ "movl %edi,8(%ecx)\n\t" /* jmp_buf.Edi */ \ "movl %esi,12(%ecx)\n\t" /* jmp_buf.Esi */ \ "movl %esp,16(%ecx)\n\t" /* jmp_buf.Esp */ \ "movl 0(%esp),%eax\n\t" \ "movl %eax,20(%ecx)\n\t" /* jmp_buf.Eip */ \ "jmp " __ASM_NAME("__regs_") # name ) /* restore the registers from the jmp buf upon longjmp */ extern void DECLSPEC_NORETURN longjmp_set_regs( struct MSVCRT___JUMP_BUFFER *jmp, int retval ); __ASM_GLOBAL_FUNC( longjmp_set_regs, "movl 4(%esp),%ecx\n\t" /* jmp_buf */ "movl 8(%esp),%eax\n\t" /* retval */ "movl 0(%ecx),%ebp\n\t" /* jmp_buf.Ebp */ "movl 4(%ecx),%ebx\n\t" /* jmp_buf.Ebx */ "movl 8(%ecx),%edi\n\t" /* jmp_buf.Edi */ "movl 12(%ecx),%esi\n\t" /* jmp_buf.Esi */ "movl 16(%ecx),%esp\n\t" /* jmp_buf.Esp */ "addl $4,%esp\n\t" /* get rid of return address */ "jmp *20(%ecx)\n\t" /* jmp_buf.Eip */ ) /* * 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_SETJMP_ENTRYPOINT(MSVCRT__setjmp) int CDECL __regs_MSVCRT__setjmp(struct MSVCRT___JUMP_BUFFER *jmp) { jmp->Registration = (unsigned long)NtCurrentTeb()->Tib.ExceptionList; if (jmp->Registration == ~0UL) jmp->TryLevel = TRYLEVEL_END; else jmp->TryLevel = ((MSVCRT_EXCEPTION_FRAME*)jmp->Registration)->trylevel; TRACE("buf=%p ebx=%08lx esi=%08lx edi=%08lx ebp=%08lx esp=%08lx eip=%08lx frame=%08lx\n", jmp, jmp->Ebx, jmp->Esi, jmp->Edi, jmp->Ebp, jmp->Esp, jmp->Eip, jmp->Registration ); return 0; } /******************************************************************* * _setjmp3 (MSVCRT.@) */ DEFINE_SETJMP_ENTRYPOINT( MSVCRT__setjmp3 ) int CDECL __regs_MSVCRT__setjmp3(struct MSVCRT___JUMP_BUFFER *jmp, int nb_args, ...) { jmp->Cookie = MSVCRT_JMP_MAGIC; jmp->UnwindFunc = 0; jmp->Registration = (unsigned long)NtCurrentTeb()->Tib.ExceptionList; if (jmp->Registration == ~0UL) { jmp->TryLevel = TRYLEVEL_END; } else { int i; va_list args; va_start( args, nb_args ); if (nb_args > 0) jmp->UnwindFunc = va_arg( args, unsigned long ); if (nb_args > 1) jmp->TryLevel = va_arg( args, unsigned long ); else jmp->TryLevel = ((MSVCRT_EXCEPTION_FRAME*)jmp->Registration)->trylevel; for (i = 0; i < 6 && i < nb_args - 2; i++) jmp->UnwindData[i] = va_arg( args, unsigned long ); va_end( args ); } TRACE("buf=%p ebx=%08lx esi=%08lx edi=%08lx ebp=%08lx esp=%08lx eip=%08lx frame=%08lx\n", jmp, jmp->Ebx, jmp->Esi, jmp->Edi, jmp->Ebp, jmp->Esp, jmp->Eip, jmp->Registration ); return 0; } /********************************************************************* * longjmp (MSVCRT.@) */ void CDECL MSVCRT_longjmp(struct MSVCRT___JUMP_BUFFER *jmp, int retval) { unsigned long cur_frame = 0; TRACE("buf=%p ebx=%08lx esi=%08lx edi=%08lx ebp=%08lx esp=%08lx eip=%08lx frame=%08lx retval=%08x\n", jmp, jmp->Ebx, jmp->Esi, jmp->Edi, jmp->Ebp, jmp->Esp, jmp->Eip, jmp->Registration, 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 msvcrt_local_unwind2((MSVCRT_EXCEPTION_FRAME*)jmp->Registration, jmp->TryLevel, (void *)jmp->Ebp); } if (!retval) retval = 1; longjmp_set_regs( jmp, retval ); } /********************************************************************* * _seh_longjmp_unwind (MSVCRT.@) */ void __stdcall _seh_longjmp_unwind(struct MSVCRT___JUMP_BUFFER *jmp) { msvcrt_local_unwind2( (MSVCRT_EXCEPTION_FRAME *)jmp->Registration, jmp->TryLevel, (void *)jmp->Ebp ); } /********************************************************************* * _seh_longjmp_unwind4 (MSVCRT.@) */ void __stdcall _seh_longjmp_unwind4(struct MSVCRT___JUMP_BUFFER *jmp) { msvcrt_local_unwind4( (void *)jmp->Cookie, (MSVCRT_EXCEPTION_FRAME *)jmp->Registration, jmp->TryLevel, (void *)jmp->Ebp ); } #elif defined(__x86_64__) /******************************************************************* * _setjmp (MSVCRT.@) */ __ASM_GLOBAL_FUNC( MSVCRT__setjmp, "xorq %rdx,%rdx\n\t" /* frame */ "jmp " __ASM_NAME("MSVCRT__setjmpex") ); /******************************************************************* * _setjmpex (MSVCRT.@) */ __ASM_GLOBAL_FUNC( MSVCRT__setjmpex, "movq %rdx,(%rcx)\n\t" /* jmp_buf->Frame */ "movq %rbx,0x8(%rcx)\n\t" /* jmp_buf->Rbx */ "leaq 0x8(%rsp),%rax\n\t" "movq %rax,0x10(%rcx)\n\t" /* jmp_buf->Rsp */ "movq %rbp,0x18(%rcx)\n\t" /* jmp_buf->Rbp */ "movq %rsi,0x20(%rcx)\n\t" /* jmp_buf->Rsi */ "movq %rdi,0x28(%rcx)\n\t" /* jmp_buf->Rdi */ "movq %r12,0x30(%rcx)\n\t" /* jmp_buf->R12 */ "movq %r13,0x38(%rcx)\n\t" /* jmp_buf->R13 */ "movq %r14,0x40(%rcx)\n\t" /* jmp_buf->R14 */ "movq %r15,0x48(%rcx)\n\t" /* jmp_buf->R15 */ "movq (%rsp),%rax\n\t" "movq %rax,0x50(%rcx)\n\t" /* jmp_buf->Rip */ "movdqa %xmm6,0x60(%rcx)\n\t" /* jmp_buf->Xmm6 */ "movdqa %xmm7,0x70(%rcx)\n\t" /* jmp_buf->Xmm7 */ "movdqa %xmm8,0x80(%rcx)\n\t" /* jmp_buf->Xmm8 */ "movdqa %xmm9,0x90(%rcx)\n\t" /* jmp_buf->Xmm9 */ "movdqa %xmm10,0xa0(%rcx)\n\t" /* jmp_buf->Xmm10 */ "movdqa %xmm11,0xb0(%rcx)\n\t" /* jmp_buf->Xmm11 */ "movdqa %xmm12,0xc0(%rcx)\n\t" /* jmp_buf->Xmm12 */ "movdqa %xmm13,0xd0(%rcx)\n\t" /* jmp_buf->Xmm13 */ "movdqa %xmm14,0xe0(%rcx)\n\t" /* jmp_buf->Xmm14 */ "movdqa %xmm15,0xf0(%rcx)\n\t" /* jmp_buf->Xmm15 */ "xorq %rax,%rax\n\t" "retq" ); extern void DECLSPEC_NORETURN CDECL longjmp_set_regs( struct MSVCRT___JUMP_BUFFER *jmp, int retval ); __ASM_GLOBAL_FUNC( longjmp_set_regs, "movq %rdx,%rax\n\t" /* retval */ "movq 0x8(%rcx),%rbx\n\t" /* jmp_buf->Rbx */ "movq 0x18(%rcx),%rbp\n\t" /* jmp_buf->Rbp */ "movq 0x20(%rcx),%rsi\n\t" /* jmp_buf->Rsi */ "movq 0x28(%rcx),%rdi\n\t" /* jmp_buf->Rdi */ "movq 0x30(%rcx),%r12\n\t" /* jmp_buf->R12 */ "movq 0x38(%rcx),%r13\n\t" /* jmp_buf->R13 */ "movq 0x40(%rcx),%r14\n\t" /* jmp_buf->R14 */ "movq 0x48(%rcx),%r15\n\t" /* jmp_buf->R15 */ "movdqa 0x60(%rcx),%xmm6\n\t" /* jmp_buf->Xmm6 */ "movdqa 0x70(%rcx),%xmm7\n\t" /* jmp_buf->Xmm7 */ "movdqa 0x80(%rcx),%xmm8\n\t" /* jmp_buf->Xmm8 */ "movdqa 0x90(%rcx),%xmm9\n\t" /* jmp_buf->Xmm9 */ "movdqa 0xa0(%rcx),%xmm10\n\t" /* jmp_buf->Xmm10 */ "movdqa 0xb0(%rcx),%xmm11\n\t" /* jmp_buf->Xmm11 */ "movdqa 0xc0(%rcx),%xmm12\n\t" /* jmp_buf->Xmm12 */ "movdqa 0xd0(%rcx),%xmm13\n\t" /* jmp_buf->Xmm13 */ "movdqa 0xe0(%rcx),%xmm14\n\t" /* jmp_buf->Xmm14 */ "movdqa 0xf0(%rcx),%xmm15\n\t" /* jmp_buf->Xmm15 */ "movq 0x50(%rcx),%rdx\n\t" /* jmp_buf->Rip */ "movq 0x10(%rcx),%rsp\n\t" /* jmp_buf->Rsp */ "jmp *%rdx" ); /******************************************************************* * longjmp (MSVCRT.@) */ void __cdecl MSVCRT_longjmp( struct MSVCRT___JUMP_BUFFER *jmp, int retval ) { EXCEPTION_RECORD rec; if (!retval) retval = 1; if (jmp->Frame) { rec.ExceptionCode = STATUS_LONGJUMP; rec.ExceptionFlags = 0; rec.ExceptionRecord = NULL; rec.ExceptionAddress = NULL; rec.NumberParameters = 1; rec.ExceptionInformation[0] = (DWORD_PTR)jmp; RtlUnwind( (void *)jmp->Frame, (void *)jmp->Rip, &rec, IntToPtr(retval) ); } longjmp_set_regs( jmp, retval ); } /******************************************************************* * _local_unwind (MSVCRT.@) */ void __cdecl _local_unwind( void *frame, void *target ) { CONTEXT context; RtlUnwindEx( frame, target, NULL, 0, &context, NULL ); } #endif /* __x86_64__ */ static MSVCRT___sighandler_t sighandlers[MSVCRT_NSIG] = { MSVCRT_SIG_DFL }; static BOOL WINAPI msvcrt_console_handler(DWORD ctrlType) { BOOL ret = FALSE; switch (ctrlType) { case CTRL_C_EVENT: if (sighandlers[MSVCRT_SIGINT]) { if (sighandlers[MSVCRT_SIGINT] != MSVCRT_SIG_IGN) sighandlers[MSVCRT_SIGINT](MSVCRT_SIGINT); ret = TRUE; } break; } return ret; } typedef void (CDECL *float_handler)(int, int); /* The exception codes are actually NTSTATUS values */ static const struct { NTSTATUS status; int signal; } float_exception_map[] = { { EXCEPTION_FLT_DENORMAL_OPERAND, MSVCRT__FPE_DENORMAL }, { EXCEPTION_FLT_DIVIDE_BY_ZERO, MSVCRT__FPE_ZERODIVIDE }, { EXCEPTION_FLT_INEXACT_RESULT, MSVCRT__FPE_INEXACT }, { EXCEPTION_FLT_INVALID_OPERATION, MSVCRT__FPE_INVALID }, { EXCEPTION_FLT_OVERFLOW, MSVCRT__FPE_OVERFLOW }, { EXCEPTION_FLT_STACK_CHECK, MSVCRT__FPE_STACKOVERFLOW }, { EXCEPTION_FLT_UNDERFLOW, MSVCRT__FPE_UNDERFLOW }, }; static LONG WINAPI msvcrt_exception_filter(struct _EXCEPTION_POINTERS *except) { LONG ret = EXCEPTION_CONTINUE_SEARCH; MSVCRT___sighandler_t handler; if (!except || !except->ExceptionRecord) return EXCEPTION_CONTINUE_SEARCH; switch (except->ExceptionRecord->ExceptionCode) { case EXCEPTION_ACCESS_VIOLATION: if ((handler = sighandlers[MSVCRT_SIGSEGV]) != MSVCRT_SIG_DFL) { if (handler != MSVCRT_SIG_IGN) { sighandlers[MSVCRT_SIGSEGV] = MSVCRT_SIG_DFL; handler(MSVCRT_SIGSEGV); } ret = EXCEPTION_CONTINUE_EXECUTION; } break; /* According to msdn, * the FPE signal handler takes as a second argument the type of * floating point exception. */ case EXCEPTION_FLT_DENORMAL_OPERAND: case EXCEPTION_FLT_DIVIDE_BY_ZERO: case EXCEPTION_FLT_INEXACT_RESULT: case EXCEPTION_FLT_INVALID_OPERATION: case EXCEPTION_FLT_OVERFLOW: case EXCEPTION_FLT_STACK_CHECK: case EXCEPTION_FLT_UNDERFLOW: if ((handler = sighandlers[MSVCRT_SIGFPE]) != MSVCRT_SIG_DFL) { if (handler != MSVCRT_SIG_IGN) { unsigned int i; int float_signal = MSVCRT__FPE_INVALID; sighandlers[MSVCRT_SIGFPE] = MSVCRT_SIG_DFL; for (i = 0; i < sizeof(float_exception_map) / sizeof(float_exception_map[0]); i++) { if (float_exception_map[i].status == except->ExceptionRecord->ExceptionCode) { float_signal = float_exception_map[i].signal; break; } } ((float_handler)handler)(MSVCRT_SIGFPE, float_signal); } ret = EXCEPTION_CONTINUE_EXECUTION; } break; case EXCEPTION_ILLEGAL_INSTRUCTION: case EXCEPTION_PRIV_INSTRUCTION: if ((handler = sighandlers[MSVCRT_SIGILL]) != MSVCRT_SIG_DFL) { if (handler != MSVCRT_SIG_IGN) { sighandlers[MSVCRT_SIGILL] = MSVCRT_SIG_DFL; handler(MSVCRT_SIGILL); } ret = EXCEPTION_CONTINUE_EXECUTION; } break; } return ret; } void msvcrt_init_signals(void) { SetConsoleCtrlHandler(msvcrt_console_handler, TRUE); SetUnhandledExceptionFilter(msvcrt_exception_filter); } void msvcrt_free_signals(void) { SetConsoleCtrlHandler(msvcrt_console_handler, FALSE); SetUnhandledExceptionFilter(NULL); } /********************************************************************* * signal (MSVCRT.@) * Some signals may never be generated except through an explicit call to * raise. */ MSVCRT___sighandler_t CDECL MSVCRT_signal(int sig, MSVCRT___sighandler_t func) { MSVCRT___sighandler_t ret = MSVCRT_SIG_ERR; TRACE("(%d, %p)\n", sig, func); if (func == MSVCRT_SIG_ERR) return MSVCRT_SIG_ERR; switch (sig) { /* Cases handled internally. Note SIGTERM is never generated by Windows, * so we effectively mask it. */ case MSVCRT_SIGABRT: case MSVCRT_SIGFPE: case MSVCRT_SIGILL: case MSVCRT_SIGSEGV: case MSVCRT_SIGINT: case MSVCRT_SIGTERM: case MSVCRT_SIGBREAK: ret = sighandlers[sig]; sighandlers[sig] = func; break; default: ret = MSVCRT_SIG_ERR; } return ret; } /********************************************************************* * raise (MSVCRT.@) */ int CDECL MSVCRT_raise(int sig) { MSVCRT___sighandler_t handler; TRACE("(%d)\n", sig); switch (sig) { case MSVCRT_SIGABRT: case MSVCRT_SIGFPE: case MSVCRT_SIGILL: case MSVCRT_SIGSEGV: case MSVCRT_SIGINT: case MSVCRT_SIGTERM: case MSVCRT_SIGBREAK: handler = sighandlers[sig]; if (handler == MSVCRT_SIG_DFL) MSVCRT__exit(3); if (handler != MSVCRT_SIG_IGN) { sighandlers[sig] = MSVCRT_SIG_DFL; if (sig == MSVCRT_SIGFPE) ((float_handler)handler)(sig, MSVCRT__FPE_EXPLICITGEN); else handler(sig); } break; default: return -1; } return 0; } /********************************************************************* * _XcptFilter (MSVCRT.@) */ int CDECL _XcptFilter(NTSTATUS ex, PEXCEPTION_POINTERS ptr) { TRACE("(%08x,%p)\n", ex, ptr); /* I assume ptr->ExceptionRecord->ExceptionCode is the same as ex */ return msvcrt_exception_filter(ptr); } /********************************************************************* * _abnormal_termination (MSVCRT.@) */ int CDECL _abnormal_termination(void) { FIXME("(void)stub\n"); return 0; } /****************************************************************** * MSVCRT___uncaught_exception */ BOOL CDECL MSVCRT___uncaught_exception(void) { return FALSE; } /* _set_security_error_handler - not exported in native msvcrt, added in msvcr70 */ MSVCRT_security_error_handler CDECL _set_security_error_handler( MSVCRT_security_error_handler handler ) { MSVCRT_security_error_handler old = security_error_handler; TRACE("(%p)\n", handler); security_error_handler = handler; return old; }