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/config.hpp> 14 #include <boost/exception/detail/clone_current_exception.hpp> 15 16 #if defined(BOOST_ENABLE_NON_INTRUSIVE_EXCEPTION_PTR) && defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) 17 18 //Non-intrusive cloning support implemented below, only for MSVC versions mentioned above. 19 //Thanks Anthony Williams! 20 //Thanks to Martin Weiss for implementing 64-bit support! 21 22 #include <boost/exception/exception.hpp> 23 #include <boost/shared_ptr.hpp> 24 #include <windows.h> 25 #include <malloc.h> 26 27 namespace 28 { 29 unsigned const exception_maximum_parameters=15; 30 unsigned const exception_noncontinuable=1; 31 32 #if _MSC_VER==1310 33 int const exception_info_offset=0x74; 34 #elif ((_MSC_VER==1400 || _MSC_VER==1500) && !defined _M_X64) 35 int const exception_info_offset=0x80; 36 #elif ((_MSC_VER==1400 || _MSC_VER==1500) && defined _M_X64) 37 int const exception_info_offset=0xE0; 38 #else 39 int const exception_info_offset=-1; 40 #endif 41 42 struct 43 exception_record 44 { 45 unsigned long ExceptionCode; 46 unsigned long ExceptionFlags; 47 exception_record * ExceptionRecord; 48 void * ExceptionAddress; 49 unsigned long NumberParameters; 50 ULONG_PTR ExceptionInformation[exception_maximum_parameters]; 51 }; 52 53 struct 54 exception_pointers 55 { 56 exception_record * ExceptionRecord; 57 void * ContextRecord; 58 }; 59 60 unsigned const cpp_exception_code=0xE06D7363; 61 unsigned const cpp_exception_magic_flag=0x19930520; 62 #ifdef _M_X64 63 unsigned const cpp_exception_parameter_count=4; 64 #else 65 unsigned const cpp_exception_parameter_count=3; 66 #endif 67 68 struct 69 dummy_exception_type 70 { 71 }; 72 73 typedef int(dummy_exception_type::*normal_copy_constructor_ptr)(void * src); 74 typedef int(dummy_exception_type::*copy_constructor_with_virtual_base_ptr)(void * src,void * dst); 75 typedef void (dummy_exception_type::*destructor_ptr)(); 76 77 union 78 cpp_copy_constructor 79 { 80 void * address; 81 normal_copy_constructor_ptr normal_copy_constructor; 82 copy_constructor_with_virtual_base_ptr copy_constructor_with_virtual_base; 83 }; 84 85 union 86 cpp_destructor 87 { 88 void * address; 89 destructor_ptr destructor; 90 }; 91 92 enum 93 cpp_type_flags 94 { 95 class_is_simple_type=1, 96 class_has_virtual_base=4 97 }; 98 99 // ATTENTION: On x86 fields such as type_info and copy_constructor are really pointers 100 // but on 64bit these are 32bit offsets from HINSTANCE. Hints on the 64bit handling from 101 // http://blogs.msdn.com/b/oldnewthing/archive/2010/07/30/10044061.aspx . 102 struct 103 cpp_type_info 104 { 105 unsigned flags; 106 int type_info; 107 int this_offset; 108 int vbase_descr; 109 int vbase_offset; 110 unsigned long size; 111 int copy_constructor; 112 }; 113 114 struct 115 cpp_type_info_table 116 { 117 unsigned count; 118 int info; 119 }; 120 121 struct 122 cpp_exception_type 123 { 124 unsigned flags; 125 int destructor; 126 int custom_handler; 127 int type_info_table; 128 }; 129 130 struct 131 exception_object_deleter 132 { 133 cpp_exception_type const & et_; 134 size_t image_base_; 135 exception_object_deleter__anon3a5a964a0111::exception_object_deleter136 exception_object_deleter( cpp_exception_type const & et, size_t image_base ): 137 et_(et), 138 image_base_(image_base) 139 { 140 } 141 142 void operator ()__anon3a5a964a0111::exception_object_deleter143 operator()( void * obj ) 144 { 145 BOOST_ASSERT(obj!=0); 146 dummy_exception_type* dummy_exception_ptr = static_cast<dummy_exception_type *>(obj); 147 if( et_.destructor ) 148 { 149 cpp_destructor destructor; 150 destructor.address = reinterpret_cast<void *>(et_.destructor + image_base_); 151 (dummy_exception_ptr->*(destructor.destructor))(); 152 } 153 free(obj); 154 } 155 }; 156 157 cpp_type_info const & get_cpp_type_info(cpp_exception_type const & et,size_t image_base)158 get_cpp_type_info( cpp_exception_type const & et, size_t image_base ) 159 { 160 cpp_type_info_table * const typearray = reinterpret_cast<cpp_type_info_table * const>(et.type_info_table + image_base); 161 cpp_type_info * const ti = reinterpret_cast<cpp_type_info * const>(typearray->info + image_base); 162 BOOST_ASSERT(ti!=0); 163 return *ti; 164 } 165 166 void copy_msvc_exception(void * dst,void * src,cpp_type_info const & ti,size_t image_base)167 copy_msvc_exception( void * dst, void * src, cpp_type_info const & ti, size_t image_base ) 168 { 169 cpp_copy_constructor copy_constructor; 170 copy_constructor.address = reinterpret_cast<void *>(ti.copy_constructor + image_base); 171 172 if( !(ti.flags & class_is_simple_type) && copy_constructor.normal_copy_constructor ) 173 { 174 dummy_exception_type * dummy_exception_ptr = static_cast<dummy_exception_type *>(dst); 175 if( ti.flags & class_has_virtual_base ) 176 (dummy_exception_ptr->*(copy_constructor.copy_constructor_with_virtual_base))(src,dst); 177 else 178 (dummy_exception_ptr->*(copy_constructor.normal_copy_constructor))(src); 179 } 180 else 181 memmove(dst,src,ti.size); 182 } 183 184 boost::shared_ptr<void> clone_msvc_exception(void * src,cpp_exception_type const & et,size_t image_base)185 clone_msvc_exception( void * src, cpp_exception_type const & et, size_t image_base ) 186 { 187 BOOST_ASSERT(src!=0); 188 cpp_type_info const & ti=get_cpp_type_info(et,image_base); 189 if( void * dst = malloc(ti.size) ) 190 { 191 try 192 { 193 copy_msvc_exception(dst,src,ti,image_base); 194 } 195 catch( 196 ... ) 197 { 198 free(dst); 199 throw; 200 } 201 return boost::shared_ptr<void>(dst,exception_object_deleter(et,image_base)); 202 } 203 else 204 throw std::bad_alloc(); 205 } 206 207 class 208 cloned_exception: 209 public boost::exception_detail::clone_base 210 { 211 cloned_exception( cloned_exception const & ); 212 cloned_exception & operator=( cloned_exception const & ); 213 214 cpp_exception_type const & et_; 215 size_t image_base_; 216 boost::shared_ptr<void> exc_; 217 218 public: cloned_exception(EXCEPTION_RECORD const * record)219 cloned_exception( EXCEPTION_RECORD const * record ): 220 et_(*reinterpret_cast<cpp_exception_type const *>(record->ExceptionInformation[2])), 221 image_base_((cpp_exception_parameter_count==4) ? record->ExceptionInformation[3] : 0), 222 exc_(clone_msvc_exception(reinterpret_cast<void *>(record->ExceptionInformation[1]),et_,image_base_)) 223 { 224 } 225 cloned_exception(void * exc,cpp_exception_type const & et,size_t image_base)226 cloned_exception( void * exc, cpp_exception_type const & et, size_t image_base ): 227 et_(et), 228 image_base_(image_base), 229 exc_(clone_msvc_exception(exc,et_,image_base)) 230 { 231 } 232 ~cloned_exception()233 ~cloned_exception() BOOST_NOEXCEPT_OR_NOTHROW 234 { 235 } 236 237 boost::exception_detail::clone_base const * clone() const238 clone() const 239 { 240 return new cloned_exception(exc_.get(),et_,image_base_); 241 } 242 243 void rethrow() const244 rethrow() const 245 { 246 cpp_type_info const & ti=get_cpp_type_info(et_,image_base_); 247 void * dst = _alloca(ti.size); 248 copy_msvc_exception(dst,exc_.get(),ti,image_base_); 249 ULONG_PTR args[cpp_exception_parameter_count]; 250 args[0]=cpp_exception_magic_flag; 251 args[1]=reinterpret_cast<ULONG_PTR>(dst); 252 args[2]=reinterpret_cast<ULONG_PTR>(&et_); 253 if (cpp_exception_parameter_count==4) 254 args[3]=image_base_; 255 256 RaiseException(cpp_exception_code,EXCEPTION_NONCONTINUABLE,cpp_exception_parameter_count,args); 257 } 258 }; 259 260 bool is_cpp_exception(EXCEPTION_RECORD const * record)261 is_cpp_exception( EXCEPTION_RECORD const * record ) 262 { 263 return record && 264 (record->ExceptionCode==cpp_exception_code) && 265 (record->NumberParameters==cpp_exception_parameter_count) && 266 (record->ExceptionInformation[0]==cpp_exception_magic_flag); 267 } 268 269 unsigned long exception_cloning_filter(int & result,boost::exception_detail::clone_base const * & ptr,void * info_)270 exception_cloning_filter( int & result, boost::exception_detail::clone_base const * & ptr, void * info_ ) 271 { 272 BOOST_ASSERT(exception_info_offset>=0); 273 BOOST_ASSERT(info_!=0); 274 EXCEPTION_RECORD* record = static_cast<EXCEPTION_POINTERS *>(info_)->ExceptionRecord; 275 if( is_cpp_exception(record) ) 276 { 277 if( !record->ExceptionInformation[2] ) 278 record = *reinterpret_cast<EXCEPTION_RECORD * *>(reinterpret_cast<char *>(_errno())+exception_info_offset); 279 if( is_cpp_exception(record) && record->ExceptionInformation[2] ) 280 try 281 { 282 ptr = new cloned_exception(record); 283 result = boost::exception_detail::clone_current_exception_result::success; 284 } 285 catch( 286 std::bad_alloc & ) 287 { 288 result = boost::exception_detail::clone_current_exception_result::bad_alloc; 289 } 290 catch( 291 ... ) 292 { 293 result = boost::exception_detail::clone_current_exception_result::bad_exception; 294 } 295 } 296 return EXCEPTION_EXECUTE_HANDLER; 297 } 298 } 299 300 namespace 301 boost 302 { 303 namespace 304 exception_detail 305 { 306 int clone_current_exception_non_intrusive(clone_base const * & cloned)307 clone_current_exception_non_intrusive( clone_base const * & cloned ) 308 { 309 BOOST_ASSERT(!cloned); 310 int result = clone_current_exception_result::not_supported; 311 if( exception_info_offset>=0 ) 312 { 313 clone_base const * ptr=0; 314 __try 315 { 316 throw; 317 } 318 __except(exception_cloning_filter(result,ptr,GetExceptionInformation())) 319 { 320 } 321 if( result==clone_current_exception_result::success ) 322 cloned=ptr; 323 } 324 BOOST_ASSERT(result!=clone_current_exception_result::success || cloned); 325 return result; 326 } 327 } 328 } 329 330 #else 331 332 //On all other compilers, return clone_current_exception_result::not_supported. 333 //On such platforms, only the intrusive enable_current_exception() cloning will work. 334 335 namespace 336 boost 337 { 338 namespace 339 exception_detail 340 { 341 int clone_current_exception_non_intrusive(clone_base const * &)342 clone_current_exception_non_intrusive( clone_base const * & ) 343 { 344 return clone_current_exception_result::not_supported; 345 } 346 } 347 } 348 349 #endif 350