blob: 932bca8910ad27343148cf171a7e6a8b244139bd [file] [log] [blame]
//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 <boost/config.hpp>
#include <boost/exception/detail/clone_current_exception.hpp>
#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 <boost/exception/exception.hpp>
#include <boost/shared_ptr.hpp>
#include <windows.h>
#include <malloc.h>
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<dummy_exception_type *>(obj);
if( et_.destructor )
{
cpp_destructor destructor;
destructor.address = reinterpret_cast<void *>(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<cpp_type_info_table * const>(et.type_info_table + image_base);
cpp_type_info * const ti = reinterpret_cast<cpp_type_info * const>(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<void *>(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<dummy_exception_type *>(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<void>
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<void>(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<void> exc_;
public:
cloned_exception( EXCEPTION_RECORD const * record ):
et_(*reinterpret_cast<cpp_exception_type const *>(record->ExceptionInformation[2])),
image_base_((cpp_exception_parameter_count==4) ? record->ExceptionInformation[3] : 0),
exc_(clone_msvc_exception(reinterpret_cast<void *>(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<ULONG_PTR>(dst);
args[2]=reinterpret_cast<ULONG_PTR>(&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<EXCEPTION_POINTERS *>(info_)->ExceptionRecord;
if( is_cpp_exception(record) )
{
if( !record->ExceptionInformation[2] )
record = *reinterpret_cast<EXCEPTION_RECORD * *>(reinterpret_cast<char *>(_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