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
27namespace
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
136 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
143 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 &
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
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>
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:
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
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
233 ~cloned_exception() BOOST_NOEXCEPT_OR_NOTHROW
234 {
235 }
236
237 boost::exception_detail::clone_base const *
238 clone() const
239 {
240 return new cloned_exception(exc_.get(),et_,image_base_);
241 }
242
243 void
244 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
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
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
300namespace
301boost
302 {
303 namespace
304 exception_detail
305 {
306 int
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
335namespace
336boost
337 {
338 namespace
339 exception_detail
340 {
341 int
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

source code of boost/libs/exception/src/clone_current_exception_non_intrusive.cpp