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