Implement ClearCustData.
[wine] / dlls / msvcrt / except.c
1 /*
2  * msvcrt.dll exception handling
3  *
4  * Copyright 2000 Jon Griffiths
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  *
22  * See http://www.microsoft.com/msj/0197/exception/exception.htm,
23  * but don't believe all of it.
24  *
25  * FIXME: Incomplete support for nested exceptions/try block cleanup.
26  */
27
28 #include "config.h"
29 #include "wine/port.h"
30
31 #include <stdarg.h>
32
33 #include "windef.h"
34 #include "winbase.h"
35 #include "winreg.h"
36 #include "winternl.h"
37 #include "wine/exception.h"
38 #include "msvcrt.h"
39
40 #include "msvcrt/setjmp.h"
41 #include "excpt.h"
42
43
44 #include "wine/debug.h"
45
46 WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
47
48 typedef void (*MSVCRT_sig_handler_func)(void);
49
50 /* VC++ extensions to Win32 SEH */
51 typedef struct _SCOPETABLE
52 {
53   int previousTryLevel;
54   int (*lpfnFilter)(PEXCEPTION_POINTERS);
55   int (*lpfnHandler)(void);
56 } SCOPETABLE, *PSCOPETABLE;
57
58 typedef struct _MSVCRT_EXCEPTION_FRAME
59 {
60   EXCEPTION_REGISTRATION_RECORD *prev;
61   void (*handler)(PEXCEPTION_RECORD, PEXCEPTION_REGISTRATION_RECORD,
62                   PCONTEXT, PEXCEPTION_RECORD);
63   PSCOPETABLE scopetable;
64   int trylevel;
65   int _ebp;
66   PEXCEPTION_POINTERS xpointers;
67 } MSVCRT_EXCEPTION_FRAME;
68
69 #define TRYLEVEL_END (-1) /* End of trylevel list */
70
71 #if defined(__GNUC__) && defined(__i386__)
72 inline static void call_finally_block( void *code_block, void *base_ptr )
73 {
74     __asm__ __volatile__ ("movl %1,%%ebp; call *%%eax" \
75                           : : "a" (code_block), "g" (base_ptr));
76 }
77
78 inline static DWORD call_filter( void *func, void *arg, void *ebp )
79 {
80     DWORD ret;
81     __asm__ __volatile__ ("pushl %%ebp; pushl %3; movl %2,%%ebp; call *%%eax; popl %%ebp; popl %%ebp"
82                           : "=a" (ret)
83                           : "0" (func), "g" (ebp), "g" (arg)
84                           : "ecx", "edx", "memory" );
85     return ret;
86 }
87 #endif
88
89 static DWORD MSVCRT_nested_handler(PEXCEPTION_RECORD rec,
90                                    EXCEPTION_REGISTRATION_RECORD* frame,
91                                    PCONTEXT context WINE_UNUSED,
92                                    EXCEPTION_REGISTRATION_RECORD** dispatch)
93 {
94   if (rec->ExceptionFlags & 0x6)
95     return ExceptionContinueSearch;
96   *dispatch = frame;
97   return ExceptionCollidedUnwind;
98 }
99
100
101 /*********************************************************************
102  *              _XcptFilter (MSVCRT.@)
103  */
104 int _XcptFilter(int ex, PEXCEPTION_POINTERS ptr)
105 {
106   FIXME("(%d,%p)semi-stub\n", ex, ptr);
107   return UnhandledExceptionFilter(ptr);
108 }
109
110 /*********************************************************************
111  *              _EH_prolog (MSVCRT.@)
112  */
113 #ifdef __i386__
114 /* Provided for VC++ binary compatibility only */
115 __ASM_GLOBAL_FUNC(_EH_prolog,
116                   "pushl $-1\n\t"
117                   "pushl %eax\n\t"
118                   "pushl %fs:0\n\t"
119                   "movl  %esp, %fs:0\n\t"
120                   "movl  12(%esp), %eax\n\t"
121                   "movl  %ebp, 12(%esp)\n\t"
122                   "leal  12(%esp), %ebp\n\t"
123                   "pushl %eax\n\t"
124                   "ret");
125 #endif
126
127 /*******************************************************************
128  *              _global_unwind2 (MSVCRT.@)
129  */
130 void _global_unwind2(PEXCEPTION_REGISTRATION_RECORD frame)
131 {
132     TRACE("(%p)\n",frame);
133     RtlUnwind( frame, 0, 0, 0 );
134 }
135
136 /*******************************************************************
137  *              _local_unwind2 (MSVCRT.@)
138  */
139 void _local_unwind2(MSVCRT_EXCEPTION_FRAME* frame, int trylevel)
140 {
141   MSVCRT_EXCEPTION_FRAME *curframe = frame;
142   EXCEPTION_REGISTRATION_RECORD reg;
143
144   TRACE("(%p,%d,%d)\n",frame, frame->trylevel, trylevel);
145
146   /* Register a handler in case of a nested exception */
147   reg.Handler = (PEXCEPTION_HANDLER)MSVCRT_nested_handler;
148   reg.Prev = NtCurrentTeb()->Tib.ExceptionList;
149   __wine_push_frame(&reg);
150
151   while (frame->trylevel != TRYLEVEL_END && frame->trylevel != trylevel)
152   {
153     int curtrylevel = frame->scopetable[frame->trylevel].previousTryLevel;
154     curframe = frame;
155     curframe->trylevel = curtrylevel;
156     if (!frame->scopetable[curtrylevel].lpfnFilter)
157     {
158       ERR("__try block cleanup not implemented - expect crash!\n");
159       /* FIXME: Remove current frame, set ebp, call
160        * frame->scopetable[curtrylevel].lpfnHandler()
161        */
162     }
163   }
164   __wine_pop_frame(&reg);
165   TRACE("unwound OK\n");
166 }
167
168 /*********************************************************************
169  *              _except_handler2 (MSVCRT.@)
170  */
171 int _except_handler2(PEXCEPTION_RECORD rec,
172                      PEXCEPTION_REGISTRATION_RECORD frame,
173                      PCONTEXT context,
174                      PEXCEPTION_REGISTRATION_RECORD* dispatcher)
175 {
176   FIXME("exception %lx flags=%lx at %p handler=%p %p %p stub\n",
177         rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress,
178         frame->Handler, context, dispatcher);
179   return ExceptionContinueSearch;
180 }
181
182 /*********************************************************************
183  *              _except_handler3 (MSVCRT.@)
184  */
185 int _except_handler3(PEXCEPTION_RECORD rec,
186                      MSVCRT_EXCEPTION_FRAME* frame,
187                      PCONTEXT context, void* dispatcher)
188 {
189 #if defined(__GNUC__) && defined(__i386__)
190   long retval;
191   int trylevel;
192   EXCEPTION_POINTERS exceptPtrs;
193   PSCOPETABLE pScopeTable;
194
195   TRACE("exception %lx flags=%lx at %p handler=%p %p %p semi-stub\n",
196         rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress,
197         frame->handler, context, dispatcher);
198
199   __asm__ __volatile__ ("cld");
200
201   if (rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
202   {
203     /* Unwinding the current frame */
204      _local_unwind2(frame, TRYLEVEL_END);
205     return ExceptionContinueSearch;
206   }
207   else
208   {
209     /* Hunting for handler */
210     exceptPtrs.ExceptionRecord = rec;
211     exceptPtrs.ContextRecord = context;
212     *((DWORD *)frame-1) = (DWORD)&exceptPtrs;
213     trylevel = frame->trylevel;
214     pScopeTable = frame->scopetable;
215
216     while (trylevel != TRYLEVEL_END)
217     {
218       if (pScopeTable[trylevel].lpfnFilter)
219       {
220         TRACE("filter = %p\n", pScopeTable[trylevel].lpfnFilter);
221
222         retval = call_filter( pScopeTable[trylevel].lpfnFilter, &exceptPtrs, &frame->_ebp );
223
224         TRACE("filter returned %s\n", retval == EXCEPTION_CONTINUE_EXECUTION ?
225               "CONTINUE_EXECUTION" : retval == EXCEPTION_EXECUTE_HANDLER ?
226               "EXECUTE_HANDLER" : "CONTINUE_SEARCH");
227
228         if (retval == EXCEPTION_CONTINUE_EXECUTION)
229           return ExceptionContinueExecution;
230
231         if (retval == EXCEPTION_EXECUTE_HANDLER)
232         {
233           /* Unwind all higher frames, this one will handle the exception */
234           _global_unwind2((PEXCEPTION_REGISTRATION_RECORD)frame);
235           _local_unwind2(frame, trylevel);
236
237           /* Set our trylevel to the enclosing block, and call the __finally
238            * code, which won't return
239            */
240           frame->trylevel = pScopeTable->previousTryLevel;
241           TRACE("__finally block %p\n",pScopeTable[trylevel].lpfnHandler);
242           call_finally_block(pScopeTable[trylevel].lpfnHandler, &frame->_ebp);
243           ERR("Returned from __finally block - expect crash!\n");
244        }
245       }
246       trylevel = pScopeTable->previousTryLevel;
247     }
248   }
249 #else
250   TRACE("exception %lx flags=%lx at %p handler=%p %p %p stub\n",
251         rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress,
252         frame->handler, context, dispatcher);
253 #endif
254   return ExceptionContinueSearch;
255 }
256
257 /*********************************************************************
258  *              _abnormal_termination (MSVCRT.@)
259  */
260 int _abnormal_termination(void)
261 {
262   FIXME("(void)stub\n");
263   return 0;
264 }
265
266 /*
267  * setjmp/longjmp implementation
268  */
269
270 #ifdef __i386__
271 #define MSVCRT_JMP_MAGIC 0x56433230 /* ID value for new jump structure */
272 typedef void (*MSVCRT_unwind_function)(const void*);
273
274 /*
275  * The signatures of the setjmp/longjmp functions do not match that
276  * declared in the setjmp header so they don't follow the regular naming
277  * convention to avoid conflicts.
278  */
279
280 /*******************************************************************
281  *              _setjmp (MSVCRT.@)
282  */
283 DEFINE_REGS_ENTRYPOINT( MSVCRT__setjmp, _MSVCRT__setjmp, 4, 0 );
284 void _MSVCRT__setjmp(_JUMP_BUFFER *jmp, CONTEXT86* context)
285 {
286     TRACE("(%p)\n",jmp);
287     jmp->Ebp = context->Ebp;
288     jmp->Ebx = context->Ebx;
289     jmp->Edi = context->Edi;
290     jmp->Esi = context->Esi;
291     jmp->Esp = context->Esp;
292     jmp->Eip = context->Eip;
293     jmp->Registration = (unsigned long)NtCurrentTeb()->Tib.ExceptionList;
294     if (jmp->Registration == TRYLEVEL_END)
295         jmp->TryLevel = TRYLEVEL_END;
296     else
297         jmp->TryLevel = ((MSVCRT_EXCEPTION_FRAME*)jmp->Registration)->trylevel;
298     TRACE("returning 0\n");
299     context->Eax=0;
300 }
301
302 /*******************************************************************
303  *              _setjmp3 (MSVCRT.@)
304  */
305 DEFINE_REGS_ENTRYPOINT( MSVCRT__setjmp3, _MSVCRT__setjmp3, 8, 0 );
306 void _MSVCRT__setjmp3(_JUMP_BUFFER *jmp, int nb_args, CONTEXT86* context)
307 {
308     TRACE("(%p,%d)\n",jmp,nb_args);
309     jmp->Ebp = context->Ebp;
310     jmp->Ebx = context->Ebx;
311     jmp->Edi = context->Edi;
312     jmp->Esi = context->Esi;
313     jmp->Esp = context->Esp;
314     jmp->Eip = context->Eip;
315     jmp->Cookie = MSVCRT_JMP_MAGIC;
316     jmp->UnwindFunc = 0;
317     jmp->Registration = (unsigned long)NtCurrentTeb()->Tib.ExceptionList;
318     if (jmp->Registration == TRYLEVEL_END)
319     {
320         jmp->TryLevel = TRYLEVEL_END;
321     }
322     else
323     {
324         void **args = ((void**)context->Esp)+2;
325
326         if (nb_args > 0) jmp->UnwindFunc = (unsigned long)*args++;
327         if (nb_args > 1) jmp->TryLevel = (unsigned long)*args++;
328         else jmp->TryLevel = ((MSVCRT_EXCEPTION_FRAME*)jmp->Registration)->trylevel;
329         if (nb_args > 2)
330         {
331             size_t size = (nb_args - 2) * sizeof(DWORD);
332             memcpy( jmp->UnwindData, args, min( size, sizeof(jmp->UnwindData) ));
333         }
334     }
335     TRACE("returning 0\n");
336     context->Eax = 0;
337 }
338
339 /*********************************************************************
340  *              longjmp (MSVCRT.@)
341  */
342 DEFINE_REGS_ENTRYPOINT( MSVCRT_longjmp, _MSVCRT_longjmp, 8, 0 );
343 void _MSVCRT_longjmp(_JUMP_BUFFER *jmp, int retval, CONTEXT86* context)
344 {
345     unsigned long cur_frame = 0;
346
347     TRACE("(%p,%d)\n", jmp, retval);
348
349     cur_frame=(unsigned long)NtCurrentTeb()->Tib.ExceptionList;
350     TRACE("cur_frame=%lx\n",cur_frame);
351
352     if (cur_frame != jmp->Registration)
353         _global_unwind2((PEXCEPTION_REGISTRATION_RECORD)jmp->Registration);
354
355     if (jmp->Registration)
356     {
357         if (!IsBadReadPtr(&jmp->Cookie, sizeof(long)) &&
358             jmp->Cookie == MSVCRT_JMP_MAGIC && jmp->UnwindFunc)
359         {
360             MSVCRT_unwind_function unwind_func;
361
362             unwind_func=(MSVCRT_unwind_function)jmp->UnwindFunc;
363             unwind_func(jmp);
364         }
365         else
366             _local_unwind2((MSVCRT_EXCEPTION_FRAME*)jmp->Registration,
367                            jmp->TryLevel);
368     }
369
370     if (!retval)
371         retval = 1;
372
373     TRACE("Jump to %lx returning %d\n",jmp->Eip,retval);
374     context->Ebp = jmp->Ebp;
375     context->Ebx = jmp->Ebx;
376     context->Edi = jmp->Edi;
377     context->Esi = jmp->Esi;
378     context->Esp = jmp->Esp;
379     context->Eip = jmp->Eip;
380     context->Eax = retval;
381 }
382
383 /*********************************************************************
384  *              _seh_longjmp_unwind (MSVCRT.@)
385  */
386 void __stdcall _seh_longjmp_unwind(_JUMP_BUFFER *jmp)
387 {
388     _local_unwind2( (MSVCRT_EXCEPTION_FRAME *)jmp->Registration, jmp->TryLevel );
389 }
390 #endif /* i386 */
391
392 /*********************************************************************
393  *              signal (MSVCRT.@)
394  */
395 void* MSVCRT_signal(int sig, MSVCRT_sig_handler_func func)
396 {
397   FIXME("(%d %p):stub\n", sig, func);
398   return (void*)-1;
399 }