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 | |
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 | |
300 | namespace |
301 | boost |
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 | |
335 | namespace |
336 | boost |
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 | |