From 89b2dd08ffb16d92da900f0682fdca81ec79e790 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 13 Mar 2012 15:24:18 +0100 Subject: [PATCH] msvcrt: Move more i386-specific exception code to except_i386.c. --- dlls/msvcrt/cppexcept.h | 61 +---- dlls/msvcrt/except.c | 489 +--------------------------------- dlls/msvcrt/except_i386.c | 536 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 542 insertions(+), 544 deletions(-) diff --git a/dlls/msvcrt/cppexcept.h b/dlls/msvcrt/cppexcept.h index 705364bd09d..81066bfee14 100644 --- a/dlls/msvcrt/cppexcept.h +++ b/dlls/msvcrt/cppexcept.h @@ -44,59 +44,6 @@ typedef struct __exception int do_free; /* Whether to free 'name' in our dtor */ } exception; -/* the exception frame used by CxxFrameHandler */ -typedef struct __cxx_exception_frame -{ - EXCEPTION_REGISTRATION_RECORD frame; /* the standard exception frame */ - int trylevel; - DWORD ebp; -} cxx_exception_frame; - -/* info about a single catch {} block */ -typedef struct __catchblock_info -{ - UINT flags; /* flags (see below) */ - const type_info *type_info; /* C++ type caught by this block */ - int offset; /* stack offset to copy exception object to */ - void (*handler)(void);/* catch block handler code */ -} catchblock_info; -#define TYPE_FLAG_CONST 1 -#define TYPE_FLAG_VOLATILE 2 -#define TYPE_FLAG_REFERENCE 8 - -/* info about a single try {} block */ -typedef struct __tryblock_info -{ - int start_level; /* start trylevel of that block */ - int end_level; /* end trylevel of that block */ - int catch_level; /* initial trylevel of the catch block */ - int catchblock_count; /* count of catch blocks in array */ - const catchblock_info *catchblock; /* array of catch blocks */ -} tryblock_info; - -/* info about the unwind handler for a given trylevel */ -typedef struct __unwind_info -{ - int prev; /* prev trylevel unwind handler, to run after this one */ - void (*handler)(void);/* unwind handler */ -} unwind_info; - -/* descriptor of all try blocks of a given function */ -typedef struct __cxx_function_descr -{ - UINT magic; /* must be CXX_FRAME_MAGIC */ - UINT unwind_count; /* number of unwind handlers */ - const unwind_info *unwind_table; /* array of unwind handlers */ - UINT tryblock_count; /* number of try blocks */ - const tryblock_info *tryblock; /* array of try blocks */ - UINT ipmap_count; - const void *ipmap; - const void *expect_list; /* expected exceptions list when magic >= VC7 */ - UINT flags; /* flags when magic >= VC8 */ -} cxx_function_descr; - -#define FUNC_DESCR_SYNCHRONOUS 1 /* synchronous exceptions only (built with /EHs) */ - typedef void (*cxx_copy_ctor)(void); /* offsets for computing the this pointer */ @@ -126,9 +73,12 @@ typedef struct __cxx_type_info_table const cxx_type_info *info[3]; /* variable length, we declare it large enough for static RTTI */ } cxx_type_info_table; -typedef DWORD (*cxx_exc_custom_handler)( PEXCEPTION_RECORD, cxx_exception_frame*, +struct __cxx_exception_frame; +struct __cxx_function_descr; + +typedef DWORD (*cxx_exc_custom_handler)( PEXCEPTION_RECORD, struct __cxx_exception_frame*, PCONTEXT, EXCEPTION_REGISTRATION_RECORD**, - const cxx_function_descr*, int nested_trylevel, + const struct __cxx_function_descr*, int nested_trylevel, EXCEPTION_REGISTRATION_RECORD *nested_frame, DWORD unknown3 ); /* type information for an exception object */ @@ -142,7 +92,6 @@ typedef struct __cxx_exception_type void WINAPI _CxxThrowException(exception*,const cxx_exception_type*); int CDECL _XcptFilter(NTSTATUS, PEXCEPTION_POINTERS); -int CDECL __CppXcptFilter(NTSTATUS, PEXCEPTION_POINTERS); static inline const char *dbgstr_type_info( const type_info *info ) { diff --git a/dlls/msvcrt/except.c b/dlls/msvcrt/except.c index eb209036417..0a172c2f67f 100644 --- a/dlls/msvcrt/except.c +++ b/dlls/msvcrt/except.c @@ -41,494 +41,7 @@ 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__) +#ifdef __x86_64__ /******************************************************************* * _setjmp (MSVCRT.@) diff --git a/dlls/msvcrt/except_i386.c b/dlls/msvcrt/except_i386.c index c95f49b3ae0..d8b03a4518c 100644 --- a/dlls/msvcrt/except_i386.c +++ b/dlls/msvcrt/except_i386.c @@ -1,7 +1,9 @@ /* * msvcrt C++ exception handling * + * Copyright 2000 Jon Griffiths * Copyright 2002 Alexandre Julliard + * 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 @@ -42,6 +44,89 @@ WINE_DEFAULT_DEBUG_CHANNEL(seh); + +/* the exception frame used by CxxFrameHandler */ +typedef struct __cxx_exception_frame +{ + EXCEPTION_REGISTRATION_RECORD frame; /* the standard exception frame */ + int trylevel; + DWORD ebp; +} cxx_exception_frame; + +/* info about a single catch {} block */ +typedef struct __catchblock_info +{ + UINT flags; /* flags (see below) */ + const type_info *type_info; /* C++ type caught by this block */ + int offset; /* stack offset to copy exception object to */ + void (*handler)(void);/* catch block handler code */ +} catchblock_info; +#define TYPE_FLAG_CONST 1 +#define TYPE_FLAG_VOLATILE 2 +#define TYPE_FLAG_REFERENCE 8 + +/* info about a single try {} block */ +typedef struct __tryblock_info +{ + int start_level; /* start trylevel of that block */ + int end_level; /* end trylevel of that block */ + int catch_level; /* initial trylevel of the catch block */ + int catchblock_count; /* count of catch blocks in array */ + const catchblock_info *catchblock; /* array of catch blocks */ +} tryblock_info; + +/* info about the unwind handler for a given trylevel */ +typedef struct __unwind_info +{ + int prev; /* prev trylevel unwind handler, to run after this one */ + void (*handler)(void);/* unwind handler */ +} unwind_info; + +/* descriptor of all try blocks of a given function */ +typedef struct __cxx_function_descr +{ + UINT magic; /* must be CXX_FRAME_MAGIC */ + UINT unwind_count; /* number of unwind handlers */ + const unwind_info *unwind_table; /* array of unwind handlers */ + UINT tryblock_count; /* number of try blocks */ + const tryblock_info *tryblock; /* array of try blocks */ + UINT ipmap_count; + const void *ipmap; + const void *expect_list; /* expected exceptions list when magic >= VC7 */ + UINT flags; /* flags when magic >= VC8 */ +} cxx_function_descr; + +#define FUNC_DESCR_SYNCHRONOUS 1 /* synchronous exceptions only (built with /EHs) */ + +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 */ + DWORD CDECL cxx_frame_handler( PEXCEPTION_RECORD rec, cxx_exception_frame* frame, PCONTEXT context, EXCEPTION_REGISTRATION_RECORD** dispatch, const cxx_function_descr *descr, @@ -90,6 +175,41 @@ static inline void DECLSPEC_NORETURN continue_after_catch( cxx_exception_frame* for (;;) ; /* unreached */ } +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; +} + static inline void dump_type( const cxx_type_info *type ) { TRACE( "flags %x type %p %s offsets %d,%d,%d size %d copy ctor %p\n", @@ -496,4 +616,420 @@ unsigned int CDECL __CxxQueryExceptionSize(void) return sizeof(cxx_exception_type); } + +/********************************************************************* + * _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 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; +} + +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 ); +} + #endif /* __i386__ */