1 //Copyright (c) 2006-2009 Emil Dotchevski and Reverge Studios, Inc. 2 3 //Distributed under the Boost Software License, Version 1.0. (See accompanying 4 //file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 6 //This MSVC-specific cpp file implements non-intrusive cloning of exception objects. 7 //Based on an exception_ptr implementation by Anthony Williams. 8 9 #ifdef BOOST_NO_EXCEPTIONS 10 #error This file requires exception handling to be enabled. 11 #endif 12 13 #include <boost/exception/detail/clone_current_exception.hpp> 14 15 #if defined(BOOST_ENABLE_NON_INTRUSIVE_EXCEPTION_PTR) && defined(_MSC_VER) && defined(_M_IX86) && !defined(_M_X64) 16 17 //Non-intrusive cloning support implemented below, only for MSVC versions mentioned above. 18 //Thanks Anthony Williams! 19 20 #include <boost/exception/exception.hpp> 21 #include <boost/shared_ptr.hpp> 22 #ifndef BOOST_NO_RTTI 23 #include <typeinfo> 24 #endif 25 #include <windows.h> 26 #include <malloc.h> 27 28 namespace 29 { 30 unsigned const exception_maximum_parameters=15; 31 unsigned const exception_noncontinuable=1; 32 33 #if _MSC_VER==1310 34 int const exception_info_offset=0x74; 35 #elif (_MSC_VER==1400 || _MSC_VER==1500) 36 int const exception_info_offset=0x80; 37 #else 38 int const exception_info_offset=-1; 39 #endif 40 41 struct 42 exception_record 43 { 44 unsigned long ExceptionCode; 45 unsigned long ExceptionFlags; 46 exception_record * ExceptionRecord; 47 void * ExceptionAddress; 48 unsigned long NumberParameters; 49 ULONG_PTR ExceptionInformation[exception_maximum_parameters]; 50 }; 51 52 struct 53 exception_pointers 54 { 55 exception_record * ExceptionRecord; 56 void * ContextRecord; 57 }; 58 59 unsigned const cpp_exception_code=0xE06D7363; 60 unsigned const cpp_exception_magic_flag=0x19930520; 61 unsigned const cpp_exception_parameter_count=3; 62 63 struct 64 dummy_exception_type 65 { 66 }; 67 68 typedef int(dummy_exception_type::*normal_copy_constructor_ptr)(void * src); 69 typedef int(dummy_exception_type::*copy_constructor_with_virtual_base_ptr)(void * src,void * dst); 70 typedef void (dummy_exception_type::*destructor_ptr)(); 71 72 union 73 cpp_copy_constructor 74 { 75 normal_copy_constructor_ptr normal_copy_constructor; 76 copy_constructor_with_virtual_base_ptr copy_constructor_with_virtual_base; 77 }; 78 79 enum 80 cpp_type_flags 81 { 82 class_is_simple_type=1, 83 class_has_virtual_base=4 84 }; 85 86 struct 87 cpp_type_info 88 { 89 unsigned flags; 90 #ifndef BOOST_NO_RTTI 91 void const * type_info; 92 #else 93 std::type_info * type_info; 94 #endif 95 int this_offset; 96 int vbase_descr; 97 int vbase_offset; 98 unsigned long size; 99 cpp_copy_constructor copy_constructor; 100 }; 101 102 struct 103 cpp_type_info_table 104 { 105 unsigned count; 106 const cpp_type_info * info[1]; 107 }; 108 109 struct 110 cpp_exception_type 111 { 112 unsigned flags; 113 destructor_ptr destructor; 114 void(*custom_handler)(); 115 cpp_type_info_table const * type_info_table; 116 }; 117 118 struct 119 exception_object_deleter 120 { 121 cpp_exception_type const & et_; 122 exception_object_deleter__anon7820757a0111::exception_object_deleter123 exception_object_deleter( cpp_exception_type const & et ): 124 et_(et) 125 { 126 } 127 128 void operator ()__anon7820757a0111::exception_object_deleter129 operator()( void * obj ) 130 { 131 BOOST_ASSERT(obj!=0); 132 dummy_exception_type * dummy_exception_ptr=reinterpret_cast<dummy_exception_type *>(obj); 133 (dummy_exception_ptr->*(et_.destructor))(); 134 free(obj); 135 } 136 }; 137 138 cpp_type_info const & get_cpp_type_info(cpp_exception_type const & et)139 get_cpp_type_info( cpp_exception_type const & et ) 140 { 141 cpp_type_info const * ti = et.type_info_table->info[0]; 142 BOOST_ASSERT(ti!=0); 143 return *ti; 144 } 145 146 void copy_msvc_exception(void * dst,void * src,cpp_type_info const & ti)147 copy_msvc_exception( void * dst, void * src, cpp_type_info const & ti ) 148 { 149 if( !(ti.flags & class_is_simple_type) && ti.copy_constructor.normal_copy_constructor ) 150 { 151 dummy_exception_type * dummy_exception_ptr = reinterpret_cast<dummy_exception_type *>(dst); 152 if( ti.flags & class_has_virtual_base ) 153 (dummy_exception_ptr->*(ti.copy_constructor.copy_constructor_with_virtual_base))(src,dst); 154 else 155 (dummy_exception_ptr->*(ti.copy_constructor.normal_copy_constructor))(src); 156 } 157 else 158 memmove(dst,src,ti.size); 159 } 160 161 boost::shared_ptr<void> clone_msvc_exception(void * src,cpp_exception_type const & et)162 clone_msvc_exception( void * src, cpp_exception_type const & et ) 163 { 164 assert(src!=0); 165 cpp_type_info const & ti=get_cpp_type_info(et); 166 if( void * dst = malloc(ti.size) ) 167 { 168 try 169 { 170 copy_msvc_exception(dst,src,ti); 171 } 172 catch( 173 ... ) 174 { 175 free(dst); 176 throw; 177 } 178 return boost::shared_ptr<void>(dst,exception_object_deleter(et)); 179 } 180 else 181 throw std::bad_alloc(); 182 } 183 184 class 185 cloned_exception: 186 public boost::exception_detail::clone_base 187 { 188 cloned_exception( cloned_exception const & ); 189 cloned_exception & operator=( cloned_exception const & ); 190 191 cpp_exception_type const & et_; 192 boost::shared_ptr<void> exc_; 193 194 public: 195 cloned_exception(void * exc,cpp_exception_type const & et)196 cloned_exception( void * exc, cpp_exception_type const & et ): 197 et_(et), 198 exc_(clone_msvc_exception(exc,et_)) 199 { 200 } 201 ~cloned_exception()202 ~cloned_exception() throw() 203 { 204 } 205 206 boost::exception_detail::clone_base const * clone() const207 clone() const 208 { 209 return new cloned_exception(exc_.get(),et_); 210 } 211 212 void rethrow() const213 rethrow() const 214 { 215 cpp_type_info const & ti=get_cpp_type_info(et_); 216 void * dst = _alloca(ti.size); 217 copy_msvc_exception(dst,exc_.get(),ti); 218 ULONG_PTR args[cpp_exception_parameter_count]; 219 args[0]=cpp_exception_magic_flag; 220 args[1]=reinterpret_cast<ULONG_PTR>(dst); 221 args[2]=reinterpret_cast<ULONG_PTR>(&et_); 222 RaiseException(cpp_exception_code,EXCEPTION_NONCONTINUABLE,cpp_exception_parameter_count,args); 223 } 224 }; 225 226 bool is_cpp_exception(EXCEPTION_RECORD const * record)227 is_cpp_exception( EXCEPTION_RECORD const * record ) 228 { 229 return record && 230 (record->ExceptionCode==cpp_exception_code) && 231 (record->NumberParameters==cpp_exception_parameter_count) && 232 (record->ExceptionInformation[0]==cpp_exception_magic_flag); 233 } 234 235 unsigned long exception_cloning_filter(int & result,boost::exception_detail::clone_base const * & ptr,void * info_)236 exception_cloning_filter( int & result, boost::exception_detail::clone_base const * & ptr, void * info_ ) 237 { 238 BOOST_ASSERT(exception_info_offset>=0); 239 BOOST_ASSERT(info_!=0); 240 EXCEPTION_POINTERS * info=reinterpret_cast<EXCEPTION_POINTERS *>(info_); 241 EXCEPTION_RECORD * record=info->ExceptionRecord; 242 if( is_cpp_exception(record) ) 243 { 244 if( !record->ExceptionInformation[2] ) 245 record = *reinterpret_cast<EXCEPTION_RECORD * *>(reinterpret_cast<char *>(_errno())+exception_info_offset); 246 if( is_cpp_exception(record) && record->ExceptionInformation[2] ) 247 try 248 { 249 ptr = new cloned_exception( 250 reinterpret_cast<void *>(record->ExceptionInformation[1]), 251 *reinterpret_cast<cpp_exception_type const *>(record->ExceptionInformation[2])); 252 result = boost::exception_detail::clone_current_exception_result::success; 253 } 254 catch( 255 std::bad_alloc & ) 256 { 257 result = boost::exception_detail::clone_current_exception_result::bad_alloc; 258 } 259 catch( 260 ... ) 261 { 262 result = boost::exception_detail::clone_current_exception_result::bad_exception; 263 } 264 } 265 return EXCEPTION_EXECUTE_HANDLER; 266 } 267 } 268 269 namespace 270 boost 271 { 272 namespace 273 exception_detail 274 { 275 int clone_current_exception_non_intrusive(clone_base const * & cloned)276 clone_current_exception_non_intrusive( clone_base const * & cloned ) 277 { 278 BOOST_ASSERT(!cloned); 279 int result = clone_current_exception_result::not_supported; 280 if( exception_info_offset>=0 ) 281 { 282 clone_base const * ptr=0; 283 __try 284 { 285 throw; 286 } 287 __except(exception_cloning_filter(result,ptr,GetExceptionInformation())) 288 { 289 } 290 if( result==clone_current_exception_result::success ) 291 cloned=ptr; 292 } 293 BOOST_ASSERT(result!=clone_current_exception_result::success || cloned); 294 return result; 295 } 296 } 297 } 298 299 #else 300 301 //On all other compilers, return clone_current_exception_result::not_supported. 302 //On such platforms, only the intrusive enable_current_exception() cloning will work. 303 304 #include <boost/config.hpp> 305 306 namespace 307 boost 308 { 309 namespace 310 exception_detail 311 { 312 int clone_current_exception_non_intrusive(clone_base const * &)313 clone_current_exception_non_intrusive( clone_base const * & ) 314 { 315 return clone_current_exception_result::not_supported; 316 } 317 } 318 } 319 320 #endif 321