2 * msvcrt C++ exception handling
4 * Copyright 2002 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 * A good reference is the article "How a C++ compiler implements
22 * exception handling" by Vishal Kochhar, available on
23 * www.thecodeproject.com.
27 #include "wine/port.h"
31 #include "wine/exception.h"
32 #include "msvcrt/excpt.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(seh
);
37 #define CXX_FRAME_MAGIC 0x19930520
38 #define CXX_EXCEPTION 0xe06d7363
40 typedef struct __type_info
47 /* the exception frame used by CxxFrameHandler */
48 typedef struct __cxx_exception_frame
50 EXCEPTION_FRAME
*prev
;
51 void (*handler
)(PEXCEPTION_RECORD
, PEXCEPTION_FRAME
, PCONTEXT
, PEXCEPTION_RECORD
);
54 } cxx_exception_frame
;
56 /* info about a single catch {} block */
57 typedef struct __catchblock_info
59 UINT flags
; /* flags (see below) */
60 type_info
*type_info
; /* C++ type caught by this block */
61 int offset
; /* stack offset to copy exception object to */
62 void (*handler
)(); /* catch block handler code */
64 #define TYPE_FLAG_CONST 1
65 #define TYPE_FLAG_VOLATILE 2
66 #define TYPE_FLAG_REFERENCE 8
68 /* info about a single try {} block */
69 typedef struct __tryblock_info
71 int start_level
; /* start trylevel of that block */
72 int end_level
; /* end trylevel of that block */
73 int catch_level
; /* initial trylevel of the catch block */
74 int catchblock_count
; /* count of catch blocks in array */
75 catchblock_info
*catchblock
; /* array of catch blocks */
78 /* info about the unwind handler for a given trylevel */
79 typedef struct __unwind_info
81 int prev
; /* prev trylevel unwind handler, to run after this one */
82 void (*handler
)(); /* unwind handler */
85 /* descriptor of all try blocks of a given function */
86 typedef struct __cxx_function_descr
88 UINT magic
; /* must be CXX_FRAME_MAGIC */
89 UINT unwind_count
; /* number of unwind handlers */
90 unwind_info
*unwind_table
; /* array of unwind handlers */
91 UINT tryblock_count
; /* number of try blocks */
92 tryblock_info
*tryblock
; /* array of try blocks */
96 /* complete information about a C++ type */
97 typedef struct __cxx_type_info
99 UINT flags
; /* flags (see CLASS_* flags below) */
100 type_info
*type_info
; /* C++ type info */
101 int this_offset
; /* offset of base class this pointer from start of object */
102 int vbase_descr
; /* offset of virtual base class descriptor */
103 int vbase_offset
; /* offset of this pointer offset in virtual base class descriptor */
104 size_t size
; /* object size */
105 void (*copy_ctor
)(); /* copy constructor */
107 #define CLASS_IS_SIMPLE_TYPE 1
108 #define CLASS_HAS_VIRTUAL_BASE_CLASS 4
110 /* table of C++ types that apply for a given object */
111 typedef struct __cxx_type_info_table
113 UINT count
; /* number of types */
114 cxx_type_info
*info
[1]; /* array of types */
115 } cxx_type_info_table
;
117 typedef DWORD (*cxx_exc_custom_handler
)( PEXCEPTION_RECORD
, cxx_exception_frame
*,
118 PCONTEXT
, struct __EXCEPTION_FRAME
**,
119 cxx_function_descr
*, DWORD unknown1
,
120 DWORD unknown2
, DWORD unknown3
);
122 /* type information for an exception object */
123 typedef struct __cxx_exception_type
125 UINT flags
; /* TYPE_FLAG flags */
126 void (*destructor
)(); /* exception object destructor */
127 cxx_exc_custom_handler custom_handler
; /* custom handler for this exception */
128 cxx_type_info_table
*type_info_table
; /* list of types for this exception object */
129 } cxx_exception_type
;
132 #ifdef __i386__ /* CxxFrameHandler is not supported on non-i386 */
134 /* call a function with a given ebp */
135 inline static void *call_ebp_func( void *func
, void *ebp
)
138 __asm__
__volatile__ ("pushl %%ebp; movl %2,%%ebp; call *%%eax;; popl %%ebp" \
139 : "=a" (ret
) : "0" (func
), "g" (ebp
));
143 /* call a copy constructor */
144 inline static void call_copy_ctor( void *func
, void *this, void *src
, int has_vbase
)
146 TRACE( "calling copy ctor %p object %p src %p\n", func
, this, src
);
148 /* in that case copy ctor takes an extra bool indicating whether to copy the base class */
149 __asm__
__volatile__("pushl $1; pushl %2; call *%0"
150 : : "r" (func
), "c" (this), "g" (src
) );
152 __asm__
__volatile__("pushl %2; call *%0"
153 : : "r" (func
), "c" (this), "g" (src
) );
156 /* call the destructor of the exception object */
157 inline static void call_dtor( void *func
, void *object
)
159 __asm__
__volatile__("call *%0" : : "r" (func
), "c" (object
) );
163 static void dump_type( cxx_type_info
*type
)
165 DPRINTF( "flags %x type %p", type
->flags
, type
->type_info
);
166 if (type
->type_info
) DPRINTF( " (%p %s)", type
->type_info
->data
, type
->type_info
->name
);
167 DPRINTF( " offset %d vbase %d,%d size %d copy ctor %p\n", type
->this_offset
,
168 type
->vbase_descr
, type
->vbase_offset
, type
->size
, type
->copy_ctor
);
171 static void dump_exception_type( cxx_exception_type
*type
)
175 DPRINTF( "exception type:\n" );
176 DPRINTF( "flags %x destr %p handler %p type info %p\n",
177 type
->flags
, type
->destructor
, type
->custom_handler
, type
->type_info_table
);
178 for (i
= 0; i
< type
->type_info_table
->count
; i
++)
180 DPRINTF( " %d: ", i
);
181 dump_type( type
->type_info_table
->info
[i
] );
185 static void dump_function_descr( cxx_function_descr
*descr
, cxx_exception_type
*info
)
189 DPRINTF( "function descr:\n" );
190 DPRINTF( "magic %x\n", descr
->magic
);
191 DPRINTF( "unwind table: %p %d\n", descr
->unwind_table
, descr
->unwind_count
);
192 for (i
= 0; i
< descr
->unwind_count
; i
++)
194 DPRINTF( " %d: prev %d func %p\n", i
,
195 descr
->unwind_table
[i
].prev
, descr
->unwind_table
[i
].handler
);
197 DPRINTF( "try table: %p %d\n", descr
->tryblock
, descr
->tryblock_count
);
198 for (i
= 0; i
< descr
->tryblock_count
; i
++)
200 DPRINTF( " %d: start %d end %d catchlevel %d catch %p %d\n", i
,
201 descr
->tryblock
[i
].start_level
, descr
->tryblock
[i
].end_level
,
202 descr
->tryblock
[i
].catch_level
, descr
->tryblock
[i
].catchblock
,
203 descr
->tryblock
[i
].catchblock_count
);
204 for (j
= 0; j
< descr
->tryblock
[i
].catchblock_count
; j
++)
206 catchblock_info
*ptr
= &descr
->tryblock
[i
].catchblock
[j
];
207 DPRINTF( " %d: flags %x offset %d handler %p type %p",
208 j
, ptr
->flags
, ptr
->offset
, ptr
->handler
, ptr
->type_info
);
209 if (ptr
->type_info
) DPRINTF( " (%p %s)", ptr
->type_info
->data
, ptr
->type_info
->name
);
215 /* compute the this pointer for a base class of a given type */
216 static void *get_this_pointer( cxx_type_info
*type
, void *object
)
221 if (!object
) return NULL
;
222 this_ptr
= (char *)object
+ type
->this_offset
;
223 if (type
->vbase_descr
>= 0)
225 /* move this ptr to vbase descriptor */
226 this_ptr
= (char *)this_ptr
+ type
->vbase_descr
;
227 /* and fetch additional offset from vbase descriptor */
228 offset_ptr
= (int *)(*(char **)this_ptr
+ type
->vbase_offset
);
229 this_ptr
= (char *)this_ptr
+ *offset_ptr
;
234 /* check if the exception type is caught by a given catch block, and return the type that matched */
235 static cxx_type_info
*find_caught_type( cxx_exception_type
*exc_type
, catchblock_info
*catchblock
)
239 for (i
= 0; i
< exc_type
->type_info_table
->count
; i
++)
241 cxx_type_info
*type
= exc_type
->type_info_table
->info
[i
];
243 if (!catchblock
->type_info
) return type
; /* catch(...) matches any type */
244 if (catchblock
->type_info
!= type
->type_info
)
246 if (strcmp( catchblock
->type_info
->name
, type
->type_info
->name
)) continue;
248 /* type is the same, now check the flags */
249 if ((exc_type
->flags
& TYPE_FLAG_CONST
) &&
250 !(catchblock
->flags
& TYPE_FLAG_CONST
)) continue;
251 if ((exc_type
->flags
& TYPE_FLAG_VOLATILE
) &&
252 !(catchblock
->flags
& TYPE_FLAG_VOLATILE
)) continue;
253 return type
; /* it matched */
259 /* copy the exception object where the catch block wants it */
260 static void copy_exception( void *object
, cxx_exception_frame
*frame
,
261 catchblock_info
*catchblock
, cxx_type_info
*type
)
265 if (!catchblock
->type_info
|| !catchblock
->type_info
->name
[0]) return;
266 if (!catchblock
->offset
) return;
267 dest_ptr
= (void **)((char *)&frame
->ebp
+ catchblock
->offset
);
269 if (catchblock
->flags
& TYPE_FLAG_REFERENCE
)
271 *dest_ptr
= get_this_pointer( type
, object
);
273 else if (type
->flags
& CLASS_IS_SIMPLE_TYPE
)
275 memmove( dest_ptr
, object
, type
->size
);
276 /* if it is a pointer, adjust it */
277 if (type
->size
== sizeof(void *)) *dest_ptr
= get_this_pointer( type
, *dest_ptr
);
279 else /* copy the object */
282 call_copy_ctor( type
->copy_ctor
, dest_ptr
, get_this_pointer(type
,object
),
283 (type
->flags
& CLASS_HAS_VIRTUAL_BASE_CLASS
) );
285 memmove( dest_ptr
, get_this_pointer(type
,object
), type
->size
);
289 /* unwind the local function up to a given trylevel */
290 static void cxx_local_unwind( cxx_exception_frame
* frame
, cxx_function_descr
*descr
, int last_level
)
293 int trylevel
= frame
->trylevel
;
295 while (trylevel
!= last_level
)
297 if (trylevel
< 0 || trylevel
>= descr
->unwind_count
)
299 ERR( "invalid trylevel %d\n", trylevel
);
302 handler
= descr
->unwind_table
[trylevel
].handler
;
305 TRACE( "calling unwind handler %p trylevel %d last %d ebp %p\n",
306 handler
, trylevel
, last_level
, &frame
->ebp
);
307 call_ebp_func( handler
, &frame
->ebp
);
309 trylevel
= descr
->unwind_table
[trylevel
].prev
;
311 frame
->trylevel
= last_level
;
314 /* find and call the appropriate catch block for an exception */
315 /* returns the address to continue execution to after the catch block was called */
316 inline static void *call_catch_block( PEXCEPTION_RECORD rec
, cxx_exception_frame
*frame
,
317 EXCEPTION_FRAME
*unwind_frame
,
318 cxx_function_descr
*descr
, int trylevel
,
319 cxx_exception_type
*info
)
322 void *addr
, *object
= (void *)rec
->ExceptionInformation
[1];
324 for (i
= 0; i
< descr
->tryblock_count
; i
++)
326 tryblock_info
*tryblock
= &descr
->tryblock
[i
];
328 if (trylevel
< tryblock
->start_level
) continue;
329 if (trylevel
> tryblock
->end_level
) continue;
331 /* got a try block */
332 for (j
= 0; j
< tryblock
->catchblock_count
; j
++)
334 catchblock_info
*catchblock
= &tryblock
->catchblock
[j
];
335 cxx_type_info
*type
= find_caught_type( info
, catchblock
);
338 TRACE( "matched type %p in tryblock %d catchblock %d\n", type
, i
, j
);
340 /* copy the exception to its destination on the stack */
341 copy_exception( object
, frame
, catchblock
, type
);
343 /* unwind the stack */
344 RtlUnwind( unwind_frame
, 0, rec
, 0 );
345 cxx_local_unwind( frame
, descr
, tryblock
->start_level
);
346 frame
->trylevel
= tryblock
->end_level
+ 1;
348 /* call the catch block */
349 TRACE( "calling catch block %p for type %p addr %p ebp %p\n",
350 catchblock
, type
, catchblock
->handler
, &frame
->ebp
);
351 addr
= call_ebp_func( catchblock
->handler
, &frame
->ebp
);
352 if (info
->destructor
) call_dtor( info
->destructor
, object
);
353 TRACE( "done, continuing at %p\n", addr
);
361 /*********************************************************************
364 * Implementation of __CxxFrameHandler.
366 static DWORD
cxx_frame_handler( PEXCEPTION_RECORD rec
, cxx_exception_frame
* frame
,
367 PCONTEXT exc_context
, EXCEPTION_FRAME
** dispatch
,
368 cxx_function_descr
*descr
, CONTEXT86
*context
)
370 cxx_exception_type
*exc_type
;
373 if (descr
->magic
!= CXX_FRAME_MAGIC
)
375 ERR( "invalid frame magic %x\n", descr
->magic
);
376 return ExceptionContinueSearch
;
378 if (rec
->ExceptionFlags
& (EH_UNWINDING
|EH_EXIT_UNWIND
))
380 if (descr
->unwind_count
) cxx_local_unwind( frame
, descr
, -1 );
381 return ExceptionContinueSearch
;
383 if (!descr
->tryblock_count
) return ExceptionContinueSearch
;
385 exc_type
= (cxx_exception_type
*)rec
->ExceptionInformation
[2];
386 if (rec
->ExceptionCode
!= CXX_EXCEPTION
) goto normal_handler
;
387 if (rec
->ExceptionInformation
[0] != CXX_FRAME_MAGIC
) goto normal_handler
;
388 if (exc_type
->custom_handler
)
389 return exc_type
->custom_handler( rec
, frame
, exc_context
, dispatch
, descr
, 0, 0, 0 );
394 TRACE("handling C++ exception rec %p frame %p trylevel %d descr %p\n",
395 rec
, frame
, frame
->trylevel
, descr
);
396 dump_exception_type( exc_type
);
397 dump_function_descr( descr
, exc_type
);
400 next_ip
= call_catch_block( rec
, frame
, (EXCEPTION_FRAME
*)frame
,
401 descr
, frame
->trylevel
, exc_type
);
403 if (!next_ip
) return ExceptionContinueSearch
;
404 context
->Eip
= (DWORD
)next_ip
;
405 context
->Ebp
= (DWORD
)&frame
->ebp
;
406 context
->Esp
= ((DWORD
*)frame
)[-1];
407 return ExceptionContinueExecution
;
411 /*********************************************************************
412 * __CxxFrameHandler (MSVCRT.@)
414 void __CxxFrameHandler( PEXCEPTION_RECORD rec
, EXCEPTION_FRAME
* frame
,
415 PCONTEXT exc_context
, EXCEPTION_FRAME
** dispatch
,
418 cxx_function_descr
*descr
= (cxx_function_descr
*)context
->Eax
;
419 context
->Eax
= cxx_frame_handler( rec
, (cxx_exception_frame
*)frame
,
420 exc_context
, dispatch
, descr
, context
);
423 #endif /* __i386__ */
425 /*********************************************************************
426 * _CxxThrowException (MSVCRT.@)
428 void _CxxThrowException( void *object
, cxx_exception_type
*type
)
432 args
[0] = CXX_FRAME_MAGIC
;
433 args
[1] = (DWORD
)object
;
434 args
[2] = (DWORD
)type
;
435 RaiseException( CXX_EXCEPTION
, EH_NONCONTINUABLE
, 3, args
);