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__anon1ede17050111::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 ()__anon1ede17050111::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() throw()
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