From 5b21408b3308bd83cf5b432d0ebbcafb981e00ef Mon Sep 17 00:00:00 2001 From: Aleksey Bragin Date: Sun, 11 Nov 2007 20:40:21 +0000 Subject: [PATCH] - Winesync cppexcept.c. This fixes try/catch C++ exception handling in various apps (like MFC based apps). svn path=/trunk/; revision=30363 --- reactos/lib/sdk/crt/except/cppexcept.c | 263 +++++++++++------- .../sdk/crt/include/internal/wine/cppexcept.h | 4 +- .../sdk/crt/include/internal/wine/msvcrt.h | 17 ++ reactos/media/doc/README.WINE | 2 +- 4 files changed, 183 insertions(+), 103 deletions(-) diff --git a/reactos/lib/sdk/crt/except/cppexcept.c b/reactos/lib/sdk/crt/except/cppexcept.c index 8aea563d926..ae6d9c3b553 100644 --- a/reactos/lib/sdk/crt/except/cppexcept.c +++ b/reactos/lib/sdk/crt/except/cppexcept.c @@ -15,7 +15,7 @@ * * 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 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * NOTES * A good reference is the article "How a C++ compiler implements @@ -39,44 +39,59 @@ #include -WINE_DEFAULT_DEBUG_CHANNEL(seh); - #ifdef __i386__ /* CxxFrameHandler is not supported on non-i386 */ -static DWORD cxx_frame_handler( PEXCEPTION_RECORD rec, cxx_exception_frame* frame, - PCONTEXT exc_context, EXCEPTION_REGISTRATION_RECORD** dispatch, - cxx_function_descr *descr, EXCEPTION_REGISTRATION_RECORD* nested_frame, - int nested_trylevel ); +WINE_DEFAULT_DEBUG_CHANNEL(seh); + +DWORD CDECL cxx_frame_handler( PEXCEPTION_RECORD rec, cxx_exception_frame* frame, + PCONTEXT context, EXCEPTION_REGISTRATION_RECORD** dispatch, + const cxx_function_descr *descr, + EXCEPTION_REGISTRATION_RECORD* nested_frame, int nested_trylevel ); /* call a function with a given ebp */ -__inline static void *call_ebp_func( void *func, void *ebp ) +static inline void *call_ebp_func( void *func, void *ebp ) { void *ret; - __asm__ __volatile__ ("pushl %%ebp; movl %2,%%ebp; call *%%eax; popl %%ebp" \ - : "=a" (ret) : "0" (func), "g" (ebp) : "ecx", "edx", "memory" ); + int dummy; + __asm__ __volatile__ ("pushl %%ebx\n\t" + "pushl %%ebp\n\t" + "movl %4,%%ebp\n\t" + "call *%%eax\n\t" + "popl %%ebp\n\t" + "popl %%ebx" + : "=a" (ret), "=S" (dummy), "=D" (dummy) + : "0" (func), "1" (ebp) : "ecx", "edx", "memory" ); return ret; } /* call a copy constructor */ -__inline static void call_copy_ctor( void *func, void *this, void *src, int has_vbase ) +static inline void call_copy_ctor( void *func, void *this, void *src, int has_vbase ) { TRACE( "calling copy ctor %p object %p src %p\n", func, this, src ); if (has_vbase) /* in that case copy ctor takes an extra bool indicating whether to copy the base class */ __asm__ __volatile__("pushl $1; pushl %2; call *%0" - : : "r" (func), "c" (this), "g" (src) : "eax", "edx", "memory" ); + : : "r" (func), "c" (this), "r" (src) : "eax", "edx", "memory" ); else __asm__ __volatile__("pushl %2; call *%0" - : : "r" (func), "c" (this), "g" (src) : "eax", "edx", "memory" ); + : : "r" (func), "c" (this), "r" (src) : "eax", "edx", "memory" ); } /* call the destructor of the exception object */ -__inline static void call_dtor( void *func, void *object ) +static inline void call_dtor( void *func, void *object ) { __asm__ __volatile__("call *%0" : : "r" (func), "c" (object) : "eax", "edx", "memory" ); } -static void dump_type( const cxx_type_info *type ) +/* continue execution to the specified address after exception is caught */ +static inline void DECLSPEC_NORETURN continue_after_catch( cxx_exception_frame* frame, void *addr ) +{ + __asm__ __volatile__("movl -4(%0),%%esp; leal 12(%0),%%ebp; jmp *%1" + : : "r" (frame), "a" (addr) ); + for (;;) ; /* unreached */ +} + +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", type->flags, type->type_info, dbgstr_type_info(type->type_info), @@ -88,49 +103,47 @@ static void dump_exception_type( const cxx_exception_type *type ) { UINT i; - DPRINTF( "exception type:\n" ); - DPRINTF( "flags %x destr %p handler %p type info %p\n", + TRACE( "flags %x destr %p handler %p type info %p\n", type->flags, type->destructor, type->custom_handler, type->type_info_table ); for (i = 0; i < type->type_info_table->count; i++) { - DPRINTF( " %d: ", i ); + TRACE( " %d: ", i ); dump_type( type->type_info_table->info[i] ); } } -static void dump_function_descr( const cxx_function_descr *descr, const cxx_exception_type *info ) +static void dump_function_descr( const cxx_function_descr *descr ) { UINT i; int j; - DPRINTF( "function descr:\n" ); - DPRINTF( "magic %x\n", descr->magic ); - DPRINTF( "unwind table: %p %d\n", descr->unwind_table, descr->unwind_count ); + TRACE( "magic %x\n", descr->magic ); + TRACE( "unwind table: %p %d\n", descr->unwind_table, descr->unwind_count ); for (i = 0; i < descr->unwind_count; i++) { - DPRINTF( " %d: prev %d func %p\n", i, + TRACE( " %d: prev %d func %p\n", i, descr->unwind_table[i].prev, descr->unwind_table[i].handler ); } - DPRINTF( "try table: %p %d\n", descr->tryblock, descr->tryblock_count ); + TRACE( "try table: %p %d\n", descr->tryblock, descr->tryblock_count ); for (i = 0; i < descr->tryblock_count; i++) { - DPRINTF( " %d: start %d end %d catchlevel %d catch %p %d\n", i, + TRACE( " %d: start %d end %d catchlevel %d catch %p %d\n", i, descr->tryblock[i].start_level, descr->tryblock[i].end_level, descr->tryblock[i].catch_level, descr->tryblock[i].catchblock, descr->tryblock[i].catchblock_count ); for (j = 0; j < descr->tryblock[i].catchblock_count; j++) { - catchblock_info *ptr = &descr->tryblock[i].catchblock[j]; - DPRINTF( " %d: flags %x offset %d handler %p type %p", - j, ptr->flags, ptr->offset, ptr->handler, ptr->type_info ); - if (ptr->type_info) DPRINTF( " (%p %s)", ptr->type_info->name, ptr->type_info->mangled ); - DPRINTF( "\n" ); + const catchblock_info *ptr = &descr->tryblock[i].catchblock[j]; + TRACE( " %d: flags %x offset %d handler %p type %p %s\n", + j, ptr->flags, ptr->offset, ptr->handler, + ptr->type_info, dbgstr_type_info( ptr->type_info ) ); } } } /* check if the exception type is caught by a given catch block, and return the type that matched */ -static const cxx_type_info *find_caught_type( cxx_exception_type *exc_type, catchblock_info *catchblock ) +static const cxx_type_info *find_caught_type( cxx_exception_type *exc_type, + const catchblock_info *catchblock ) { UINT i; @@ -156,7 +169,7 @@ static const cxx_type_info *find_caught_type( cxx_exception_type *exc_type, catc /* copy the exception object where the catch block wants it */ static void copy_exception( void *object, cxx_exception_frame *frame, - catchblock_info *catchblock, const cxx_type_info *type ) + const catchblock_info *catchblock, const cxx_type_info *type ) { void **dest_ptr; @@ -185,14 +198,14 @@ static void copy_exception( void *object, cxx_exception_frame *frame, } /* unwind the local function up to a given trylevel */ -static void cxx_local_unwind( cxx_exception_frame* frame, cxx_function_descr *descr, int last_level) +static void cxx_local_unwind( cxx_exception_frame* frame, const cxx_function_descr *descr, int last_level) { void (*handler)(); int trylevel = frame->trylevel; while (trylevel != last_level) { - if (trylevel < 0 || trylevel >= (int)descr->unwind_count) + if (trylevel < 0 || trylevel >= descr->unwind_count) { ERR( "invalid trylevel %d\n", trylevel ); MSVCRT_terminate(); @@ -215,8 +228,9 @@ struct catch_func_nested_frame EXCEPTION_REGISTRATION_RECORD frame; /* standard exception frame */ EXCEPTION_RECORD *prev_rec; /* previous record to restore in thread data */ cxx_exception_frame *cxx_frame; /* frame of parent exception */ - cxx_function_descr *descr; /* descriptor of parent exception */ + const cxx_function_descr *descr; /* descriptor of parent exception */ int trylevel; /* current try level */ + EXCEPTION_RECORD *rec; /* rec associated with frame */ }; /* handler for exceptions happening while calling a catch function */ @@ -230,20 +244,41 @@ static DWORD catch_function_nested_handler( EXCEPTION_RECORD *rec, EXCEPTION_REG msvcrt_get_thread_data()->exc_record = nested_frame->prev_rec; return ExceptionContinueSearch; } - else + + TRACE( "got nested exception in catch function\n" ); + + if(rec->ExceptionCode == CXX_EXCEPTION) { - TRACE( "got nested exception in catch function\n" ); - return cxx_frame_handler( rec, nested_frame->cxx_frame, context, - NULL, nested_frame->descr, &nested_frame->frame, - nested_frame->trylevel ); + PEXCEPTION_RECORD prev_rec = nested_frame->rec; + if(rec->ExceptionInformation[1] == 0 && rec->ExceptionInformation[2] == 0) + { + /* exception was rethrown */ + rec->ExceptionInformation[1] = prev_rec->ExceptionInformation[1]; + rec->ExceptionInformation[2] = prev_rec->ExceptionInformation[2]; + TRACE("detect rethrow: re-propagate: obj: %lx, type: %lx\n", + rec->ExceptionInformation[1], rec->ExceptionInformation[2]); + } + else { + /* new exception in exception handler, destroy old */ + void *object = (void*)prev_rec->ExceptionInformation[1]; + cxx_exception_type *info = (cxx_exception_type*) prev_rec->ExceptionInformation[2]; + TRACE("detect threw new exception in catch block - destroy old(obj: %p type: %p)\n", + object, info); + if(info && info->destructor) + call_dtor( info->destructor, object ); + } } + + return cxx_frame_handler( rec, nested_frame->cxx_frame, context, + NULL, nested_frame->descr, &nested_frame->frame, + nested_frame->trylevel ); } /* find and call the appropriate catch block for an exception */ /* returns the address to continue execution to after the catch block was called */ -__inline static void *call_catch_block( PEXCEPTION_RECORD rec, cxx_exception_frame *frame, - cxx_function_descr *descr, int nested_trylevel, - cxx_exception_type *info ) +static inline void call_catch_block( PEXCEPTION_RECORD rec, cxx_exception_frame *frame, + const cxx_function_descr *descr, int nested_trylevel, + cxx_exception_type *info ) { UINT i; int j; @@ -251,10 +286,11 @@ __inline static void *call_catch_block( PEXCEPTION_RECORD rec, cxx_exception_fra struct catch_func_nested_frame nested_frame; int trylevel = frame->trylevel; MSVCRT_thread_data *thread_data = msvcrt_get_thread_data(); + DWORD save_esp = ((DWORD*)frame)[-1]; for (i = 0; i < descr->tryblock_count; i++) { - tryblock_info *tryblock = &descr->tryblock[i]; + const tryblock_info *tryblock = &descr->tryblock[i]; if (trylevel < tryblock->start_level) continue; if (trylevel > tryblock->end_level) continue; @@ -262,14 +298,24 @@ __inline static void *call_catch_block( PEXCEPTION_RECORD rec, cxx_exception_fra /* got a try block */ for (j = 0; j < tryblock->catchblock_count; j++) { - catchblock_info *catchblock = &tryblock->catchblock[j]; - const cxx_type_info *type = find_caught_type( info, catchblock ); - if (!type) continue; + const catchblock_info *catchblock = &tryblock->catchblock[j]; + if(info) + { + const cxx_type_info *type = find_caught_type( info, catchblock ); + if (!type) continue; - TRACE( "matched type %p in tryblock %d catchblock %d\n", type, i, j ); + TRACE( "matched type %p in tryblock %d catchblock %d\n", type, i, j ); - /* copy the exception to its destination on the stack */ - copy_exception( object, frame, catchblock, type ); + /* copy the exception to its destination on the stack */ + copy_exception( object, frame, catchblock, type ); + } + else + { + /* no CXX_EXCEPTION only proceed with a catch(...) block*/ + if(catchblock->type_info) + continue; + TRACE("found catch(...) block\n"); + } /* unwind the stack */ RtlUnwind( frame, 0, rec, 0 ); @@ -277,17 +323,17 @@ __inline static void *call_catch_block( PEXCEPTION_RECORD rec, cxx_exception_fra frame->trylevel = tryblock->end_level + 1; /* call the catch block */ - TRACE( "calling catch block %p for type %p addr %p ebp %p\n", - catchblock, type, catchblock->handler, &frame->ebp ); + TRACE( "calling catch block %p addr %p ebp %p\n", + catchblock, catchblock->handler, &frame->ebp ); /* setup an exception block for nested exceptions */ - //nested_frame.frame.Handler = catch_function_nested_handler; nested_frame.frame.Handler = (PEXCEPTION_HANDLER)catch_function_nested_handler; nested_frame.prev_rec = thread_data->exc_record; nested_frame.cxx_frame = frame; nested_frame.descr = descr; nested_frame.trylevel = nested_trylevel + 1; + nested_frame.rec = rec; __wine_push_frame( &nested_frame.frame ); thread_data->exc_record = rec; @@ -295,12 +341,13 @@ __inline static void *call_catch_block( PEXCEPTION_RECORD rec, cxx_exception_fra thread_data->exc_record = nested_frame.prev_rec; __wine_pop_frame( &nested_frame.frame ); - if (info->destructor) call_dtor( info->destructor, object ); + ((DWORD*)frame)[-1] = save_esp; + if (info && info->destructor) call_dtor( info->destructor, object ); TRACE( "done, continuing at %p\n", addr ); - return addr; + + continue_after_catch( frame, addr ); } } - return NULL; } @@ -309,14 +356,13 @@ __inline static void *call_catch_block( PEXCEPTION_RECORD rec, cxx_exception_fra * * Implementation of __CxxFrameHandler. */ -static DWORD cxx_frame_handler( PEXCEPTION_RECORD rec, cxx_exception_frame* frame, - PCONTEXT exc_context, EXCEPTION_REGISTRATION_RECORD** dispatch, - cxx_function_descr *descr, EXCEPTION_REGISTRATION_RECORD* nested_frame, - int nested_trylevel ) +DWORD CDECL cxx_frame_handler( PEXCEPTION_RECORD rec, cxx_exception_frame* frame, + PCONTEXT context, EXCEPTION_REGISTRATION_RECORD** dispatch, + const cxx_function_descr *descr, + EXCEPTION_REGISTRATION_RECORD* nested_frame, + int nested_trylevel ) { cxx_exception_type *exc_type; - void *next_ip; - PEXCEPTION_RECORD orig_rec = rec; if (descr->magic != CXX_FRAME_MAGIC) { @@ -330,71 +376,88 @@ static DWORD cxx_frame_handler( PEXCEPTION_RECORD rec, cxx_exception_frame* fram } if (!descr->tryblock_count) return ExceptionContinueSearch; - exc_type = (cxx_exception_type *)rec->ExceptionInformation[2]; - if (rec->ExceptionCode == CXX_EXCEPTION && - rec->ExceptionInformation[0] > CXX_FRAME_MAGIC && - exc_type->custom_handler) + if(rec->ExceptionCode == CXX_EXCEPTION) { - return exc_type->custom_handler( rec, frame, exc_context, dispatch, - descr, nested_trylevel, nested_frame, 0 ); - } - - if (!exc_type) /* nested exception, fetch info from original exception */ - { - rec = msvcrt_get_thread_data()->exc_record; exc_type = (cxx_exception_type *)rec->ExceptionInformation[2]; - } - if (TRACE_ON(seh)) + if (rec->ExceptionInformation[0] > CXX_FRAME_MAGIC && + exc_type->custom_handler) + { + return exc_type->custom_handler( rec, frame, context, dispatch, + descr, nested_trylevel, nested_frame, 0 ); + } + + if (TRACE_ON(seh)) + { + TRACE("handling C++ exception rec %p frame %p trylevel %d descr %p nested_frame %p\n", + rec, frame, frame->trylevel, descr, nested_frame ); + dump_exception_type( exc_type ); + dump_function_descr( descr ); + } + } + else { - TRACE("handling C++ exception rec %p frame %p trylevel %d descr %p nested_frame %p\n", - rec, frame, frame->trylevel, descr, nested_frame ); - dump_exception_type( exc_type ); - dump_function_descr( descr, exc_type ); + exc_type = NULL; + TRACE("handling C exception code %x rec %p frame %p trylevel %d descr %p nested_frame %p\n", + rec->ExceptionCode, rec, frame, frame->trylevel, descr, nested_frame ); } - next_ip = call_catch_block( rec, frame, descr, frame->trylevel, exc_type ); - - if (!next_ip) return ExceptionContinueSearch; - orig_rec->ExceptionFlags &= ~EH_NONCONTINUABLE; - exc_context->Eip = (DWORD)next_ip; - exc_context->Ebp = (DWORD)&frame->ebp; - exc_context->Esp = ((DWORD*)frame)[-1]; - return ExceptionContinueExecution; + call_catch_block( rec, frame, descr, frame->trylevel, exc_type ); + return ExceptionContinueSearch; } /********************************************************************* * __CxxFrameHandler (MSVCRT.@) */ -DWORD __CxxFrameHandler( PEXCEPTION_RECORD rec, EXCEPTION_REGISTRATION_RECORD* frame, - PCONTEXT exc_context, EXCEPTION_REGISTRATION_RECORD** dispatch ) -{ - cxx_function_descr *descr; +extern DWORD CDECL __CxxFrameHandler( PEXCEPTION_RECORD rec, EXCEPTION_REGISTRATION_RECORD* frame, + PCONTEXT context, EXCEPTION_REGISTRATION_RECORD** dispatch ); +__ASM_GLOBAL_FUNC( __CxxFrameHandler, + "pushl $0\n\t" /* nested_trylevel */ + "pushl $0\n\t" /* nested_frame */ + "pushl %eax\n\t" /* descr */ + "pushl 28(%esp)\n\t" /* dispatch */ + "pushl 28(%esp)\n\t" /* context */ + "pushl 28(%esp)\n\t" /* frame */ + "pushl 28(%esp)\n\t" /* rec */ + "call " __ASM_NAME("cxx_frame_handler") "\n\t" + "add $28,%esp\n\t" + "ret" ) - __asm__ __volatile__("mov %%eax, %0\n" : "=m"(descr)); - return cxx_frame_handler(rec, (cxx_exception_frame *)frame, - exc_context, dispatch, descr, NULL, 0 ); + +/********************************************************************* + * __CxxLongjmpUnwind (MSVCRT.@) + * + * Callback meant to be used as UnwindFunc for setjmp/longjmp. + */ +void __stdcall __CxxLongjmpUnwind( const struct MSVCRT___JUMP_BUFFER *buf ) +{ + cxx_exception_frame *frame = (cxx_exception_frame *)buf->Registration; + const cxx_function_descr *descr = (const cxx_function_descr *)buf->UnwindData[0]; + + TRACE( "unwinding frame %p descr %p trylevel %ld\n", frame, descr, buf->TryLevel ); + cxx_local_unwind( frame, descr, buf->TryLevel ); } + #endif /* __i386__ */ /********************************************************************* * _CxxThrowException (MSVCRT.@) */ -void _CxxThrowException( void *object, const cxx_exception_type *type ) +void CDECL _CxxThrowException( exception *object, const cxx_exception_type *type ) { - DWORD args[3]; + ULONG_PTR args[3]; args[0] = CXX_FRAME_MAGIC; - args[1] = (DWORD)object; - args[2] = (DWORD)type; + args[1] = (ULONG_PTR)object; + args[2] = (ULONG_PTR)type; RaiseException( CXX_EXCEPTION, EH_NONCONTINUABLE, 3, args ); } /********************************************************************* * __CxxDetectRethrow (MSVCRT.@) */ -BOOL __CxxDetectRethrow(PEXCEPTION_POINTERS ptrs) +BOOL CDECL __CxxDetectRethrow(PEXCEPTION_POINTERS ptrs) { PEXCEPTION_RECORD rec; @@ -417,7 +480,7 @@ BOOL __CxxDetectRethrow(PEXCEPTION_POINTERS ptrs) /********************************************************************* * __CxxQueryExceptionSize (MSVCRT.@) */ -unsigned int __CxxQueryExceptionSize(void) +unsigned int CDECL __CxxQueryExceptionSize(void) { return sizeof(cxx_exception_type); } diff --git a/reactos/lib/sdk/crt/include/internal/wine/cppexcept.h b/reactos/lib/sdk/crt/include/internal/wine/cppexcept.h index ea108aef85e..b09c662e443 100644 --- a/reactos/lib/sdk/crt/include/internal/wine/cppexcept.h +++ b/reactos/lib/sdk/crt/include/internal/wine/cppexcept.h @@ -121,7 +121,7 @@ typedef struct __cxx_type_info_table typedef DWORD (*cxx_exc_custom_handler)( PEXCEPTION_RECORD, cxx_exception_frame*, PCONTEXT, EXCEPTION_REGISTRATION_RECORD**, - cxx_function_descr*, int nested_trylevel, + const cxx_function_descr*, int nested_trylevel, EXCEPTION_REGISTRATION_RECORD *nested_frame, DWORD unknown3 ); /* type information for an exception object */ @@ -133,7 +133,7 @@ typedef struct __cxx_exception_type const cxx_type_info_table *type_info_table; /* list of types for this exception object */ } cxx_exception_type; -void _CxxThrowException(void*,const cxx_exception_type*); +void _CxxThrowException(exception*,const cxx_exception_type*); static inline const char *dbgstr_type_info( const type_info *info ) { diff --git a/reactos/lib/sdk/crt/include/internal/wine/msvcrt.h b/reactos/lib/sdk/crt/include/internal/wine/msvcrt.h index dcff35ce420..aecd61e0c45 100644 --- a/reactos/lib/sdk/crt/include/internal/wine/msvcrt.h +++ b/reactos/lib/sdk/crt/include/internal/wine/msvcrt.h @@ -166,6 +166,23 @@ extern void msvcrt_free_args(void); #define _RT_CRNL 252 #define _RT_BANNER 255 +#ifdef __i386__ +struct MSVCRT___JUMP_BUFFER { + unsigned long Ebp; + unsigned long Ebx; + unsigned long Edi; + unsigned long Esi; + unsigned long Esp; + unsigned long Eip; + unsigned long Registration; + unsigned long TryLevel; + /* Start of new struct members */ + unsigned long Cookie; + unsigned long UnwindFunc; + unsigned long UnwindData[6]; +}; +#endif /* __i386__ */ + typedef void* (*malloc_func_t)(size_t); typedef void (*free_func_t)(void*); #define MSVCRT_malloc malloc diff --git a/reactos/media/doc/README.WINE b/reactos/media/doc/README.WINE index 735714039d1..e06d3c83957 100644 --- a/reactos/media/doc/README.WINE +++ b/reactos/media/doc/README.WINE @@ -119,8 +119,8 @@ kernel32 - msvcrt - reactos/dll/win32/msvcrt/wine/*.c # Out of sync - reactos/lib/sdk/crt/except # Synced at XXXXXXXX reactos/lib/sdk/crt/cpp.c # Synced at 20071111 + reactos/lib/sdk/crt/cppexcept.c # Synced at 20071111 reactos/lib/sdk/crt/wine # Synced at XXXXXXXX User32 -