When including 'wine/port.h', include it first.
[wine] / dlls / msvcrt / except.c
1 /*
2  * msvcrt.dll exception handling
3  *
4  * Copyright 2000 Jon Griffiths
5  *
6  * See http://www.microsoft.com/msj/0197/exception/exception.htm,
7  * but don't believe all of it.
8  *
9  * FIXME: Incomplete support for nested exceptions/try block cleanup.
10  */
11 #include "config.h"
12
13 #include "ntddk.h"
14 #include "wine/exception.h"
15 #include "thread.h"
16 #include "msvcrt.h"
17
18 DEFAULT_DEBUG_CHANNEL(msvcrt);
19
20 typedef void (*MSVCRT_sig_handler_func)(void);
21
22 /* VC++ extensions to Win32 SEH */
23 typedef struct _SCOPETABLE
24 {
25   DWORD previousTryLevel;
26   int (*lpfnFilter)(PEXCEPTION_POINTERS);
27   int (*lpfnHandler)(void);
28 } SCOPETABLE, *PSCOPETABLE;
29
30 typedef struct _MSVCRT_EXCEPTION_FRAME
31 {
32   EXCEPTION_FRAME *prev;
33   void (*handler)(PEXCEPTION_RECORD, PEXCEPTION_FRAME,
34                   PCONTEXT, PEXCEPTION_RECORD);
35   PSCOPETABLE scopetable;
36   DWORD trylevel;
37   int _ebp;
38   PEXCEPTION_POINTERS xpointers;
39 } MSVCRT_EXCEPTION_FRAME;
40
41 #define TRYLEVEL_END 0xffffffff /* End of trylevel list */
42
43 #if defined(__GNUC__) && defined(__i386__)
44
45 inline static void call_finally_block( void *code_block, void *base_ptr )
46 {
47     __asm__ __volatile__ ("movl %1,%%ebp; call *%%eax" \
48                           : : "a" (code_block), "g" (base_ptr));
49 }
50
51 static DWORD MSVCRT_nested_handler(PEXCEPTION_RECORD rec,
52                                    struct __EXCEPTION_FRAME* frame,
53                                    PCONTEXT context WINE_UNUSED,
54                                    struct __EXCEPTION_FRAME** dispatch)
55 {
56   if (rec->ExceptionFlags & 0x6)
57     return ExceptionContinueSearch;
58   *dispatch = frame;
59   return ExceptionCollidedUnwind;
60 }
61 #endif
62
63
64 /*********************************************************************
65  *              _XcptFilter (MSVCRT.@)
66  */
67 int _XcptFilter(int ex, PEXCEPTION_POINTERS ptr)
68 {
69   FIXME("(%d,%p)semi-stub\n", ex, ptr);
70   return UnhandledExceptionFilter(ptr);
71 }
72
73 /*********************************************************************
74  *              _EH_prolog (MSVCRT.@)
75  */
76 #ifdef __i386__
77 /* Provided for VC++ binary compatability only */
78 __ASM_GLOBAL_FUNC(_EH_prolog,
79                   "pushl $0xff\n\t"
80                   "pushl %eax\n\t"
81                   "pushl %fs:0\n\t"
82                   "movl  %esp, %fs:0\n\t"
83                   "movl  12(%esp), %eax\n\t"
84                   "movl  %ebp, 12(%esp)\n\t"
85                   "leal  12(%esp), %ebp\n\t"
86                   "pushl %eax\n\t"
87                   "ret");
88 #endif
89
90 /*******************************************************************
91  *              _global_unwind2 (MSVCRT.@)
92  */
93 void _global_unwind2(PEXCEPTION_FRAME frame)
94 {
95     TRACE("(%p)\n",frame);
96     RtlUnwind( frame, 0, 0, 0 );
97 }
98
99 /*******************************************************************
100  *              _local_unwind2 (MSVCRT.@)
101  */
102 void _local_unwind2(MSVCRT_EXCEPTION_FRAME* frame,
103                     DWORD trylevel)
104 {
105   MSVCRT_EXCEPTION_FRAME *curframe = frame;
106   DWORD curtrylevel = 0xfe;
107   EXCEPTION_FRAME reg;
108
109   TRACE("(%p,%ld,%ld)\n",frame, frame->trylevel, trylevel);
110
111   /* Register a handler in case of a nested exception */
112   reg.Handler = (PEXCEPTION_HANDLER)MSVCRT_nested_handler;
113   reg.Prev = NtCurrentTeb()->except;
114   __wine_push_frame(&reg);
115
116   while (frame->trylevel != TRYLEVEL_END && frame->trylevel != trylevel)
117   {
118     curtrylevel = frame->scopetable[frame->trylevel].previousTryLevel;
119     curframe = frame;
120     curframe->trylevel = curtrylevel;
121     if (!frame->scopetable[curtrylevel].lpfnFilter)
122     {
123       ERR("__try block cleanup not implemented - expect crash!\n");
124       /* FIXME: Remove current frame, set ebp, call
125        * frame->scopetable[curtrylevel].lpfnHandler()
126        */
127     }
128   }
129   __wine_pop_frame(&reg);
130   TRACE("unwound OK\n");
131 }
132
133 /*********************************************************************
134  *              _except_handler2 (MSVCRT.@)
135  */
136 int _except_handler2(PEXCEPTION_RECORD rec,
137                      PEXCEPTION_FRAME frame,
138                      PCONTEXT context,
139                      PEXCEPTION_FRAME* dispatcher)
140 {
141   FIXME("exception %lx flags=%lx at %p handler=%p %p %p stub\n",
142         rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress,
143         frame->Handler, context, dispatcher);
144   return ExceptionContinueSearch;
145 }
146
147 /*********************************************************************
148  *              _except_handler3 (MSVCRT.@)
149  */
150 int _except_handler3(PEXCEPTION_RECORD rec,
151                      MSVCRT_EXCEPTION_FRAME* frame,
152                      PCONTEXT context, void* dispatcher)
153 {
154 #if defined(__GNUC__) && defined(__i386__)
155   long retval, trylevel;
156   EXCEPTION_POINTERS exceptPtrs;
157   PSCOPETABLE pScopeTable;
158
159   TRACE("exception %lx flags=%lx at %p handler=%p %p %p semi-stub\n",
160         rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress,
161         frame->handler, context, dispatcher);
162
163   __asm__ __volatile__ ("cld");
164
165   if (rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
166   {
167     /* Unwinding the current frame */
168      _local_unwind2(frame, TRYLEVEL_END);
169     return ExceptionContinueSearch;
170   }
171   else
172   {
173     /* Hunting for handler */
174     exceptPtrs.ExceptionRecord = rec;
175     exceptPtrs.ContextRecord = context;
176     *((DWORD *)frame-1) = (DWORD)&exceptPtrs;
177     trylevel = frame->trylevel;
178     pScopeTable = frame->scopetable;
179
180     while (trylevel != TRYLEVEL_END)
181     {
182       if (pScopeTable[trylevel].lpfnFilter)
183       {
184         TRACE("filter = %p\n", pScopeTable[trylevel].lpfnFilter);
185
186         retval = pScopeTable[trylevel].lpfnFilter(&exceptPtrs);
187
188         TRACE("filter returned %s\n", retval == EXCEPTION_CONTINUE_EXECUTION ?
189               "CONTINUE_EXECUTION" : retval == EXCEPTION_EXECUTE_HANDLER ?
190               "EXECUTE_HANDLER" : "CONTINUE_SEARCH");
191
192         if (retval == EXCEPTION_CONTINUE_EXECUTION)
193           return ExceptionContinueExecution;
194
195         if (retval == EXCEPTION_EXECUTE_HANDLER)
196         {
197           /* Unwind all higher frames, this one will handle the exception */
198           _global_unwind2((PEXCEPTION_FRAME)frame);
199           _local_unwind2(frame, trylevel);
200
201           /* Set our trylevel to the enclosing block, and call the __finally
202            * code, which won't return
203            */
204           frame->trylevel = pScopeTable->previousTryLevel;
205           TRACE("__finally block %p\n",pScopeTable[trylevel].lpfnHandler);
206           call_finally_block(pScopeTable[trylevel].lpfnHandler, &frame->_ebp);
207           ERR("Returned from __finally block - expect crash!\n");
208        }
209       }
210       trylevel = pScopeTable->previousTryLevel;
211     }
212   }
213 #else
214   TRACE("exception %lx flags=%lx at %p handler=%p %p %p stub\n",
215         rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress,
216         frame->handler, context, dispatcher);
217 #endif
218   return ExceptionContinueSearch;
219 }
220
221 /*********************************************************************
222  *              _abnormal_termination (MSVCRT.@)
223  */
224 int _abnormal_termination(void)
225 {
226   FIXME("(void)stub\n");
227   return 0;
228 }
229
230 /*******************************************************************
231  *              _setjmp (MSVCRT.@)
232  */
233 int MSVCRT__setjmp(LPDWORD* jmpbuf)
234 {
235   FIXME(":(%p): stub\n",jmpbuf);
236   return 0;
237 }
238
239 /*******************************************************************
240  *              _setjmp3 (MSVCRT.@)
241  */
242 int __cdecl MSVCRT__setjmp3(LPDWORD *jmpbuf, int x)
243 {
244   FIXME(":(%p %x): stub\n",jmpbuf,x);
245   return 0;
246 }
247
248 /*********************************************************************
249  *              longjmp (MSVCRT.@)
250  */
251 void MSVCRT_longjmp(jmp_buf env, int val)
252 {
253   FIXME("MSVCRT_longjmp semistub, expect crash\n");
254   longjmp(env, val);
255 }
256
257 /*********************************************************************
258  *              signal (MSVCRT.@)
259  */
260 void* MSVCRT_signal(int sig, MSVCRT_sig_handler_func func)
261 {
262   FIXME("(%d %p):stub\n", sig, func);
263   return (void*)-1;
264 }