//Copyright (c) 2006-2009 Emil Dotchevski and Reverge Studios, Inc. //Distributed under the Boost Software License, Version 1.0. (See accompanying //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) //This MSVC-specific cpp file implements non-intrusive cloning of exception objects. //Based on an exception_ptr implementation by Anthony Williams. #ifdef BOOST_NO_EXCEPTIONS #error This file requires exception handling to be enabled. #endif #include #include #if defined(BOOST_ENABLE_NON_INTRUSIVE_EXCEPTION_PTR) && defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) //Non-intrusive cloning support implemented below, only for MSVC versions mentioned above. //Thanks Anthony Williams! //Thanks to Martin Weiss for implementing 64-bit support! #include #include #include #include namespace { unsigned const exception_maximum_parameters=15; unsigned const exception_noncontinuable=1; #if _MSC_VER==1310 int const exception_info_offset=0x74; #elif ((_MSC_VER==1400 || _MSC_VER==1500) && !defined _M_X64) int const exception_info_offset=0x80; #elif ((_MSC_VER==1400 || _MSC_VER==1500) && defined _M_X64) int const exception_info_offset=0xE0; #else int const exception_info_offset=-1; #endif struct exception_record { unsigned long ExceptionCode; unsigned long ExceptionFlags; exception_record * ExceptionRecord; void * ExceptionAddress; unsigned long NumberParameters; ULONG_PTR ExceptionInformation[exception_maximum_parameters]; }; struct exception_pointers { exception_record * ExceptionRecord; void * ContextRecord; }; unsigned const cpp_exception_code=0xE06D7363; unsigned const cpp_exception_magic_flag=0x19930520; #ifdef _M_X64 unsigned const cpp_exception_parameter_count=4; #else unsigned const cpp_exception_parameter_count=3; #endif struct dummy_exception_type { }; typedef int(dummy_exception_type::*normal_copy_constructor_ptr)(void * src); typedef int(dummy_exception_type::*copy_constructor_with_virtual_base_ptr)(void * src,void * dst); typedef void (dummy_exception_type::*destructor_ptr)(); union cpp_copy_constructor { void * address; normal_copy_constructor_ptr normal_copy_constructor; copy_constructor_with_virtual_base_ptr copy_constructor_with_virtual_base; }; union cpp_destructor { void * address; destructor_ptr destructor; }; enum cpp_type_flags { class_is_simple_type=1, class_has_virtual_base=4 }; // ATTENTION: On x86 fields such as type_info and copy_constructor are really pointers // but on 64bit these are 32bit offsets from HINSTANCE. Hints on the 64bit handling from // http://blogs.msdn.com/b/oldnewthing/archive/2010/07/30/10044061.aspx . struct cpp_type_info { unsigned flags; int type_info; int this_offset; int vbase_descr; int vbase_offset; unsigned long size; int copy_constructor; }; struct cpp_type_info_table { unsigned count; int info; }; struct cpp_exception_type { unsigned flags; int destructor; int custom_handler; int type_info_table; }; struct exception_object_deleter { cpp_exception_type const & et_; size_t image_base_; exception_object_deleter( cpp_exception_type const & et, size_t image_base ): et_(et), image_base_(image_base) { } void operator()( void * obj ) { BOOST_ASSERT(obj!=0); dummy_exception_type* dummy_exception_ptr = static_cast(obj); if( et_.destructor ) { cpp_destructor destructor; destructor.address = reinterpret_cast(et_.destructor + image_base_); (dummy_exception_ptr->*(destructor.destructor))(); } free(obj); } }; cpp_type_info const & get_cpp_type_info( cpp_exception_type const & et, size_t image_base ) { cpp_type_info_table * const typearray = reinterpret_cast(et.type_info_table + image_base); cpp_type_info * const ti = reinterpret_cast(typearray->info + image_base); BOOST_ASSERT(ti!=0); return *ti; } void copy_msvc_exception( void * dst, void * src, cpp_type_info const & ti, size_t image_base ) { cpp_copy_constructor copy_constructor; copy_constructor.address = reinterpret_cast(ti.copy_constructor + image_base); if( !(ti.flags & class_is_simple_type) && copy_constructor.normal_copy_constructor ) { dummy_exception_type * dummy_exception_ptr = static_cast(dst); if( ti.flags & class_has_virtual_base ) (dummy_exception_ptr->*(copy_constructor.copy_constructor_with_virtual_base))(src,dst); else (dummy_exception_ptr->*(copy_constructor.normal_copy_constructor))(src); } else memmove(dst,src,ti.size); } boost::shared_ptr clone_msvc_exception( void * src, cpp_exception_type const & et, size_t image_base ) { BOOST_ASSERT(src!=0); cpp_type_info const & ti=get_cpp_type_info(et,image_base); if( void * dst = malloc(ti.size) ) { try { copy_msvc_exception(dst,src,ti,image_base); } catch( ... ) { free(dst); throw; } return boost::shared_ptr(dst,exception_object_deleter(et,image_base)); } else throw std::bad_alloc(); } class cloned_exception: public boost::exception_detail::clone_base { cloned_exception( cloned_exception const & ); cloned_exception & operator=( cloned_exception const & ); cpp_exception_type const & et_; size_t image_base_; boost::shared_ptr exc_; public: cloned_exception( EXCEPTION_RECORD const * record ): et_(*reinterpret_cast(record->ExceptionInformation[2])), image_base_((cpp_exception_parameter_count==4) ? record->ExceptionInformation[3] : 0), exc_(clone_msvc_exception(reinterpret_cast(record->ExceptionInformation[1]),et_,image_base_)) { } cloned_exception( void * exc, cpp_exception_type const & et, size_t image_base ): et_(et), image_base_(image_base), exc_(clone_msvc_exception(exc,et_,image_base)) { } ~cloned_exception() BOOST_NOEXCEPT_OR_NOTHROW { } boost::exception_detail::clone_base const * clone() const { return new cloned_exception(exc_.get(),et_,image_base_); } void rethrow() const { cpp_type_info const & ti=get_cpp_type_info(et_,image_base_); void * dst = _alloca(ti.size); copy_msvc_exception(dst,exc_.get(),ti,image_base_); ULONG_PTR args[cpp_exception_parameter_count]; args[0]=cpp_exception_magic_flag; args[1]=reinterpret_cast(dst); args[2]=reinterpret_cast(&et_); if (cpp_exception_parameter_count==4) args[3]=image_base_; RaiseException(cpp_exception_code,EXCEPTION_NONCONTINUABLE,cpp_exception_parameter_count,args); } }; bool is_cpp_exception( EXCEPTION_RECORD const * record ) { return record && (record->ExceptionCode==cpp_exception_code) && (record->NumberParameters==cpp_exception_parameter_count) && (record->ExceptionInformation[0]==cpp_exception_magic_flag); } unsigned long exception_cloning_filter( int & result, boost::exception_detail::clone_base const * & ptr, void * info_ ) { BOOST_ASSERT(exception_info_offset>=0); BOOST_ASSERT(info_!=0); EXCEPTION_RECORD* record = static_cast(info_)->ExceptionRecord; if( is_cpp_exception(record) ) { if( !record->ExceptionInformation[2] ) record = *reinterpret_cast(reinterpret_cast(_errno())+exception_info_offset); if( is_cpp_exception(record) && record->ExceptionInformation[2] ) try { ptr = new cloned_exception(record); result = boost::exception_detail::clone_current_exception_result::success; } catch( std::bad_alloc & ) { result = boost::exception_detail::clone_current_exception_result::bad_alloc; } catch( ... ) { result = boost::exception_detail::clone_current_exception_result::bad_exception; } } return EXCEPTION_EXECUTE_HANDLER; } } namespace boost { namespace exception_detail { int clone_current_exception_non_intrusive( clone_base const * & cloned ) { BOOST_ASSERT(!cloned); int result = clone_current_exception_result::not_supported; if( exception_info_offset>=0 ) { clone_base const * ptr=0; __try { throw; } __except(exception_cloning_filter(result,ptr,GetExceptionInformation())) { } if( result==clone_current_exception_result::success ) cloned=ptr; } BOOST_ASSERT(result!=clone_current_exception_result::success || cloned); return result; } } } #else //On all other compilers, return clone_current_exception_result::not_supported. //On such platforms, only the intrusive enable_current_exception() cloning will work. namespace boost { namespace exception_detail { int clone_current_exception_non_intrusive( clone_base const * & ) { return clone_current_exception_result::not_supported; } } } #endif