source: trunk/src/msvcrt/cppexcept.c@ 9633

Last change on this file since 9633 was 9633, checked in by sandervl, 23 years ago

PF: Msvcrt Wine port with GCC

File size: 18.8 KB
Line 
1/*
2 * msvcrt C++ exception handling
3 *
4 * Copyright 2002 Alexandre Julliard
5 *
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.
10 *
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.
15 *
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
19 *
20 * NOTES
21 * A good reference is the article "How a C++ compiler implements
22 * exception handling" by Vishal Kochhar, available on
23 * www.thecodeproject.com.
24 */
25#ifdef __WIN32OS2__
26#include <emxheader.h>
27#include "winbase.h"
28#else
29#include "config.h"
30#endif
31
32#include "wine/port.h"
33
34#include "winternl.h"
35#include "msvcrt.h"
36#include "wine/exception.h"
37#include "excpt.h"
38#include "wine/debug.h"
39
40WINE_DEFAULT_DEBUG_CHANNEL(seh);
41
42#define CXX_FRAME_MAGIC 0x19930520
43#define CXX_EXCEPTION 0xe06d7363
44
45typedef struct __type_info
46{
47 void *vtable;
48 void *data;
49 char name[1];
50} type_info;
51
52/* the exception frame used by CxxFrameHandler */
53typedef struct __cxx_exception_frame
54{
55 EXCEPTION_FRAME frame; /* the standard exception frame */
56 int trylevel;
57 DWORD ebp;
58} cxx_exception_frame;
59
60/* info about a single catch {} block */
61typedef struct __catchblock_info
62{
63 UINT flags; /* flags (see below) */
64 type_info *type_info; /* C++ type caught by this block */
65 int offset; /* stack offset to copy exception object to */
66 void (*handler)(); /* catch block handler code */
67} catchblock_info;
68#define TYPE_FLAG_CONST 1
69#define TYPE_FLAG_VOLATILE 2
70#define TYPE_FLAG_REFERENCE 8
71
72/* info about a single try {} block */
73typedef struct __tryblock_info
74{
75 int start_level; /* start trylevel of that block */
76 int end_level; /* end trylevel of that block */
77 int catch_level; /* initial trylevel of the catch block */
78 int catchblock_count; /* count of catch blocks in array */
79 catchblock_info *catchblock; /* array of catch blocks */
80} tryblock_info;
81
82/* info about the unwind handler for a given trylevel */
83typedef struct __unwind_info
84{
85 int prev; /* prev trylevel unwind handler, to run after this one */
86 void (*handler)(); /* unwind handler */
87} unwind_info;
88
89/* descriptor of all try blocks of a given function */
90typedef struct __cxx_function_descr
91{
92 UINT magic; /* must be CXX_FRAME_MAGIC */
93 UINT unwind_count; /* number of unwind handlers */
94 unwind_info *unwind_table; /* array of unwind handlers */
95 UINT tryblock_count; /* number of try blocks */
96 tryblock_info *tryblock; /* array of try blocks */
97 UINT unknown[3];
98} cxx_function_descr;
99
100/* complete information about a C++ type */
101typedef struct __cxx_type_info
102{
103 UINT flags; /* flags (see CLASS_* flags below) */
104 type_info *type_info; /* C++ type info */
105 int this_offset; /* offset of base class this pointer from start of object */
106 int vbase_descr; /* offset of virtual base class descriptor */
107 int vbase_offset; /* offset of this pointer offset in virtual base class descriptor */
108 size_t size; /* object size */
109 void (*copy_ctor)(); /* copy constructor */
110} cxx_type_info;
111#define CLASS_IS_SIMPLE_TYPE 1
112#define CLASS_HAS_VIRTUAL_BASE_CLASS 4
113
114/* table of C++ types that apply for a given object */
115typedef struct __cxx_type_info_table
116{
117 UINT count; /* number of types */
118 cxx_type_info *info[1]; /* array of types */
119} cxx_type_info_table;
120
121typedef DWORD (*cxx_exc_custom_handler)( PEXCEPTION_RECORD, cxx_exception_frame*,
122 PCONTEXT, struct __EXCEPTION_FRAME**,
123 cxx_function_descr*, int nested_trylevel,
124 EXCEPTION_FRAME *nested_frame, DWORD unknown3 );
125
126/* type information for an exception object */
127typedef struct __cxx_exception_type
128{
129 UINT flags; /* TYPE_FLAG flags */
130 void (*destructor)(); /* exception object destructor */
131 cxx_exc_custom_handler custom_handler; /* custom handler for this exception */
132 cxx_type_info_table *type_info_table; /* list of types for this exception object */
133} cxx_exception_type;
134
135
136#ifdef __i386__ /* CxxFrameHandler is not supported on non-i386 */
137
138static DWORD cxx_frame_handler( PEXCEPTION_RECORD rec, cxx_exception_frame* frame,
139 PCONTEXT exc_context, EXCEPTION_FRAME** dispatch,
140 cxx_function_descr *descr, EXCEPTION_FRAME* nested_frame,
141 int nested_trylevel, CONTEXT86 *context );
142
143/* call a function with a given ebp */
144inline static void *call_ebp_func( void *func, void *ebp )
145{
146 void *ret;
147 __asm__ __volatile__ ("pushl %%ebp; movl %2,%%ebp; call *%%eax; popl %%ebp" \
148 : "=a" (ret) : "0" (func), "g" (ebp) : "ecx", "edx", "memory" );
149 return ret;
150}
151
152/* call a copy constructor */
153inline static void call_copy_ctor( void *func, void *this, void *src, int has_vbase )
154{
155 TRACE( "calling copy ctor %p object %p src %p\n", func, this, src );
156 if (has_vbase)
157 /* in that case copy ctor takes an extra bool indicating whether to copy the base class */
158 __asm__ __volatile__("pushl $1; pushl %2; call *%0"
159 : : "r" (func), "c" (this), "g" (src) : "eax", "edx", "memory" );
160 else
161 __asm__ __volatile__("pushl %2; call *%0"
162 : : "r" (func), "c" (this), "g" (src) : "eax", "edx", "memory" );
163}
164
165/* call the destructor of the exception object */
166inline static void call_dtor( void *func, void *object )
167{
168 __asm__ __volatile__("call *%0" : : "r" (func), "c" (object) : "eax", "edx", "memory" );
169}
170
171
172static void dump_type( cxx_type_info *type )
173{
174 DPRINTF( "flags %x type %p", type->flags, type->type_info );
175 if (type->type_info) DPRINTF( " (%p %s)", type->type_info->data, type->type_info->name );
176 DPRINTF( " offset %d vbase %d,%d size %d copy ctor %p\n", type->this_offset,
177 type->vbase_descr, type->vbase_offset, type->size, type->copy_ctor );
178}
179
180static void dump_exception_type( cxx_exception_type *type )
181{
182 int i;
183
184 DPRINTF( "exception type:\n" );
185 DPRINTF( "flags %x destr %p handler %p type info %p\n",
186 type->flags, type->destructor, type->custom_handler, type->type_info_table );
187 for (i = 0; i < type->type_info_table->count; i++)
188 {
189 DPRINTF( " %d: ", i );
190 dump_type( type->type_info_table->info[i] );
191 }
192}
193
194static void dump_function_descr( cxx_function_descr *descr, cxx_exception_type *info )
195{
196 int i, j;
197
198 DPRINTF( "function descr:\n" );
199 DPRINTF( "magic %x\n", descr->magic );
200 DPRINTF( "unwind table: %p %d\n", descr->unwind_table, descr->unwind_count );
201 for (i = 0; i < descr->unwind_count; i++)
202 {
203 DPRINTF( " %d: prev %d func %p\n", i,
204 descr->unwind_table[i].prev, descr->unwind_table[i].handler );
205 }
206 DPRINTF( "try table: %p %d\n", descr->tryblock, descr->tryblock_count );
207 for (i = 0; i < descr->tryblock_count; i++)
208 {
209 DPRINTF( " %d: start %d end %d catchlevel %d catch %p %d\n", i,
210 descr->tryblock[i].start_level, descr->tryblock[i].end_level,
211 descr->tryblock[i].catch_level, descr->tryblock[i].catchblock,
212 descr->tryblock[i].catchblock_count );
213 for (j = 0; j < descr->tryblock[i].catchblock_count; j++)
214 {
215 catchblock_info *ptr = &descr->tryblock[i].catchblock[j];
216 DPRINTF( " %d: flags %x offset %d handler %p type %p",
217 j, ptr->flags, ptr->offset, ptr->handler, ptr->type_info );
218 if (ptr->type_info) DPRINTF( " (%p %s)", ptr->type_info->data, ptr->type_info->name );
219 DPRINTF( "\n" );
220 }
221 }
222}
223
224/* compute the this pointer for a base class of a given type */
225static void *get_this_pointer( cxx_type_info *type, void *object )
226{
227 void *this_ptr;
228 int *offset_ptr;
229
230 if (!object) return NULL;
231 this_ptr = (char *)object + type->this_offset;
232 if (type->vbase_descr >= 0)
233 {
234 /* move this ptr to vbase descriptor */
235 this_ptr = (char *)this_ptr + type->vbase_descr;
236 /* and fetch additional offset from vbase descriptor */
237 offset_ptr = (int *)(*(char **)this_ptr + type->vbase_offset);
238 this_ptr = (char *)this_ptr + *offset_ptr;
239 }
240 return this_ptr;
241}
242
243/* check if the exception type is caught by a given catch block, and return the type that matched */
244static cxx_type_info *find_caught_type( cxx_exception_type *exc_type, catchblock_info *catchblock )
245{
246 UINT i;
247
248 for (i = 0; i < exc_type->type_info_table->count; i++)
249 {
250 cxx_type_info *type = exc_type->type_info_table->info[i];
251
252 if (!catchblock->type_info) return type; /* catch(...) matches any type */
253 if (catchblock->type_info != type->type_info)
254 {
255 if (strcmp( catchblock->type_info->name, type->type_info->name )) continue;
256 }
257 /* type is the same, now check the flags */
258 if ((exc_type->flags & TYPE_FLAG_CONST) &&
259 !(catchblock->flags & TYPE_FLAG_CONST)) continue;
260 if ((exc_type->flags & TYPE_FLAG_VOLATILE) &&
261 !(catchblock->flags & TYPE_FLAG_VOLATILE)) continue;
262 return type; /* it matched */
263 }
264 return NULL;
265}
266
267
268/* copy the exception object where the catch block wants it */
269static void copy_exception( void *object, cxx_exception_frame *frame,
270 catchblock_info *catchblock, cxx_type_info *type )
271{
272 void **dest_ptr;
273
274 if (!catchblock->type_info || !catchblock->type_info->name[0]) return;
275 if (!catchblock->offset) return;
276 dest_ptr = (void **)((char *)&frame->ebp + catchblock->offset);
277
278 if (catchblock->flags & TYPE_FLAG_REFERENCE)
279 {
280 *dest_ptr = get_this_pointer( type, object );
281 }
282 else if (type->flags & CLASS_IS_SIMPLE_TYPE)
283 {
284 memmove( dest_ptr, object, type->size );
285 /* if it is a pointer, adjust it */
286 if (type->size == sizeof(void *)) *dest_ptr = get_this_pointer( type, *dest_ptr );
287 }
288 else /* copy the object */
289 {
290 if (type->copy_ctor)
291 call_copy_ctor( type->copy_ctor, dest_ptr, get_this_pointer(type,object),
292 (type->flags & CLASS_HAS_VIRTUAL_BASE_CLASS) );
293 else
294 memmove( dest_ptr, get_this_pointer(type,object), type->size );
295 }
296}
297
298/* unwind the local function up to a given trylevel */
299static void cxx_local_unwind( cxx_exception_frame* frame, cxx_function_descr *descr, int last_level)
300{
301 void (*handler)();
302 int trylevel = frame->trylevel;
303
304 while (trylevel != last_level)
305 {
306 if (trylevel < 0 || trylevel >= descr->unwind_count)
307 {
308 ERR( "invalid trylevel %d\n", trylevel );
309 MSVCRT_terminate();
310 }
311 handler = descr->unwind_table[trylevel].handler;
312 if (handler)
313 {
314 TRACE( "calling unwind handler %p trylevel %d last %d ebp %p\n",
315 handler, trylevel, last_level, &frame->ebp );
316 call_ebp_func( handler, &frame->ebp );
317 }
318 trylevel = descr->unwind_table[trylevel].prev;
319 }
320 frame->trylevel = last_level;
321}
322
323/* exception frame for nested exceptions in catch block */
324struct catch_func_nested_frame
325{
326 EXCEPTION_FRAME frame; /* standard exception frame */
327 EXCEPTION_RECORD *prev_rec; /* previous record to restore in thread data */
328 cxx_exception_frame *cxx_frame; /* frame of parent exception */
329 cxx_function_descr *descr; /* descriptor of parent exception */
330 int trylevel; /* current try level */
331};
332
333/* handler for exceptions happening while calling a catch function */
334static DWORD catch_function_nested_handler( EXCEPTION_RECORD *rec, EXCEPTION_FRAME *frame,
335 CONTEXT *context, EXCEPTION_FRAME **dispatcher )
336{
337 struct catch_func_nested_frame *nested_frame = (struct catch_func_nested_frame *)frame;
338
339 if (rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
340 {
341 msvcrt_get_thread_data()->exc_record = nested_frame->prev_rec;
342 return ExceptionContinueSearch;
343 }
344 else
345 {
346 TRACE( "got nested exception in catch function\n" );
347 return cxx_frame_handler( rec, nested_frame->cxx_frame, context,
348 NULL, nested_frame->descr, &nested_frame->frame,
349 nested_frame->trylevel, context );
350 }
351}
352
353/* find and call the appropriate catch block for an exception */
354/* returns the address to continue execution to after the catch block was called */
355inline static void *call_catch_block( PEXCEPTION_RECORD rec, cxx_exception_frame *frame,
356 cxx_function_descr *descr, int nested_trylevel,
357 cxx_exception_type *info )
358{
359 int i, j;
360 void *addr, *object = (void *)rec->ExceptionInformation[1];
361 struct catch_func_nested_frame nested_frame;
362 int trylevel = frame->trylevel;
363 MSVCRT_thread_data *thread_data = msvcrt_get_thread_data();
364
365 for (i = 0; i < descr->tryblock_count; i++)
366 {
367 tryblock_info *tryblock = &descr->tryblock[i];
368
369 if (trylevel < tryblock->start_level) continue;
370 if (trylevel > tryblock->end_level) continue;
371
372 /* got a try block */
373 for (j = 0; j < tryblock->catchblock_count; j++)
374 {
375 catchblock_info *catchblock = &tryblock->catchblock[j];
376 cxx_type_info *type = find_caught_type( info, catchblock );
377 if (!type) continue;
378
379 TRACE( "matched type %p in tryblock %d catchblock %d\n", type, i, j );
380
381 /* copy the exception to its destination on the stack */
382 copy_exception( object, frame, catchblock, type );
383
384 /* unwind the stack */
385 RtlUnwind( frame, 0, rec, 0 );
386 cxx_local_unwind( frame, descr, tryblock->start_level );
387 frame->trylevel = tryblock->end_level + 1;
388
389 /* call the catch block */
390 TRACE( "calling catch block %p for type %p addr %p ebp %p\n",
391 catchblock, type, catchblock->handler, &frame->ebp );
392
393 /* setup an exception block for nested exceptions */
394
395 nested_frame.frame.Handler = catch_function_nested_handler;
396 nested_frame.prev_rec = thread_data->exc_record;
397 nested_frame.cxx_frame = frame;
398 nested_frame.descr = descr;
399 nested_frame.trylevel = nested_trylevel + 1;
400
401 __wine_push_frame( &nested_frame.frame );
402 thread_data->exc_record = rec;
403 addr = call_ebp_func( catchblock->handler, &frame->ebp );
404 thread_data->exc_record = nested_frame.prev_rec;
405 __wine_pop_frame( &nested_frame.frame );
406
407 if (info->destructor) call_dtor( info->destructor, object );
408 TRACE( "done, continuing at %p\n", addr );
409 return addr;
410 }
411 }
412 return NULL;
413}
414
415
416/*********************************************************************
417 * cxx_frame_handler
418 *
419 * Implementation of __CxxFrameHandler.
420 */
421static DWORD cxx_frame_handler( PEXCEPTION_RECORD rec, cxx_exception_frame* frame,
422 PCONTEXT exc_context, EXCEPTION_FRAME** dispatch,
423 cxx_function_descr *descr, EXCEPTION_FRAME* nested_frame,
424 int nested_trylevel, CONTEXT86 *context )
425{
426 cxx_exception_type *exc_type;
427 void *next_ip;
428
429 if (descr->magic != CXX_FRAME_MAGIC)
430 {
431 ERR( "invalid frame magic %x\n", descr->magic );
432 return ExceptionContinueSearch;
433 }
434 if (rec->ExceptionFlags & (EH_UNWINDING|EH_EXIT_UNWIND))
435 {
436 if (descr->unwind_count && !nested_trylevel) cxx_local_unwind( frame, descr, -1 );
437 return ExceptionContinueSearch;
438 }
439 if (!descr->tryblock_count) return ExceptionContinueSearch;
440
441 exc_type = (cxx_exception_type *)rec->ExceptionInformation[2];
442 if (rec->ExceptionCode == CXX_EXCEPTION &&
443 rec->ExceptionInformation[0] > CXX_FRAME_MAGIC &&
444 exc_type->custom_handler)
445 {
446 return exc_type->custom_handler( rec, frame, exc_context, dispatch,
447 descr, nested_trylevel, nested_frame, 0 );
448 }
449
450 if (!exc_type) /* nested exception, fetch info from original exception */
451 {
452 rec = msvcrt_get_thread_data()->exc_record;
453 exc_type = (cxx_exception_type *)rec->ExceptionInformation[2];
454 }
455
456 if (TRACE_ON(seh))
457 {
458 TRACE("handling C++ exception rec %p frame %p trylevel %d descr %p nested_frame %p\n",
459 rec, frame, frame->trylevel, descr, nested_frame );
460 dump_exception_type( exc_type );
461 dump_function_descr( descr, exc_type );
462 }
463
464 next_ip = call_catch_block( rec, frame, descr, frame->trylevel, exc_type );
465
466 if (!next_ip) return ExceptionContinueSearch;
467 rec->ExceptionFlags &= ~EH_NONCONTINUABLE;
468 context->Eip = (DWORD)next_ip;
469 context->Ebp = (DWORD)&frame->ebp;
470 context->Esp = ((DWORD*)frame)[-1];
471 return ExceptionContinueExecution;
472}
473
474/*********************************************************************
475 * __CxxFrameHandler (MSVCRT.@)
476 */
477
478#ifdef __WIN32OS2__
479DEFINE_REGS_ENTRYPOINT_4( __CxxFrameHandler, EXC__CxxFrameHandler, PEXCEPTION_RECORD,
480 EXCEPTION_FRAME*, PCONTEXT ,EXCEPTION_FRAME**);
481
482void EXC__CxxFrameHandler( PEXCEPTION_RECORD rec, EXCEPTION_FRAME* frame,
483#else
484void __CxxFrameHandler( PEXCEPTION_RECORD rec, EXCEPTION_FRAME* frame,
485#endif
486 PCONTEXT exc_context, EXCEPTION_FRAME** dispatch,
487 CONTEXT86 *context )
488{
489 cxx_function_descr *descr = (cxx_function_descr *)context->Eax;
490 context->Eax = cxx_frame_handler( rec, (cxx_exception_frame *)frame,
491 exc_context, dispatch, descr, NULL, 0, context );
492}
493
494#endif /* __i386__ */
495
496/*********************************************************************
497 * _CxxThrowException (MSVCRT.@)
498 */
499void _CxxThrowException( void *object, cxx_exception_type *type )
500{
501 DWORD args[3];
502
503 args[0] = CXX_FRAME_MAGIC;
504 args[1] = (DWORD)object;
505 args[2] = (DWORD)type;
506 RaiseException( CXX_EXCEPTION, EH_NONCONTINUABLE, 3, args );
507}
Note: See TracBrowser for help on using the repository browser.