| /* | |
| * msvcrt C++ exception handling | |
| * | |
| * Copyright 2002 Alexandre Julliard | |
| * | |
| * 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 | |
| */ | |
| #ifndef __MSVCRT_CPPEXCEPT_H | |
| #define __MSVCRT_CPPEXCEPT_H | |
| #define COBJMACROS | |
| #include <fpieee.h> | |
| #include "unknwn.h" | |
| #include "cxx.h" | |
| #define CXX_FRAME_MAGIC_VC6 0x19930520 | |
| #define CXX_FRAME_MAGIC_VC7 0x19930521 | |
| #define CXX_FRAME_MAGIC_VC8 0x19930522 | |
| #define CXX_EXCEPTION 0xe06d7363 | |
| typedef struct | |
| { | |
| UINT ip; | |
| int state; | |
| } ipmap_info; | |
| #ifndef CXX_USE_RVA | |
| #define CXX_EXCEPTION_PARAMS 3 | |
| /* info about a single catch {} block */ | |
| typedef struct | |
| { | |
| 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; | |
| /* info about a single try {} block */ | |
| typedef struct | |
| { | |
| 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 */ | |
| unsigned 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 | |
| { | |
| 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 | |
| { | |
| UINT magic : 29; /* must be CXX_FRAME_MAGIC */ | |
| UINT bbt_flags : 3; | |
| 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 ipmap_info *ipmap; | |
| const void *expect_list; /* expected exceptions list when magic >= VC7 */ | |
| UINT flags; /* flags when magic >= VC8 */ | |
| } cxx_function_descr; | |
| #else /* CXX_USE_RVA */ | |
| #define CXX_EXCEPTION_PARAMS 4 | |
| typedef struct | |
| { | |
| UINT flags; | |
| UINT type_info; | |
| int offset; | |
| UINT handler; | |
| #ifdef _WIN64 | |
| UINT frame; | |
| #endif | |
| } catchblock_info; | |
| typedef struct | |
| { | |
| int start_level; | |
| int end_level; | |
| int catch_level; | |
| UINT catchblock_count; | |
| UINT catchblock; | |
| } tryblock_info; | |
| typedef struct | |
| { | |
| int prev; | |
| UINT handler; | |
| } unwind_info; | |
| typedef struct | |
| { | |
| UINT magic : 29; | |
| UINT bbt_flags : 3; | |
| UINT unwind_count; | |
| UINT unwind_table; | |
| UINT tryblock_count; | |
| UINT tryblock; | |
| UINT ipmap_count; | |
| UINT ipmap; | |
| int unwind_help; | |
| UINT expect_list; | |
| UINT flags; | |
| } cxx_function_descr; | |
| #endif /* CXX_USE_RVA */ | |
| #define FUNC_DESCR_SYNCHRONOUS 1 /* synchronous exceptions only (built with /EHs and /EHsc) */ | |
| #define FUNC_DESCR_NOEXCEPT 4 /* noexcept function */ | |
| typedef struct winrt_exception_info | |
| { | |
| BSTR description; | |
| BSTR restricted_desc; | |
| void *unknown1; | |
| void *unknown2; | |
| HRESULT hr; | |
| void *error_info; /* IRestrictedErrorInfo */ | |
| const cxx_exception_type *exception_type; | |
| UINT32 unknown3; | |
| void (*WINAPI set_exception_info)(struct winrt_exception_info **); | |
| } winrt_exception_info; | |
| void WINAPI DECLSPEC_NORETURN _CxxThrowException(void*,const cxx_exception_type*); | |
| static inline BOOL is_cxx_exception( EXCEPTION_RECORD *rec ) | |
| { | |
| if (rec->ExceptionCode != CXX_EXCEPTION) return FALSE; | |
| if (rec->NumberParameters != CXX_EXCEPTION_PARAMS) return FALSE; | |
| return (rec->ExceptionInformation[0] >= CXX_FRAME_MAGIC_VC6 && | |
| rec->ExceptionInformation[0] <= CXX_FRAME_MAGIC_VC8); | |
| } | |
| typedef struct | |
| { | |
| EXCEPTION_RECORD *rec; | |
| LONG *ref; /* not binary compatible with native msvcr100 */ | |
| } exception_ptr; | |
| void throw_exception(const char*); | |
| void exception_ptr_from_record(exception_ptr*,EXCEPTION_RECORD*); | |
| void __cdecl __ExceptionPtrCreate(exception_ptr*); | |
| void __cdecl __ExceptionPtrDestroy(exception_ptr*); | |
| void __cdecl __ExceptionPtrRethrow(const exception_ptr*); | |
| BOOL __cdecl __uncaught_exception(void); | |
| static inline const char *dbgstr_type_info( const type_info *info ) | |
| { | |
| if (!info) return "{}"; | |
| return wine_dbg_sprintf( "{vtable=%p name=%s (%s)}", | |
| info->vtable, info->mangled, info->name ? info->name : "" ); | |
| } | |
| /* compute the this pointer for a base class of a given type */ | |
| static inline void *get_this_pointer( const this_ptr_offsets *off, void *object ) | |
| { | |
| if (!object) return NULL; | |
| if (off->vbase_descr >= 0) | |
| { | |
| int *offset_ptr; | |
| /* move this ptr to vbase descriptor */ | |
| object = (char *)object + off->vbase_descr; | |
| /* and fetch additional offset from vbase descriptor */ | |
| offset_ptr = (int *)(*(char **)object + off->vbase_offset); | |
| object = (char *)object + *offset_ptr; | |
| } | |
| object = (char *)object + off->this_offset; | |
| return object; | |
| } | |
| #ifdef __ASM_USE_THISCALL_WRAPPER | |
| extern void call_copy_ctor( void *func, void *this, void *src, int has_vbase ); | |
| extern void call_dtor( void *func, void *this ); | |
| #else | |
| static inline void call_copy_ctor( void *func, void *this, void *src, int has_vbase ) | |
| { | |
| if (has_vbase) | |
| ((void (__thiscall*)(void*, void*, BOOL))func)(this, src, 1); | |
| else | |
| ((void (__thiscall*)(void*, void*))func)(this, src); | |
| } | |
| static inline void call_dtor( void *func, void *this ) | |
| { | |
| ((void (__thiscall*)(void*))func)( this ); | |
| } | |
| #endif | |
| /* check if the exception type is caught by a given catch block, and return the type that matched */ | |
| static inline const cxx_type_info *find_caught_type( cxx_exception_type *exc_type, uintptr_t base, | |
| const type_info *catch_ti, UINT catch_flags ) | |
| { | |
| const cxx_type_info_table *type_info_table = cxx_rva( exc_type->type_info_table, base ); | |
| UINT i; | |
| for (i = 0; i < type_info_table->count; i++) | |
| { | |
| const cxx_type_info *type = cxx_rva( type_info_table->info[i], base ); | |
| const type_info *ti = cxx_rva( type->type_info, base ); | |
| if (!catch_ti) return type; /* catch(...) matches any type */ | |
| if (catch_ti != ti) | |
| { | |
| if (strcmp( catch_ti->mangled, ti->mangled )) continue; | |
| } | |
| /* type is the same, now check the flags */ | |
| if ((exc_type->flags & TYPE_FLAG_CONST) && | |
| !(catch_flags & TYPE_FLAG_CONST)) continue; | |
| if ((exc_type->flags & TYPE_FLAG_VOLATILE) && | |
| !(catch_flags & TYPE_FLAG_VOLATILE)) continue; | |
| return type; /* it matched */ | |
| } | |
| return NULL; | |
| } | |
| /* copy the exception object where the catch block wants it */ | |
| static inline void copy_exception( void *object, void **dest, UINT catch_flags, | |
| const cxx_type_info *type, uintptr_t base ) | |
| { | |
| if (catch_flags & TYPE_FLAG_REFERENCE) | |
| { | |
| *dest = get_this_pointer( &type->offsets, object ); | |
| } | |
| else if (type->flags & CLASS_IS_SIMPLE_TYPE) | |
| { | |
| if (type->flags & CLASS_IS_WINRT && *(IUnknown**)object) | |
| IUnknown_AddRef(*(IUnknown**)object); | |
| memmove( dest, object, type->size ); | |
| /* if it is a pointer, adjust it */ | |
| if (type->size == sizeof(void*)) *dest = get_this_pointer( &type->offsets, *dest ); | |
| } | |
| else /* copy the object */ | |
| { | |
| if (type->copy_ctor) | |
| { | |
| call_copy_ctor( cxx_rva( type->copy_ctor, base ), dest, | |
| get_this_pointer( &type->offsets, object ), | |
| (type->flags & CLASS_HAS_VIRTUAL_BASE_CLASS) ); | |
| } | |
| else | |
| { | |
| if (type->flags & CLASS_IS_WINRT && *(IUnknown**)object) | |
| IUnknown_AddRef(*(IUnknown**)object); | |
| memmove( dest, get_this_pointer( &type->offsets, object ), type->size ); | |
| } | |
| } | |
| } | |
| #define TRACE_EXCEPTION_TYPE(type,base) do { \ | |
| const cxx_type_info_table *table = cxx_rva( type->type_info_table, base ); \ | |
| unsigned int i; \ | |
| TRACE( "flags %x destr %p handler %p type info %p\n", \ | |
| type->flags, cxx_rva( type->destructor, base ), \ | |
| type->custom_handler ? cxx_rva( type->custom_handler, base ) : NULL, table ); \ | |
| for (i = 0; i < table->count; i++) \ | |
| { \ | |
| const cxx_type_info *type = cxx_rva( table->info[i], base ); \ | |
| const type_info *info = cxx_rva( type->type_info, base ); \ | |
| TRACE( " %d: flags %x type %p %s offsets %d,%d,%d size %d copy ctor %p\n", \ | |
| i, type->flags, info, dbgstr_type_info( info ), \ | |
| type->offsets.this_offset, type->offsets.vbase_descr, type->offsets.vbase_offset, \ | |
| type->size, cxx_rva( type->copy_ctor, base )); \ | |
| } \ | |
| } while(0) | |
| extern void dump_function_descr( const cxx_function_descr *descr, uintptr_t base ); | |
| extern void *find_catch_handler( void *object, uintptr_t frame, uintptr_t exc_base, | |
| const tryblock_info *tryblock, | |
| cxx_exception_type *exc_type, uintptr_t image_base ); | |
| extern int handle_fpieee_flt( __msvcrt_ulong exception_code, EXCEPTION_POINTERS *ep, | |
| int (__cdecl *handler)(_FPIEEE_RECORD*) ); | |
| #ifndef __i386__ | |
| extern void *call_catch_handler( EXCEPTION_RECORD *rec ); | |
| extern void *call_unwind_handler( void *func, uintptr_t frame, DISPATCHER_CONTEXT *dispatch ); | |
| extern ULONG_PTR get_exception_pc( DISPATCHER_CONTEXT *dispatch ); | |
| #endif | |
| #if _MSVCR_VER >= 80 | |
| #define EXCEPTION_MANGLED_NAME ".?AVexception@std@@" | |
| #else | |
| #define EXCEPTION_MANGLED_NAME ".?AVexception@@" | |
| #endif | |
| #define CREATE_EXCEPTION_OBJECT(exception_name) \ | |
| static exception* __exception_ctor(exception *this, const char *str, const vtable_ptr *vtbl) \ | |
| { \ | |
| if (str) \ | |
| { \ | |
| unsigned int len = strlen(str) + 1; \ | |
| this->name = malloc(len); \ | |
| memcpy(this->name, str, len); \ | |
| this->do_free = TRUE; \ | |
| } \ | |
| else \ | |
| { \ | |
| this->name = NULL; \ | |
| this->do_free = FALSE; \ | |
| } \ | |
| this->vtable = vtbl; \ | |
| return this; \ | |
| } \ | |
| \ | |
| static exception* __exception_copy_ctor(exception *this, const exception *rhs, const vtable_ptr *vtbl) \ | |
| { \ | |
| if (rhs->do_free) \ | |
| { \ | |
| __exception_ctor(this, rhs->name, vtbl); \ | |
| } \ | |
| else \ | |
| { \ | |
| *this = *rhs; \ | |
| this->vtable = vtbl; \ | |
| } \ | |
| return this; \ | |
| } \ | |
| extern const vtable_ptr exception_name ## _vtable; \ | |
| DEFINE_THISCALL_WRAPPER(exception_name ## _copy_ctor,8) \ | |
| exception* __thiscall exception_name ## _copy_ctor(exception *this, const exception *rhs) \ | |
| { \ | |
| return __exception_copy_ctor(this, rhs, & exception_name ## _vtable); \ | |
| } \ | |
| \ | |
| DEFINE_THISCALL_WRAPPER(exception_name ## _dtor,4) \ | |
| void __thiscall exception_name ## _dtor(exception *this) \ | |
| { \ | |
| if (this->do_free) free(this->name); \ | |
| } \ | |
| \ | |
| DEFINE_THISCALL_WRAPPER(exception_name ## _vector_dtor,8) \ | |
| void* __thiscall exception_name ## _vector_dtor(exception *this, unsigned int flags) \ | |
| { \ | |
| if (flags & 2) \ | |
| { \ | |
| INT_PTR i, *ptr = (INT_PTR *)this - 1; \ | |
| \ | |
| for (i = *ptr - 1; i >= 0; i--) exception_name ## _dtor(this + i); \ | |
| operator_delete(ptr); \ | |
| } \ | |
| else \ | |
| { \ | |
| exception_name ## _dtor(this); \ | |
| if (flags & 1) operator_delete(this); \ | |
| } \ | |
| return this; \ | |
| } \ | |
| \ | |
| DEFINE_THISCALL_WRAPPER(exception_name ## _what,4) \ | |
| const char* __thiscall exception_name ## _what(exception *this) \ | |
| { \ | |
| return this->name ? this->name : "Unknown exception"; \ | |
| } \ | |
| \ | |
| __ASM_BLOCK_BEGIN(exception_name ## _vtables) \ | |
| __ASM_VTABLE(exception_name, \ | |
| VTABLE_ADD_FUNC(exception_name ## _vector_dtor) \ | |
| VTABLE_ADD_FUNC(exception_name ## _what)); \ | |
| __ASM_BLOCK_END \ | |
| \ | |
| DEFINE_RTTI_DATA(exception_name, 0, EXCEPTION_MANGLED_NAME) | |
| #endif /* __MSVCRT_CPPEXCEPT_H */ |