Commit | Line | Data |
---|---|---|
1db20bfd JG |
1 | /* |
2 | * msvcrt.dll exception handling | |
3 | * | |
4 | * Copyright 2000 Jon Griffiths | |
5 | * | |
0799c1a7 AJ |
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 | * | |
1db20bfd JG |
22 | * See http://www.microsoft.com/msj/0197/exception/exception.htm, |
23 | * but don't believe all of it. | |
24 | * | |
5f308d3c | 25 | * FIXME: Incomplete support for nested exceptions/try block cleanup. |
1db20bfd | 26 | */ |
894b188f | 27 | |
e0700217 | 28 | #include "config.h" |
894b188f | 29 | #include "wine/port.h" |
e0700217 | 30 | |
e37c6e18 AJ |
31 | #include <stdarg.h> |
32 | ||
33 | #include "windef.h" | |
34 | #include "winbase.h" | |
35 | #include "winreg.h" | |
9c1de6de | 36 | #include "winternl.h" |
5f308d3c | 37 | #include "wine/exception.h" |
1db20bfd JG |
38 | #include "msvcrt.h" |
39 | ||
1849f1eb | 40 | #include "msvcrt/setjmp.h" |
737d4be8 | 41 | #include "excpt.h" |
1849f1eb AJ |
42 | |
43 | ||
bd1689ec AJ |
44 | #include "wine/debug.h" |
45 | ||
46 | WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); | |
1db20bfd JG |
47 | |
48 | typedef void (*MSVCRT_sig_handler_func)(void); | |
49 | ||
50 | /* VC++ extensions to Win32 SEH */ | |
51 | typedef struct _SCOPETABLE | |
52 | { | |
d78b458e | 53 | int previousTryLevel; |
203a8f82 FG |
54 | int (*lpfnFilter)(PEXCEPTION_POINTERS); |
55 | int (*lpfnHandler)(void); | |
1db20bfd JG |
56 | } SCOPETABLE, *PSCOPETABLE; |
57 | ||
5f308d3c | 58 | typedef struct _MSVCRT_EXCEPTION_FRAME |
1db20bfd | 59 | { |
ee106783 AJ |
60 | EXCEPTION_REGISTRATION_RECORD *prev; |
61 | void (*handler)(PEXCEPTION_RECORD, PEXCEPTION_REGISTRATION_RECORD, | |
1db20bfd JG |
62 | PCONTEXT, PEXCEPTION_RECORD); |
63 | PSCOPETABLE scopetable; | |
d78b458e | 64 | int trylevel; |
1db20bfd JG |
65 | int _ebp; |
66 | PEXCEPTION_POINTERS xpointers; | |
5f308d3c | 67 | } MSVCRT_EXCEPTION_FRAME; |
1db20bfd | 68 | |
d78b458e | 69 | #define TRYLEVEL_END (-1) /* End of trylevel list */ |
5f308d3c JG |
70 | |
71 | #if defined(__GNUC__) && defined(__i386__) | |
107b289a AJ |
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 | } | |
0ac49a99 | 77 | |
6bd508f8 | 78 | inline static DWORD call_filter( void *func, void *arg, void *ebp ) |
0ac49a99 AJ |
79 | { |
80 | DWORD ret; | |
81 | __asm__ __volatile__ ("pushl %%ebp; pushl %3; movl %2,%%ebp; call *%%eax; popl %%ebp; popl %%ebp" | |
6bd508f8 AJ |
82 | : "=a" (ret) |
83 | : "0" (func), "g" (ebp), "g" (arg) | |
84 | : "ecx", "edx", "memory" ); | |
0ac49a99 AJ |
85 | return ret; |
86 | } | |
c43b5c8f | 87 | #endif |
5f308d3c | 88 | |
203a8f82 | 89 | static DWORD MSVCRT_nested_handler(PEXCEPTION_RECORD rec, |
ee106783 | 90 | EXCEPTION_REGISTRATION_RECORD* frame, |
203a8f82 | 91 | PCONTEXT context WINE_UNUSED, |
ee106783 | 92 | EXCEPTION_REGISTRATION_RECORD** dispatch) |
1db20bfd | 93 | { |
5f308d3c JG |
94 | if (rec->ExceptionFlags & 0x6) |
95 | return ExceptionContinueSearch; | |
96 | *dispatch = frame; | |
97 | return ExceptionCollidedUnwind; | |
98 | } | |
5f308d3c | 99 | |
1db20bfd | 100 | |
1db20bfd JG |
101 | /********************************************************************* |
102 | * _XcptFilter (MSVCRT.@) | |
103 | */ | |
203a8f82 | 104 | int _XcptFilter(int ex, PEXCEPTION_POINTERS ptr) |
1db20bfd JG |
105 | { |
106 | FIXME("(%d,%p)semi-stub\n", ex, ptr); | |
107 | return UnhandledExceptionFilter(ptr); | |
108 | } | |
109 | ||
e5348e27 JG |
110 | /********************************************************************* |
111 | * _EH_prolog (MSVCRT.@) | |
112 | */ | |
113 | #ifdef __i386__ | |
f803e2a9 | 114 | /* Provided for VC++ binary compatibility only */ |
203a8f82 | 115 | __ASM_GLOBAL_FUNC(_EH_prolog, |
d78b458e | 116 | "pushl $-1\n\t" |
e5348e27 JG |
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 | ||
1db20bfd JG |
127 | /******************************************************************* |
128 | * _global_unwind2 (MSVCRT.@) | |
129 | */ | |
ee106783 | 130 | void _global_unwind2(PEXCEPTION_REGISTRATION_RECORD frame) |
1db20bfd | 131 | { |
5f308d3c JG |
132 | TRACE("(%p)\n",frame); |
133 | RtlUnwind( frame, 0, 0, 0 ); | |
1db20bfd JG |
134 | } |
135 | ||
136 | /******************************************************************* | |
137 | * _local_unwind2 (MSVCRT.@) | |
138 | */ | |
d78b458e | 139 | void _local_unwind2(MSVCRT_EXCEPTION_FRAME* frame, int trylevel) |
1db20bfd | 140 | { |
5f308d3c | 141 | MSVCRT_EXCEPTION_FRAME *curframe = frame; |
ee106783 | 142 | EXCEPTION_REGISTRATION_RECORD reg; |
5f308d3c | 143 | |
d78b458e | 144 | TRACE("(%p,%d,%d)\n",frame, frame->trylevel, trylevel); |
5f308d3c JG |
145 | |
146 | /* Register a handler in case of a nested exception */ | |
147 | reg.Handler = (PEXCEPTION_HANDLER)MSVCRT_nested_handler; | |
b91e9cb3 | 148 | reg.Prev = NtCurrentTeb()->Tib.ExceptionList; |
5f308d3c JG |
149 | __wine_push_frame(®); |
150 | ||
151 | while (frame->trylevel != TRYLEVEL_END && frame->trylevel != trylevel) | |
152 | { | |
d78b458e | 153 | int curtrylevel = frame->scopetable[frame->trylevel].previousTryLevel; |
5f308d3c JG |
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(®); | |
165 | TRACE("unwound OK\n"); | |
1db20bfd JG |
166 | } |
167 | ||
168 | /********************************************************************* | |
169 | * _except_handler2 (MSVCRT.@) | |
170 | */ | |
203a8f82 | 171 | int _except_handler2(PEXCEPTION_RECORD rec, |
ee106783 | 172 | PEXCEPTION_REGISTRATION_RECORD frame, |
203a8f82 | 173 | PCONTEXT context, |
ee106783 | 174 | PEXCEPTION_REGISTRATION_RECORD* dispatcher) |
1db20bfd JG |
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 | */ | |
203a8f82 FG |
185 | int _except_handler3(PEXCEPTION_RECORD rec, |
186 | MSVCRT_EXCEPTION_FRAME* frame, | |
187 | PCONTEXT context, void* dispatcher) | |
1db20bfd | 188 | { |
5f308d3c | 189 | #if defined(__GNUC__) && defined(__i386__) |
d78b458e AJ |
190 | long retval; |
191 | int trylevel; | |
5f308d3c JG |
192 | EXCEPTION_POINTERS exceptPtrs; |
193 | PSCOPETABLE pScopeTable; | |
194 | ||
195 | TRACE("exception %lx flags=%lx at %p handler=%p %p %p semi-stub\n", | |
1db20bfd JG |
196 | rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, |
197 | frame->handler, context, dispatcher); | |
5f308d3c JG |
198 | |
199 | __asm__ __volatile__ ("cld"); | |
200 | ||
201 | if (rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND)) | |
202 | { | |
203 | /* Unwinding the current frame */ | |
203a8f82 | 204 | _local_unwind2(frame, TRYLEVEL_END); |
5f308d3c JG |
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 | ||
0ac49a99 | 222 | retval = call_filter( pScopeTable[trylevel].lpfnFilter, &exceptPtrs, &frame->_ebp ); |
5f308d3c JG |
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 */ | |
ee106783 | 234 | _global_unwind2((PEXCEPTION_REGISTRATION_RECORD)frame); |
203a8f82 | 235 | _local_unwind2(frame, trylevel); |
5f308d3c JG |
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); | |
107b289a | 242 | call_finally_block(pScopeTable[trylevel].lpfnHandler, &frame->_ebp); |
5f308d3c JG |
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 | |
1db20bfd JG |
254 | return ExceptionContinueSearch; |
255 | } | |
256 | ||
257 | /********************************************************************* | |
258 | * _abnormal_termination (MSVCRT.@) | |
259 | */ | |
203a8f82 | 260 | int _abnormal_termination(void) |
1db20bfd JG |
261 | { |
262 | FIXME("(void)stub\n"); | |
263 | return 0; | |
264 | } | |
265 | ||
1849f1eb AJ |
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 | /* | |
9a624916 VB |
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 | |
1849f1eb AJ |
277 | * convention to avoid conflicts. |
278 | */ | |
279 | ||
1db20bfd JG |
280 | /******************************************************************* |
281 | * _setjmp (MSVCRT.@) | |
282 | */ | |
f752be84 | 283 | DEFINE_REGS_ENTRYPOINT( MSVCRT__setjmp, _MSVCRT__setjmp, 4, 0 ); |
1849f1eb | 284 | void _MSVCRT__setjmp(_JUMP_BUFFER *jmp, CONTEXT86* context) |
1db20bfd | 285 | { |
1849f1eb AJ |
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; | |
b91e9cb3 | 293 | jmp->Registration = (unsigned long)NtCurrentTeb()->Tib.ExceptionList; |
1849f1eb AJ |
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; | |
1db20bfd JG |
300 | } |
301 | ||
03df00e9 EK |
302 | /******************************************************************* |
303 | * _setjmp3 (MSVCRT.@) | |
304 | */ | |
f752be84 | 305 | DEFINE_REGS_ENTRYPOINT( MSVCRT__setjmp3, _MSVCRT__setjmp3, 8, 0 ); |
1849f1eb | 306 | void _MSVCRT__setjmp3(_JUMP_BUFFER *jmp, int nb_args, CONTEXT86* context) |
03df00e9 | 307 | { |
1849f1eb AJ |
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; | |
b91e9cb3 | 317 | jmp->Registration = (unsigned long)NtCurrentTeb()->Tib.ExceptionList; |
1849f1eb AJ |
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; | |
03df00e9 EK |
337 | } |
338 | ||
1db20bfd JG |
339 | /********************************************************************* |
340 | * longjmp (MSVCRT.@) | |
341 | */ | |
f752be84 | 342 | DEFINE_REGS_ENTRYPOINT( MSVCRT_longjmp, _MSVCRT_longjmp, 8, 0 ); |
1849f1eb | 343 | void _MSVCRT_longjmp(_JUMP_BUFFER *jmp, int retval, CONTEXT86* context) |
1db20bfd | 344 | { |
1849f1eb AJ |
345 | unsigned long cur_frame = 0; |
346 | ||
347 | TRACE("(%p,%d)\n", jmp, retval); | |
348 | ||
b91e9cb3 | 349 | cur_frame=(unsigned long)NtCurrentTeb()->Tib.ExceptionList; |
1849f1eb AJ |
350 | TRACE("cur_frame=%lx\n",cur_frame); |
351 | ||
352 | if (cur_frame != jmp->Registration) | |
ee106783 | 353 | _global_unwind2((PEXCEPTION_REGISTRATION_RECORD)jmp->Registration); |
1849f1eb AJ |
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; | |
1db20bfd JG |
381 | } |
382 | ||
acea9d12 AJ |
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 | } | |
306a60d1 | 390 | #endif /* i386 */ |
acea9d12 | 391 | |
1db20bfd JG |
392 | /********************************************************************* |
393 | * signal (MSVCRT.@) | |
394 | */ | |
203a8f82 | 395 | void* MSVCRT_signal(int sig, MSVCRT_sig_handler_func func) |
1db20bfd JG |
396 | { |
397 | FIXME("(%d %p):stub\n", sig, func); | |
398 | return (void*)-1; | |
399 | } |