ntdll: Simplify the checking of heap pointers when debugging is not enabled.
[wine] / dlls / ntdll / tests / exception.c
1 /*
2  * Unit test suite for ntdll exceptions
3  *
4  * Copyright 2005 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22 #include <stdio.h>
23
24 #ifndef _WIN32_WINNT
25 #define _WIN32_WINNT 0x500 /* For NTSTATUS */
26 #endif
27
28 #define NONAMELESSUNION
29 #define NONAMELESSSTRUCT
30 #include "ntstatus.h"
31 #define WIN32_NO_STATUS
32 #include "windef.h"
33 #include "winbase.h"
34 #include "winnt.h"
35 #include "winreg.h"
36 #include "winternl.h"
37 #include "excpt.h"
38 #include "wine/test.h"
39
40 static void *code_mem;
41
42 static struct _TEB * (WINAPI *pNtCurrentTeb)(void);
43 static NTSTATUS  (WINAPI *pNtGetContextThread)(HANDLE,CONTEXT*);
44 static NTSTATUS  (WINAPI *pNtSetContextThread)(HANDLE,CONTEXT*);
45 static NTSTATUS  (WINAPI *pRtlRaiseException)(EXCEPTION_RECORD *rec);
46 static PVOID     (WINAPI *pRtlAddVectoredExceptionHandler)(ULONG first, PVECTORED_EXCEPTION_HANDLER func);
47 static ULONG     (WINAPI *pRtlRemoveVectoredExceptionHandler)(PVOID handler);
48 static NTSTATUS  (WINAPI *pNtReadVirtualMemory)(HANDLE, const void*, void*, SIZE_T, SIZE_T*);
49 static NTSTATUS  (WINAPI *pNtTerminateProcess)(HANDLE handle, LONG exit_code);
50 static NTSTATUS  (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
51 static NTSTATUS  (WINAPI *pNtSetInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG);
52
53 #ifdef __i386__
54
55 #ifndef __WINE_WINTRNL_H
56 #define ProcessExecuteFlags 0x22
57 #define MEM_EXECUTE_OPTION_DISABLE   0x01
58 #define MEM_EXECUTE_OPTION_ENABLE    0x02
59 #define MEM_EXECUTE_OPTION_PERMANENT 0x08
60 #endif
61
62 static int      my_argc;
63 static char**   my_argv;
64 static int      test_stage;
65
66 /* Test various instruction combinations that cause a protection fault on the i386,
67  * and check what the resulting exception looks like.
68  */
69
70 static const struct exception
71 {
72     BYTE     code[18];   /* asm code */
73     BYTE     offset;     /* offset of faulting instruction */
74     BYTE     length;     /* length of faulting instruction */
75     NTSTATUS status;     /* expected status code */
76     DWORD    nb_params;  /* expected number of parameters */
77     DWORD    params[4];  /* expected parameters */
78 } exceptions[] =
79 {
80     /* test some privileged instructions */
81     { { 0xfb, 0xc3 },  /* 0: sti; ret */
82       0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
83     { { 0x6c, 0xc3 },  /* 1: insb (%dx); ret */
84       0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
85     { { 0x6d, 0xc3 },  /* 2: insl (%dx); ret */
86       0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
87     { { 0x6e, 0xc3 },  /* 3: outsb (%dx); ret */
88       0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
89     { { 0x6f, 0xc3 },  /* 4: outsl (%dx); ret */
90       0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
91     { { 0xe4, 0x11, 0xc3 },  /* 5: inb $0x11,%al; ret */
92       0, 2, STATUS_PRIVILEGED_INSTRUCTION, 0 },
93     { { 0xe5, 0x11, 0xc3 },  /* 6: inl $0x11,%eax; ret */
94       0, 2, STATUS_PRIVILEGED_INSTRUCTION, 0 },
95     { { 0xe6, 0x11, 0xc3 },  /* 7: outb %al,$0x11; ret */
96       0, 2, STATUS_PRIVILEGED_INSTRUCTION, 0 },
97     { { 0xe7, 0x11, 0xc3 },  /* 8: outl %eax,$0x11; ret */
98       0, 2, STATUS_PRIVILEGED_INSTRUCTION, 0 },
99     { { 0xed, 0xc3 },  /* 9: inl (%dx),%eax; ret */
100       0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
101     { { 0xee, 0xc3 },  /* 10: outb %al,(%dx); ret */
102       0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
103     { { 0xef, 0xc3 },  /* 11: outl %eax,(%dx); ret */
104       0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
105     { { 0xf4, 0xc3 },  /* 12: hlt; ret */
106       0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
107     { { 0xfa, 0xc3 },  /* 13: cli; ret */
108       0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
109
110     /* test long jump to invalid selector */
111     { { 0xea, 0, 0, 0, 0, 0, 0, 0xc3 },  /* 14: ljmp $0,$0; ret */
112       0, 7, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
113
114     /* test iret to invalid selector */
115     { { 0x6a, 0x00, 0x6a, 0x00, 0x6a, 0x00, 0xcf, 0x83, 0xc4, 0x0c, 0xc3 },
116       /* 15: pushl $0; pushl $0; pushl $0; iret; addl $12,%esp; ret */
117       6, 1, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
118
119     /* test loading an invalid selector */
120     { { 0xb8, 0xef, 0xbe, 0x00, 0x00, 0x8e, 0xe8, 0xc3 },  /* 16: mov $beef,%ax; mov %ax,%gs; ret */
121       5, 2, STATUS_ACCESS_VIOLATION, 2, { 0, 0xbee8 } }, /* 0xbee8 or 0xffffffff */
122
123     /* test accessing a zero selector */
124     { { 0x06, 0x31, 0xc0, 0x8e, 0xc0, 0x26, 0xa1, 0, 0, 0, 0, 0x07, 0xc3 },
125       /* 17: push %es; xor %eax,%eax; mov %ax,%es; mov %es:(0),%ax; pop %es; ret */
126       5, 6, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
127
128     /* test moving %cs -> %ss */
129     { { 0x0e, 0x17, 0x58, 0xc3 },  /* 18: pushl %cs; popl %ss; popl %eax; ret */
130       1, 1, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
131
132     /* 19: test overlong instruction (limit is 15 bytes, 5 on Win7) */
133     { { 0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0xfa,0xc3 },
134       0, 16, STATUS_ILLEGAL_INSTRUCTION, 0 },
135     { { 0x64,0x64,0x64,0x64,0xfa,0xc3 },
136       0, 5, STATUS_PRIVILEGED_INSTRUCTION, 0 },
137
138     /* test invalid interrupt */
139     { { 0xcd, 0xff, 0xc3 },   /* 21: int $0xff; ret */
140       0, 2, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
141
142     /* test moves to/from Crx */
143     { { 0x0f, 0x20, 0xc0, 0xc3 },  /* 22: movl %cr0,%eax; ret */
144       0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
145     { { 0x0f, 0x20, 0xe0, 0xc3 },  /* 23: movl %cr4,%eax; ret */
146       0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
147     { { 0x0f, 0x22, 0xc0, 0xc3 },  /* 24: movl %eax,%cr0; ret */
148       0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
149     { { 0x0f, 0x22, 0xe0, 0xc3 },  /* 25: movl %eax,%cr4; ret */
150       0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
151
152     /* test moves to/from Drx */
153     { { 0x0f, 0x21, 0xc0, 0xc3 },  /* 26: movl %dr0,%eax; ret */
154       0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
155     { { 0x0f, 0x21, 0xc8, 0xc3 },  /* 27: movl %dr1,%eax; ret */
156       0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
157     { { 0x0f, 0x21, 0xf8, 0xc3 },  /* 28: movl %dr7,%eax; ret */
158       0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
159     { { 0x0f, 0x23, 0xc0, 0xc3 },  /* 29: movl %eax,%dr0; ret */
160       0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
161     { { 0x0f, 0x23, 0xc8, 0xc3 },  /* 30: movl %eax,%dr1; ret */
162       0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
163     { { 0x0f, 0x23, 0xf8, 0xc3 },  /* 31: movl %eax,%dr7; ret */
164       0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
165
166     /* test memory reads */
167     { { 0xa1, 0xfc, 0xff, 0xff, 0xff, 0xc3 },  /* 32: movl 0xfffffffc,%eax; ret */
168       0, 5, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffc } },
169     { { 0xa1, 0xfd, 0xff, 0xff, 0xff, 0xc3 },  /* 33: movl 0xfffffffd,%eax; ret */
170       0, 5, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffd } },
171     { { 0xa1, 0xfe, 0xff, 0xff, 0xff, 0xc3 },  /* 34: movl 0xfffffffe,%eax; ret */
172       0, 5, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffe } },
173     { { 0xa1, 0xff, 0xff, 0xff, 0xff, 0xc3 },  /* 35: movl 0xffffffff,%eax; ret */
174       0, 5, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
175
176     /* test memory writes */
177     { { 0xa3, 0xfc, 0xff, 0xff, 0xff, 0xc3 },  /* 36: movl %eax,0xfffffffc; ret */
178       0, 5, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffc } },
179     { { 0xa3, 0xfd, 0xff, 0xff, 0xff, 0xc3 },  /* 37: movl %eax,0xfffffffd; ret */
180       0, 5, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffd } },
181     { { 0xa3, 0xfe, 0xff, 0xff, 0xff, 0xc3 },  /* 38: movl %eax,0xfffffffe; ret */
182       0, 5, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffe } },
183     { { 0xa3, 0xff, 0xff, 0xff, 0xff, 0xc3 },  /* 39: movl %eax,0xffffffff; ret */
184       0, 5, STATUS_ACCESS_VIOLATION, 2, { 1, 0xffffffff } },
185
186     /* 40: test exception with cleared %ds and %es */
187     { { 0x1e, 0x06, 0x31, 0xc0, 0x8e, 0xd8, 0x8e, 0xc0, 0xfa, 0x07, 0x1f, 0xc3 },
188           /* push %ds; push %es; xorl %eax,%eax; mov %ax,%ds; mov %ax,%es; cli; pop %es; pop %ds; ret */
189       8, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
190 };
191
192 static int got_exception;
193 static BOOL have_vectored_api;
194
195 static void run_exception_test(void *handler, const void* context,
196                                const void *code, unsigned int code_size,
197                                DWORD access)
198 {
199     struct {
200         EXCEPTION_REGISTRATION_RECORD frame;
201         const void *context;
202     } exc_frame;
203     void (*func)(void) = code_mem;
204     DWORD oldaccess, oldaccess2;
205
206     exc_frame.frame.Handler = handler;
207     exc_frame.frame.Prev = pNtCurrentTeb()->Tib.ExceptionList;
208     exc_frame.context = context;
209
210     memcpy(code_mem, code, code_size);
211     if(access)
212         VirtualProtect(code_mem, code_size, access, &oldaccess);
213
214     pNtCurrentTeb()->Tib.ExceptionList = &exc_frame.frame;
215     func();
216     pNtCurrentTeb()->Tib.ExceptionList = exc_frame.frame.Prev;
217
218     if(access)
219         VirtualProtect(code_mem, code_size, oldaccess, &oldaccess2);
220 }
221
222 static LONG CALLBACK rtlraiseexception_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
223 {
224     PCONTEXT context = ExceptionInfo->ContextRecord;
225     PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
226     trace("vect. handler %08x addr:%p context.Eip:%x\n", rec->ExceptionCode,
227           rec->ExceptionAddress, context->Eip);
228
229     ok(rec->ExceptionAddress == (char *)code_mem + 0xb, "ExceptionAddress at %p instead of %p\n",
230        rec->ExceptionAddress, (char *)code_mem + 0xb);
231
232     if (pNtCurrentTeb()->Peb->BeingDebugged)
233         ok((void *)context->Eax == pRtlRaiseException, "debugger managed to modify Eax to %x should be %p\n",
234            context->Eax, pRtlRaiseException);
235
236     /* check that context.Eip is fixed up only for EXCEPTION_BREAKPOINT
237      * even if raised by RtlRaiseException
238      */
239     if(rec->ExceptionCode == EXCEPTION_BREAKPOINT)
240     {
241         ok(context->Eip == (DWORD)code_mem + 0xa ||
242            broken(context->Eip == (DWORD)code_mem + 0xb), /* win2k3 */
243            "Eip at %x instead of %x or %x\n", context->Eip,
244            (DWORD)code_mem + 0xa, (DWORD)code_mem + 0xb);
245     }
246     else
247     {
248         ok(context->Eip == (DWORD)code_mem + 0xb, "Eip at %x instead of %x\n",
249            context->Eip, (DWORD)code_mem + 0xb);
250     }
251
252     /* test if context change is preserved from vectored handler to stack handlers */
253     context->Eax = 0xf00f00f0;
254     return EXCEPTION_CONTINUE_SEARCH;
255 }
256
257 static DWORD rtlraiseexception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
258                       CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
259 {
260     trace( "exception: %08x flags:%x addr:%p context: Eip:%x\n",
261            rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, context->Eip );
262
263     ok(rec->ExceptionAddress == (char *)code_mem + 0xb, "ExceptionAddress at %p instead of %p\n",
264        rec->ExceptionAddress, (char *)code_mem + 0xb);
265
266     /* check that context.Eip is fixed up only for EXCEPTION_BREAKPOINT
267      * even if raised by RtlRaiseException
268      */
269     if(rec->ExceptionCode == EXCEPTION_BREAKPOINT)
270     {
271         ok(context->Eip == (DWORD)code_mem + 0xa ||
272            broken(context->Eip == (DWORD)code_mem + 0xb), /* win2k3 */
273            "Eip at %x instead of %x or %x\n", context->Eip,
274            (DWORD)code_mem + 0xa, (DWORD)code_mem + 0xb);
275     }
276     else
277     {
278         ok(context->Eip == (DWORD)code_mem + 0xb, "Eip at %x instead of %x\n",
279            context->Eip, (DWORD)code_mem + 0xb);
280     }
281
282     if(have_vectored_api)
283         ok(context->Eax == 0xf00f00f0, "Eax is %x, should have been set to 0xf00f00f0 in vectored handler\n",
284            context->Eax);
285
286     /* give the debugger a chance to examine the state a second time */
287     /* without the exception handler changing Eip */
288     if (test_stage == 2)
289         return ExceptionContinueSearch;
290
291     /* Eip in context is decreased by 1
292      * Increase it again, else execution will continue in the middle of a instruction */
293     if(rec->ExceptionCode == EXCEPTION_BREAKPOINT && (context->Eip == (DWORD)code_mem + 0xa))
294         context->Eip += 1;
295     return ExceptionContinueExecution;
296 }
297
298
299 static const BYTE call_one_arg_code[] = {
300         0x8b, 0x44, 0x24, 0x08, /* mov 0x8(%esp),%eax */
301         0x50,                   /* push %eax */
302         0x8b, 0x44, 0x24, 0x08, /* mov 0x8(%esp),%eax */
303         0xff, 0xd0,             /* call *%eax */
304         0x90,                   /* nop */
305         0x90,                   /* nop */
306         0x90,                   /* nop */
307         0x90,                   /* nop */
308         0xc3,                   /* ret */
309 };
310
311
312 static void run_rtlraiseexception_test(DWORD exceptioncode)
313 {
314     EXCEPTION_REGISTRATION_RECORD frame;
315     EXCEPTION_RECORD record;
316     PVOID vectored_handler = NULL;
317
318     void (*func)(void* function, EXCEPTION_RECORD* record) = code_mem;
319
320     record.ExceptionCode = exceptioncode;
321     record.ExceptionFlags = 0;
322     record.ExceptionRecord = NULL;
323     record.ExceptionAddress = NULL; /* does not matter, copied return address */
324     record.NumberParameters = 0;
325
326     frame.Handler = rtlraiseexception_handler;
327     frame.Prev = pNtCurrentTeb()->Tib.ExceptionList;
328
329     memcpy(code_mem, call_one_arg_code, sizeof(call_one_arg_code));
330
331     pNtCurrentTeb()->Tib.ExceptionList = &frame;
332     if (have_vectored_api)
333     {
334         vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &rtlraiseexception_vectored_handler);
335         ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
336     }
337
338     func(pRtlRaiseException, &record);
339     ok( record.ExceptionAddress == (char *)code_mem + 0x0b,
340         "address set to %p instead of %p\n", record.ExceptionAddress, (char *)code_mem + 0x0b );
341
342     if (have_vectored_api)
343         pRtlRemoveVectoredExceptionHandler(vectored_handler);
344     pNtCurrentTeb()->Tib.ExceptionList = frame.Prev;
345 }
346
347 static void test_rtlraiseexception(void)
348 {
349     if (!pRtlRaiseException)
350     {
351         skip("RtlRaiseException not found\n");
352         return;
353     }
354
355     /* test without debugger */
356     run_rtlraiseexception_test(0x12345);
357     run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
358     run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
359 }
360
361 static DWORD handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
362                       CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
363 {
364     const struct exception *except = *(const struct exception **)(frame + 1);
365     unsigned int i, entry = except - exceptions;
366
367     got_exception++;
368     trace( "exception: %x flags:%x addr:%p\n",
369            rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
370
371     ok( rec->ExceptionCode == except->status,
372         "%u: Wrong exception code %x/%x\n", entry, rec->ExceptionCode, except->status );
373     ok( rec->ExceptionAddress == (char*)code_mem + except->offset,
374         "%u: Wrong exception address %p/%p\n", entry,
375         rec->ExceptionAddress, (char*)code_mem + except->offset );
376
377     ok( rec->NumberParameters == except->nb_params,
378         "%u: Wrong number of parameters %u/%u\n", entry, rec->NumberParameters, except->nb_params );
379
380     /* Most CPUs (except Intel Core apparently) report a segment limit violation */
381     /* instead of page faults for accesses beyond 0xffffffff */
382     if (except->nb_params == 2 && except->params[1] >= 0xfffffffd)
383     {
384         if (rec->ExceptionInformation[0] == 0 && rec->ExceptionInformation[1] == 0xffffffff)
385             goto skip_params;
386     }
387
388     /* Seems that both 0xbee8 and 0xfffffffff can be returned in windows */
389     if (except->nb_params == 2 && rec->NumberParameters == 2
390         && except->params[1] == 0xbee8 && rec->ExceptionInformation[1] == 0xffffffff
391         && except->params[0] == rec->ExceptionInformation[0])
392     {
393         goto skip_params;
394     }
395
396     for (i = 0; i < rec->NumberParameters; i++)
397         ok( rec->ExceptionInformation[i] == except->params[i],
398             "%u: Wrong parameter %d: %lx/%x\n",
399             entry, i, rec->ExceptionInformation[i], except->params[i] );
400
401 skip_params:
402     /* don't handle exception if it's not the address we expected */
403     if (rec->ExceptionAddress != (char*)code_mem + except->offset) return ExceptionContinueSearch;
404
405     context->Eip += except->length;
406     return ExceptionContinueExecution;
407 }
408
409 static void test_prot_fault(void)
410 {
411     unsigned int i;
412
413     for (i = 0; i < sizeof(exceptions)/sizeof(exceptions[0]); i++)
414     {
415         got_exception = 0;
416         run_exception_test(handler, &exceptions[i], &exceptions[i].code,
417                            sizeof(exceptions[i].code), 0);
418         if (!i && !got_exception)
419         {
420             trace( "No exception, assuming win9x, no point in testing further\n" );
421             break;
422         }
423         ok( got_exception == (exceptions[i].status != 0),
424             "%u: bad exception count %d\n", i, got_exception );
425     }
426 }
427
428 /* test handling of debug registers */
429 static DWORD dreg_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
430                       CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
431 {
432     context->Eip += 2;  /* Skips the popl (%eax) */
433     context->Dr0 = 0x42424242;
434     context->Dr1 = 0;
435     context->Dr2 = 0;
436     context->Dr3 = 0;
437     context->Dr6 = 0;
438     context->Dr7 = 0x155;
439     return ExceptionContinueExecution;
440 }
441
442 static const BYTE segfault_code[5] = {
443         0x31, 0xc0, /* xor    %eax,%eax */
444         0x8f, 0x00, /* popl   (%eax) - cause exception */
445         0xc3        /* ret */
446 };
447
448 /* test the single step exception behaviour */
449 static DWORD single_step_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
450                                   CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
451 {
452     got_exception++;
453     ok (!(context->EFlags & 0x100), "eflags has single stepping bit set\n");
454
455     if( got_exception < 3)
456         context->EFlags |= 0x100;  /* single step until popf instruction */
457     else {
458         /* show that the last single step exception on the popf instruction
459          * (which removed the TF bit), still is a EXCEPTION_SINGLE_STEP exception */
460         ok( rec->ExceptionCode == EXCEPTION_SINGLE_STEP,
461             "exception is not EXCEPTION_SINGLE_STEP: %x\n", rec->ExceptionCode);
462     }
463
464     return ExceptionContinueExecution;
465 }
466
467 static const BYTE single_stepcode[] = {
468     0x9c,               /* pushf */
469     0x58,               /* pop   %eax */
470     0x0d,0,1,0,0,       /* or    $0x100,%eax */
471     0x50,               /* push   %eax */
472     0x9d,               /* popf    */
473     0x35,0,1,0,0,       /* xor    $0x100,%eax */
474     0x50,               /* push   %eax */
475     0x9d,               /* popf    */
476     0xc3
477 };
478
479 /* Test the alignment check (AC) flag handling. */
480 static const BYTE align_check_code[] = {
481     0x55,                       /* push   %ebp */
482     0x89,0xe5,                  /* mov    %esp,%ebp */
483     0x9c,                       /* pushf   */
484     0x58,                       /* pop    %eax */
485     0x0d,0,0,4,0,               /* or     $0x40000,%eax */
486     0x50,                       /* push   %eax */
487     0x9d,                       /* popf    */
488     0x89,0xe0,                  /* mov %esp, %eax */
489     0x8b,0x40,0x1,              /* mov 0x1(%eax), %eax - cause exception */
490     0x9c,                       /* pushf   */
491     0x58,                       /* pop    %eax */
492     0x35,0,0,4,0,               /* xor    $0x40000,%eax */
493     0x50,                       /* push   %eax */
494     0x9d,                       /* popf    */
495     0x5d,                       /* pop    %ebp */
496     0xc3,                       /* ret     */
497 };
498
499 static DWORD align_check_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
500                                   CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
501 {
502     ok (!(context->EFlags & 0x40000), "eflags has AC bit set\n");
503     got_exception++;
504     return ExceptionContinueExecution;
505 }
506
507 /* Test the direction flag handling. */
508 static const BYTE direction_flag_code[] = {
509     0x55,                       /* push   %ebp */
510     0x89,0xe5,                  /* mov    %esp,%ebp */
511     0xfd,                       /* std */
512     0xfa,                       /* cli - cause exception */
513     0x5d,                       /* pop    %ebp */
514     0xc3,                       /* ret     */
515 };
516
517 static DWORD direction_flag_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
518                                      CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
519 {
520 #ifdef __GNUC__
521     unsigned int flags;
522     __asm__("pushfl; popl %0; cld" : "=r" (flags) );
523     /* older windows versions don't clear DF properly so don't test */
524     if (flags & 0x400) trace( "eflags has DF bit set\n" );
525 #endif
526     ok( context->EFlags & 0x400, "context eflags has DF bit cleared\n" );
527     got_exception++;
528     context->Eip++;  /* skip cli */
529     context->EFlags &= ~0x400;  /* make sure it is cleared on return */
530     return ExceptionContinueExecution;
531 }
532
533 /* test single stepping over hardware breakpoint */
534 static DWORD bpx_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
535                           CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
536 {
537     got_exception++;
538     ok( rec->ExceptionCode == EXCEPTION_SINGLE_STEP,
539         "wrong exception code: %x\n", rec->ExceptionCode);
540
541     if(got_exception == 1) {
542         /* hw bp exception on first nop */
543         ok( context->Eip == (DWORD)code_mem, "eip is wrong:  %x instead of %x\n",
544                                              context->Eip, (DWORD)code_mem);
545         ok( (context->Dr6 & 0xf) == 1, "B0 flag is not set in Dr6\n");
546         ok( !(context->Dr6 & 0x4000), "BS flag is set in Dr6\n");
547         context->Dr0 = context->Dr0 + 1;  /* set hw bp again on next instruction */
548         context->EFlags |= 0x100;       /* enable single stepping */
549     } else if(  got_exception == 2) {
550         /* single step exception on second nop */
551         ok( context->Eip == (DWORD)code_mem + 1, "eip is wrong: %x instead of %x\n",
552                                                  context->Eip, (DWORD)code_mem + 1);
553         ok( (context->Dr6 & 0x4000), "BS flag is not set in Dr6\n");
554        /* depending on the win version the B0 bit is already set here as well
555         ok( (context->Dr6 & 0xf) == 0, "B0...3 flags in Dr6 shouldn't be set\n"); */
556         context->EFlags |= 0x100;
557     } else if( got_exception == 3) {
558         /* hw bp exception on second nop */
559         ok( context->Eip == (DWORD)code_mem + 1, "eip is wrong: %x instead of %x\n",
560                                                  context->Eip, (DWORD)code_mem + 1);
561         ok( (context->Dr6 & 0xf) == 1, "B0 flag is not set in Dr6\n");
562         ok( !(context->Dr6 & 0x4000), "BS flag is set in Dr6\n");
563         context->Dr0 = 0;       /* clear breakpoint */
564         context->EFlags |= 0x100;
565     } else {
566         /* single step exception on ret */
567         ok( context->Eip == (DWORD)code_mem + 2, "eip is wrong: %x instead of %x\n",
568                                                  context->Eip, (DWORD)code_mem + 2);
569         ok( (context->Dr6 & 0xf) == 0, "B0...3 flags in Dr6 shouldn't be set\n");
570         ok( (context->Dr6 & 0x4000), "BS flag is not set in Dr6\n");
571     }
572
573     context->Dr6 = 0;  /* clear status register */
574     return ExceptionContinueExecution;
575 }
576
577 static const BYTE dummy_code[] = { 0x90, 0x90, 0xc3 };  /* nop, nop, ret */
578
579 /* test int3 handling */
580 static DWORD int3_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
581                            CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
582 {
583     ok( rec->ExceptionAddress == code_mem, "exception address not at: %p, but at %p\n",
584                                            code_mem,  rec->ExceptionAddress);
585     ok( context->Eip == (DWORD)code_mem, "eip not at: %p, but at %#x\n", code_mem, context->Eip);
586     if(context->Eip == (DWORD)code_mem) context->Eip++; /* skip breakpoint */
587
588     return ExceptionContinueExecution;
589 }
590
591 static const BYTE int3_code[] = { 0xCC, 0xc3 };  /* int 3, ret */
592
593
594 static void test_exceptions(void)
595 {
596     CONTEXT ctx;
597     NTSTATUS res;
598
599     if (!pNtGetContextThread || !pNtSetContextThread)
600     {
601         skip( "NtGetContextThread/NtSetContextThread not found\n" );
602         return;
603     }
604
605     /* test handling of debug registers */
606     run_exception_test(dreg_handler, NULL, &segfault_code, sizeof(segfault_code), 0);
607
608     ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
609     res = pNtGetContextThread(GetCurrentThread(), &ctx);
610     ok (res == STATUS_SUCCESS,"NtGetContextThread failed with %x\n", res);
611     ok(ctx.Dr0 == 0x42424242,"failed to set debugregister 0 to 0x42424242, got %x\n", ctx.Dr0);
612     ok((ctx.Dr7 & ~0xdc00) == 0x155,"failed to set debugregister 7 to 0x155, got %x\n", ctx.Dr7);
613
614     /* test single stepping behavior */
615     got_exception = 0;
616     run_exception_test(single_step_handler, NULL, &single_stepcode, sizeof(single_stepcode), 0);
617     ok(got_exception == 3, "expected 3 single step exceptions, got %d\n", got_exception);
618
619     /* test alignment exceptions */
620     got_exception = 0;
621     run_exception_test(align_check_handler, NULL, align_check_code, sizeof(align_check_code), 0);
622     ok(got_exception == 0, "got %d alignment faults, expected 0\n", got_exception);
623
624     /* test direction flag */
625     got_exception = 0;
626     run_exception_test(direction_flag_handler, NULL, direction_flag_code, sizeof(direction_flag_code), 0);
627     ok(got_exception == 1, "got %d exceptions, expected 1\n", got_exception);
628
629     /* test single stepping over hardware breakpoint */
630     memset(&ctx, 0, sizeof(ctx));
631     ctx.Dr0 = (DWORD) code_mem;  /* set hw bp on first nop */
632     ctx.Dr7 = 3;
633     ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
634     res = pNtSetContextThread( GetCurrentThread(), &ctx);
635     ok( res == STATUS_SUCCESS, "NtSetContextThread faild with %x\n", res);
636
637     got_exception = 0;
638     run_exception_test(bpx_handler, NULL, dummy_code, sizeof(dummy_code), 0);
639     ok( got_exception == 4,"expected 4 exceptions, got %d\n", got_exception);
640
641     /* test int3 handling */
642     run_exception_test(int3_handler, NULL, int3_code, sizeof(int3_code), 0);
643 }
644
645 static void test_debugger(void)
646 {
647     char cmdline[MAX_PATH];
648     PROCESS_INFORMATION pi;
649     STARTUPINFO si = { 0 };
650     DEBUG_EVENT de;
651     DWORD continuestatus;
652     PVOID code_mem_address = NULL;
653     NTSTATUS status;
654     SIZE_T size_read;
655     BOOL ret;
656     int counter = 0;
657     si.cb = sizeof(si);
658
659     if(!pNtGetContextThread || !pNtSetContextThread || !pNtReadVirtualMemory || !pNtTerminateProcess)
660     {
661         skip("NtGetContextThread, NtSetContextThread, NtReadVirtualMemory or NtTerminateProcess not found\n");
662         return;
663     }
664
665     sprintf(cmdline, "%s %s %s %p", my_argv[0], my_argv[1], "debuggee", &test_stage);
666     ret = CreateProcess(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi);
667     ok(ret, "could not create child process error: %u\n", GetLastError());
668     if (!ret)
669         return;
670
671     do
672     {
673         continuestatus = DBG_CONTINUE;
674         ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n");
675
676         if (de.dwThreadId != pi.dwThreadId)
677         {
678             trace("event %d not coming from main thread, ignoring\n", de.dwDebugEventCode);
679             ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE);
680             continue;
681         }
682
683         if (de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT)
684         {
685             if(de.u.CreateProcessInfo.lpBaseOfImage != pNtCurrentTeb()->Peb->ImageBaseAddress)
686             {
687                 skip("child process loaded at different address, terminating it\n");
688                 pNtTerminateProcess(pi.hProcess, 0);
689             }
690         }
691         else if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
692         {
693             CONTEXT ctx;
694             int stage;
695
696             counter++;
697             status = pNtReadVirtualMemory(pi.hProcess, &code_mem, &code_mem_address,
698                                           sizeof(code_mem_address), &size_read);
699             ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
700             status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
701                                           sizeof(stage), &size_read);
702             ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
703
704             ctx.ContextFlags = CONTEXT_FULL;
705             status = pNtGetContextThread(pi.hThread, &ctx);
706             ok(!status, "NtGetContextThread failed with 0x%x\n", status);
707
708             trace("exception 0x%x at %p firstchance=%d Eip=0x%x, Eax=0x%x\n",
709                   de.u.Exception.ExceptionRecord.ExceptionCode,
710                   de.u.Exception.ExceptionRecord.ExceptionAddress, de.u.Exception.dwFirstChance, ctx.Eip, ctx.Eax);
711
712             if (counter > 100)
713             {
714                 ok(FALSE, "got way too many exceptions, probaby caught in a infinite loop, terminating child\n");
715                 pNtTerminateProcess(pi.hProcess, 1);
716             }
717             else if (counter >= 2) /* skip startup breakpoint */
718             {
719                 if (stage == 1)
720                 {
721                     ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at %x instead of %p\n",
722                        ctx.Eip, (char *)code_mem_address + 0xb);
723                     /* setting the context from debugger does not affect the context, the exception handlers gets */
724                     /* uncomment once wine is fixed */
725                     /* ctx.Eip = 0x12345; */
726                     ctx.Eax = 0xf00f00f1;
727
728                     /* let the debuggee handle the exception */
729                     continuestatus = DBG_EXCEPTION_NOT_HANDLED;
730                 }
731                 else if (stage == 2)
732                 {
733                     if (de.u.Exception.dwFirstChance)
734                     {
735                         /* debugger gets first chance exception with unmodified ctx.Eip */
736                         ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at 0x%x instead of %p\n",
737                             ctx.Eip, (char *)code_mem_address + 0xb);
738
739                         /* setting the context from debugger does not affect the context, the exception handlers gets */
740                         /* uncomment once wine is fixed */
741                         /* ctx.Eip = 0x12345; */
742                         ctx.Eax = 0xf00f00f1;
743
744                         /* pass exception to debuggee
745                          * exception will not be handled and
746                          * a second chance exception will be raised */
747                         continuestatus = DBG_EXCEPTION_NOT_HANDLED;
748                     }
749                     else
750                     {
751                         /* debugger gets context after exception handler has played with it */
752                         /* ctx.Eip is the same value the exception handler got */
753                         if (de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT)
754                         {
755                             ok((char *)ctx.Eip == (char *)code_mem_address + 0xa, "Eip at 0x%x instead of %p\n",
756                                 ctx.Eip, (char *)code_mem_address + 0xa);
757                             /* need to fixup Eip for debuggee */
758                             if ((char *)ctx.Eip == (char *)code_mem_address + 0xa)
759                                 ctx.Eip += 1;
760                         }
761                         else
762                             ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at 0x%x instead of %p\n",
763                                 ctx.Eip, (char *)code_mem_address + 0xb);
764                         /* here we handle exception */
765                     }
766                 }
767                 else
768                     ok(FALSE, "unexpected stage %x\n", stage);
769
770                 status = pNtSetContextThread(pi.hThread, &ctx);
771                 ok(!status, "NtSetContextThread failed with 0x%x\n", status);
772             }
773         }
774
775         ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus);
776
777     } while (de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT);
778
779     winetest_wait_child_process( pi.hProcess );
780     ok(CloseHandle(pi.hThread) != 0, "error %u\n", GetLastError());
781     ok(CloseHandle(pi.hProcess) != 0, "error %u\n", GetLastError());
782
783     return;
784 }
785
786 static DWORD simd_fault_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
787                                  CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
788 {
789     int *stage = *(int **)(frame + 1);
790
791     got_exception++;
792
793     if( *stage == 1) {
794         /* fault while executing sse instruction */
795         context->Eip += 3; /* skip addps */
796         return ExceptionContinueExecution;
797     }
798
799     /* stage 2 - divide by zero fault */
800     if( rec->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION)
801         skip("system doesn't support SIMD exceptions\n");
802     else {
803         ok( rec->ExceptionCode ==  STATUS_FLOAT_MULTIPLE_TRAPS,
804             "exception code: %#x, should be %#x\n",
805             rec->ExceptionCode,  STATUS_FLOAT_MULTIPLE_TRAPS);
806         ok( rec->NumberParameters == 1, "# of params: %i, should be 1\n",
807             rec->NumberParameters);
808         if( rec->NumberParameters == 1 )
809             ok( rec->ExceptionInformation[0] == 0, "param #1: %lx, should be 0\n", rec->ExceptionInformation[0]);
810     }
811
812     context->Eip += 3; /* skip divps */
813
814     return ExceptionContinueExecution;
815 }
816
817 static const BYTE simd_exception_test[] = {
818     0x83, 0xec, 0x4,                     /* sub $0x4, %esp       */
819     0x0f, 0xae, 0x1c, 0x24,              /* stmxcsr (%esp)       */
820     0x66, 0x81, 0x24, 0x24, 0xff, 0xfd,  /* andw $0xfdff,(%esp)  * enable divide by */
821     0x0f, 0xae, 0x14, 0x24,              /* ldmxcsr (%esp)       * zero exceptions  */
822     0x6a, 0x01,                          /* push   $0x1          */
823     0x6a, 0x01,                          /* push   $0x1          */
824     0x6a, 0x01,                          /* push   $0x1          */
825     0x6a, 0x01,                          /* push   $0x1          */
826     0x0f, 0x10, 0x0c, 0x24,              /* movups (%esp),%xmm1  * fill dividend  */
827     0x0f, 0x57, 0xc0,                    /* xorps  %xmm0,%xmm0   * clear divisor  */
828     0x0f, 0x5e, 0xc8,                    /* divps  %xmm0,%xmm1   * generate fault */
829     0x83, 0xc4, 0x10,                    /* add    $0x10,%esp    */
830     0x66, 0x81, 0x0c, 0x24, 0x00, 0x02,  /* orw    $0x200,(%esp) * disable exceptions */
831     0x0f, 0xae, 0x14, 0x24,              /* ldmxcsr (%esp)       */
832     0x83, 0xc4, 0x04,                    /* add    $0x4,%esp     */
833     0xc3,                                /* ret */
834 };
835
836 static const BYTE sse_check[] = {
837     0x0f, 0x58, 0xc8,                    /* addps  %xmm0,%xmm1 */
838     0xc3,                                /* ret */
839 };
840
841 static void test_simd_exceptions(void)
842 {
843     int stage;
844
845     /* test if CPU & OS can do sse */
846     stage = 1;
847     got_exception = 0;
848     run_exception_test(simd_fault_handler, &stage, sse_check, sizeof(sse_check), 0);
849     if(got_exception) {
850         skip("system doesn't support SSE\n");
851         return;
852     }
853
854     /* generate a SIMD exception */
855     stage = 2;
856     got_exception = 0;
857     run_exception_test(simd_fault_handler, &stage, simd_exception_test,
858                        sizeof(simd_exception_test), 0);
859     ok( got_exception == 1, "got exception: %i, should be 1\n", got_exception);
860 }
861
862 struct fpu_exception_info
863 {
864     DWORD exception_code;
865     DWORD exception_offset;
866     DWORD eip_offset;
867 };
868
869 static DWORD fpu_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
870         CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher)
871 {
872     struct fpu_exception_info *info = *(struct fpu_exception_info **)(frame + 1);
873
874     info->exception_code = rec->ExceptionCode;
875     info->exception_offset = (BYTE *)rec->ExceptionAddress - (BYTE *)code_mem;
876     info->eip_offset = context->Eip - (DWORD)code_mem;
877
878     ++context->Eip;
879     return ExceptionContinueExecution;
880 }
881
882 static void test_fpu_exceptions(void)
883 {
884     static const BYTE fpu_exception_test_ie[] =
885     {
886         0x83, 0xec, 0x04,                   /* sub $0x4,%esp        */
887         0x66, 0xc7, 0x04, 0x24, 0xfe, 0x03, /* movw $0x3fe,(%esp)   */
888         0x9b, 0xd9, 0x7c, 0x24, 0x02,       /* fstcw 0x2(%esp)      */
889         0xd9, 0x2c, 0x24,                   /* fldcw (%esp)         */
890         0xd9, 0xee,                         /* fldz                 */
891         0xd9, 0xe8,                         /* fld1                 */
892         0xde, 0xf1,                         /* fdivp                */
893         0xdd, 0xd8,                         /* fstp %st(0)          */
894         0xdd, 0xd8,                         /* fstp %st(0)          */
895         0x9b,                               /* fwait                */
896         0xdb, 0xe2,                         /* fnclex               */
897         0xd9, 0x6c, 0x24, 0x02,             /* fldcw 0x2(%esp)      */
898         0x83, 0xc4, 0x04,                   /* add $0x4,%esp        */
899         0xc3,                               /* ret                  */
900     };
901
902     static const BYTE fpu_exception_test_de[] =
903     {
904         0x83, 0xec, 0x04,                   /* sub $0x4,%esp        */
905         0x66, 0xc7, 0x04, 0x24, 0xfb, 0x03, /* movw $0x3fb,(%esp)   */
906         0x9b, 0xd9, 0x7c, 0x24, 0x02,       /* fstcw 0x2(%esp)      */
907         0xd9, 0x2c, 0x24,                   /* fldcw (%esp)         */
908         0xdd, 0xd8,                         /* fstp %st(0)          */
909         0xd9, 0xee,                         /* fldz                 */
910         0xd9, 0xe8,                         /* fld1                 */
911         0xde, 0xf1,                         /* fdivp                */
912         0x9b,                               /* fwait                */
913         0xdb, 0xe2,                         /* fnclex               */
914         0xdd, 0xd8,                         /* fstp %st(0)          */
915         0xdd, 0xd8,                         /* fstp %st(0)          */
916         0xd9, 0x6c, 0x24, 0x02,             /* fldcw 0x2(%esp)      */
917         0x83, 0xc4, 0x04,                   /* add $0x4,%esp        */
918         0xc3,                               /* ret                  */
919     };
920
921     struct fpu_exception_info info;
922
923     memset(&info, 0, sizeof(info));
924     run_exception_test(fpu_exception_handler, &info, fpu_exception_test_ie, sizeof(fpu_exception_test_ie), 0);
925     ok(info.exception_code == EXCEPTION_FLT_STACK_CHECK,
926             "Got exception code %#x, expected EXCEPTION_FLT_STACK_CHECK\n", info.exception_code);
927     ok(info.exception_offset == 0x19, "Got exception offset %#x, expected 0x19\n", info.exception_offset);
928     ok(info.eip_offset == 0x1b, "Got EIP offset %#x, expected 0x1b\n", info.eip_offset);
929
930     memset(&info, 0, sizeof(info));
931     run_exception_test(fpu_exception_handler, &info, fpu_exception_test_de, sizeof(fpu_exception_test_de), 0);
932     ok(info.exception_code == EXCEPTION_FLT_DIVIDE_BY_ZERO,
933             "Got exception code %#x, expected EXCEPTION_FLT_DIVIDE_BY_ZERO\n", info.exception_code);
934     ok(info.exception_offset == 0x17, "Got exception offset %#x, expected 0x17\n", info.exception_offset);
935     ok(info.eip_offset == 0x19, "Got EIP offset %#x, expected 0x19\n", info.eip_offset);
936 }
937
938 struct dpe_exception_info {
939     BOOL exception_caught;
940     DWORD exception_info;
941 };
942
943 static DWORD dpe_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
944         CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher)
945 {
946     DWORD old_prot;
947     struct dpe_exception_info *info = *(struct dpe_exception_info **)(frame + 1);
948
949     ok(rec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION,
950        "Exception code %08x\n", rec->ExceptionCode);
951     ok(rec->NumberParameters == 2,
952        "Parameter count: %d\n", rec->NumberParameters);
953     ok((LPVOID)rec->ExceptionInformation[1] == code_mem,
954        "Exception address: %p, expected %p\n",
955        (LPVOID)rec->ExceptionInformation[1], code_mem);
956
957     info->exception_info = rec->ExceptionInformation[0];
958     info->exception_caught = TRUE;
959
960     VirtualProtect(code_mem, 1, PAGE_EXECUTE_READWRITE, &old_prot);
961     return ExceptionContinueExecution;
962 }
963
964 static void test_dpe_exceptions(void)
965 {
966     static char single_ret[] = {0xC3};
967     struct dpe_exception_info info;
968     NTSTATUS stat;
969     BOOL has_hw_support;
970     BOOL is_permanent = FALSE, can_test_without = TRUE, can_test_with = TRUE;
971     DWORD val;
972     ULONG len;
973
974     /* Query DEP with len to small */
975     stat = pNtQueryInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val - 1, &len);
976     if(stat == STATUS_INVALID_INFO_CLASS)
977     {
978         skip("This software platform does not support DEP\n");
979         return;
980     }
981     ok(stat == STATUS_INFO_LENGTH_MISMATCH, "buffer too small: %08x\n", stat);
982
983     /* Query DEP */
984     stat = pNtQueryInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val, &len);
985     ok(stat == STATUS_SUCCESS, "querying DEP: status %08x\n", stat);
986     if(stat == STATUS_SUCCESS)
987     {
988         ok(len == sizeof val, "returned length: %d\n", len);
989         if(val & MEM_EXECUTE_OPTION_PERMANENT)
990         {
991             skip("toggling DEP impossible - status locked\n");
992             is_permanent = TRUE;
993             if(val & MEM_EXECUTE_OPTION_DISABLE)
994                 can_test_without = FALSE;
995             else
996                 can_test_with = FALSE;
997         }
998     }
999
1000     if(!is_permanent)
1001     {
1002         /* Enable DEP */
1003         val = MEM_EXECUTE_OPTION_DISABLE;
1004         stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
1005         ok(stat == STATUS_SUCCESS, "enabling DEP: status %08x\n", stat);
1006     }
1007
1008     if(can_test_with)
1009     {
1010         /* Try access to locked page with DEP on*/
1011         info.exception_caught = FALSE;
1012         run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_NOACCESS);
1013         ok(info.exception_caught == TRUE, "Execution of disabled memory succeeded\n");
1014         ok(info.exception_info == EXCEPTION_READ_FAULT ||
1015            info.exception_info == EXCEPTION_EXECUTE_FAULT,
1016               "Access violation type: %08x\n", (unsigned)info.exception_info);
1017         has_hw_support = info.exception_info == EXCEPTION_EXECUTE_FAULT;
1018         trace("DEP hardware support: %s\n", has_hw_support?"Yes":"No");
1019
1020         /* Try execution of data with DEP on*/
1021         info.exception_caught = FALSE;
1022         run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_READWRITE);
1023         if(has_hw_support)
1024         {
1025             ok(info.exception_caught == TRUE, "Execution of data memory succeeded\n");
1026             ok(info.exception_info == EXCEPTION_EXECUTE_FAULT,
1027                   "Access violation type: %08x\n", (unsigned)info.exception_info);
1028         }
1029         else
1030             ok(info.exception_caught == FALSE, "Execution trapped without hardware support\n");
1031     }
1032     else
1033         skip("DEP is in AlwaysOff state\n");
1034
1035     if(!is_permanent)
1036     {
1037         /* Disable DEP */
1038         val = MEM_EXECUTE_OPTION_ENABLE;
1039         stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
1040         ok(stat == STATUS_SUCCESS, "disabling DEP: status %08x\n", stat);
1041     }
1042
1043     /* page is read without exec here */
1044     if(can_test_without)
1045     {
1046         /* Try execution of data with DEP off */
1047         info.exception_caught = FALSE;
1048         run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_READWRITE);
1049         ok(info.exception_caught == FALSE, "Execution trapped with DEP turned off\n");
1050
1051         /* Try access to locked page with DEP off - error code is different than
1052            with hardware DEP on */
1053         info.exception_caught = FALSE;
1054         run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_NOACCESS);
1055         ok(info.exception_caught == TRUE, "Execution of disabled memory succeeded\n");
1056         ok(info.exception_info == EXCEPTION_READ_FAULT,
1057               "Access violation type: %08x\n", (unsigned)info.exception_info);
1058     }
1059     else
1060         skip("DEP is in AlwaysOn state\n");
1061
1062     if(!is_permanent)
1063     {
1064         /* Turn off DEP permanently */
1065         val = MEM_EXECUTE_OPTION_ENABLE | MEM_EXECUTE_OPTION_PERMANENT;
1066         stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
1067         ok(stat == STATUS_SUCCESS, "disabling DEP permanently: status %08x\n", stat);
1068     }
1069
1070     /* Try to turn off DEP */
1071     val = MEM_EXECUTE_OPTION_ENABLE;
1072     stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
1073     ok(stat == STATUS_ACCESS_DENIED, "disabling DEP while permanent: status %08x\n", stat);
1074
1075     /* Try to turn on DEP */
1076     val = MEM_EXECUTE_OPTION_DISABLE;
1077     stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
1078     ok(stat == STATUS_ACCESS_DENIED, "enabling DEP while permanent: status %08x\n", stat);
1079 }
1080
1081 #elif defined(__x86_64__)
1082
1083 #define UNW_FLAG_NHANDLER  0
1084 #define UNW_FLAG_EHANDLER  1
1085 #define UNW_FLAG_UHANDLER  2
1086 #define UNW_FLAG_CHAININFO 4
1087
1088 #define UWOP_PUSH_NONVOL     0
1089 #define UWOP_ALLOC_LARGE     1
1090 #define UWOP_ALLOC_SMALL     2
1091 #define UWOP_SET_FPREG       3
1092 #define UWOP_SAVE_NONVOL     4
1093 #define UWOP_SAVE_NONVOL_FAR 5
1094 #define UWOP_SAVE_XMM128     8
1095 #define UWOP_SAVE_XMM128_FAR 9
1096 #define UWOP_PUSH_MACHFRAME  10
1097
1098 struct results
1099 {
1100     int rip_offset;   /* rip offset from code start */
1101     int rbp_offset;   /* rbp offset from stack pointer */
1102     int handler;      /* expect handler to be set? */
1103     int rip;          /* expected final rip value */
1104     int frame;        /* expected frame return value */
1105     int regs[8][2];   /* expected values for registers */
1106 };
1107
1108 struct unwind_test
1109 {
1110     const BYTE *function;
1111     size_t function_size;
1112     const BYTE *unwind_info;
1113     const struct results *results;
1114     unsigned int nb_results;
1115 };
1116
1117 enum regs
1118 {
1119     rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi,
1120     r8,  r9,  r10, r11, r12, r13, r14, r15
1121 };
1122
1123 static const char * const reg_names[16] =
1124 {
1125     "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
1126     "r8",  "r9",  "r10", "r11", "r12", "r13", "r14", "r15"
1127 };
1128
1129 #define UWOP(code,info) (UWOP_##code | ((info) << 4))
1130
1131 static void call_virtual_unwind( int testnum, const struct unwind_test *test )
1132 {
1133     static const int code_offset = 1024;
1134     static const int unwind_offset = 2048;
1135     void *handler, *data;
1136     CONTEXT context;
1137     RUNTIME_FUNCTION runtime_func;
1138     KNONVOLATILE_CONTEXT_POINTERS ctx_ptr;
1139     UINT i, j, k;
1140     ULONG64 fake_stack[256];
1141     ULONG64 frame, orig_rip, orig_rbp, unset_reg;
1142     UINT unwind_size = 4 + 2 * test->unwind_info[2] + 8;
1143
1144     memcpy( (char *)code_mem + code_offset, test->function, test->function_size );
1145     memcpy( (char *)code_mem + unwind_offset, test->unwind_info, unwind_size );
1146
1147     runtime_func.BeginAddress = code_offset;
1148     runtime_func.EndAddress = code_offset + test->function_size;
1149     runtime_func.UnwindData = unwind_offset;
1150
1151     trace( "code: %p stack: %p\n", code_mem, fake_stack );
1152
1153     for (i = 0; i < test->nb_results; i++)
1154     {
1155         memset( &ctx_ptr, 0, sizeof(ctx_ptr) );
1156         memset( &context, 0x55, sizeof(context) );
1157         memset( &unset_reg, 0x55, sizeof(unset_reg) );
1158         for (j = 0; j < 256; j++) fake_stack[j] = j * 8;
1159
1160         context.Rsp = (ULONG_PTR)fake_stack;
1161         context.Rbp = (ULONG_PTR)fake_stack + test->results[i].rbp_offset;
1162         orig_rbp = context.Rbp;
1163         orig_rip = (ULONG64)code_mem + code_offset + test->results[i].rip_offset;
1164
1165         trace( "%u/%u: rip=%p (%02x) rbp=%p rsp=%p\n", testnum, i,
1166                (void *)orig_rip, *(BYTE *)orig_rip, (void *)orig_rbp, (void *)context.Rsp );
1167
1168         data = (void *)0xdeadbeef;
1169         handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG64)code_mem, orig_rip,
1170                                     &runtime_func, &context, &data, &frame, &ctx_ptr );
1171         if (test->results[i].handler)
1172         {
1173             ok( (char *)handler == (char *)code_mem + 0x200,
1174                 "%u/%u: wrong handler %p/%p\n", testnum, i, handler, (char *)code_mem + 0x200 );
1175             if (handler) ok( *(DWORD *)data == 0x08070605,
1176                              "%u/%u: wrong handler data %p\n", testnum, i, data );
1177         }
1178         else
1179         {
1180             ok( handler == NULL, "%u/%u: handler %p instead of NULL\n", testnum, i, handler );
1181             ok( data == (void *)0xdeadbeef, "%u/%u: handler data set to %p\n", testnum, i, data );
1182         }
1183
1184         ok( context.Rip == test->results[i].rip, "%u/%u: wrong rip %p/%x\n",
1185             testnum, i, (void *)context.Rip, test->results[i].rip );
1186         ok( frame == (ULONG64)fake_stack + test->results[i].frame, "%u/%u: wrong frame %p/%p\n",
1187             testnum, i, (void *)frame, (char *)fake_stack + test->results[i].frame );
1188
1189         for (j = 0; j < 16; j++)
1190         {
1191             static const UINT nb_regs = sizeof(test->results[i].regs) / sizeof(test->results[i].regs[0]);
1192
1193             for (k = 0; k < nb_regs; k++)
1194             {
1195                 if (test->results[i].regs[k][0] == -1)
1196                 {
1197                     k = nb_regs;
1198                     break;
1199                 }
1200                 if (test->results[i].regs[k][0] == j) break;
1201             }
1202
1203             if (j == rsp)  /* rsp is special */
1204             {
1205                 ok( !ctx_ptr.u2.IntegerContext[j],
1206                     "%u/%u: rsp should not be set in ctx_ptr\n", testnum, i );
1207                 ok( context.Rsp == (ULONG64)fake_stack + test->results[i].regs[k][1],
1208                     "%u/%u: register rsp wrong %p/%p\n",
1209                     testnum, i, (void *)context.Rsp, (char *)fake_stack + test->results[i].regs[k][1] );
1210                 continue;
1211             }
1212
1213             if (ctx_ptr.u2.IntegerContext[j])
1214             {
1215                 ok( k < nb_regs, "%u/%u: register %s should not be set to %lx\n",
1216                     testnum, i, reg_names[j], *(&context.Rax + j) );
1217                 if (k < nb_regs)
1218                     ok( *(&context.Rax + j) == test->results[i].regs[k][1],
1219                         "%u/%u: register %s wrong %p/%x\n",
1220                         testnum, i, reg_names[j], (void *)*(&context.Rax + j), test->results[i].regs[k][1] );
1221             }
1222             else
1223             {
1224                 ok( k == nb_regs, "%u/%u: register %s should be set\n", testnum, i, reg_names[j] );
1225                 if (j == rbp)
1226                     ok( context.Rbp == orig_rbp, "%u/%u: register rbp wrong %p/unset\n",
1227                         testnum, i, (void *)context.Rbp );
1228                 else
1229                     ok( *(&context.Rax + j) == unset_reg,
1230                         "%u/%u: register %s wrong %p/unset\n",
1231                         testnum, i, reg_names[j], (void *)*(&context.Rax + j));
1232             }
1233         }
1234     }
1235 }
1236
1237 static void test_virtual_unwind(void)
1238 {
1239     static const BYTE function_0[] =
1240     {
1241         0xff, 0xf5,                                  /* 00: push %rbp */
1242         0x48, 0x81, 0xec, 0x10, 0x01, 0x00, 0x00,    /* 02: sub $0x110,%rsp */
1243         0x48, 0x8d, 0x6c, 0x24, 0x30,                /* 09: lea 0x30(%rsp),%rbp */
1244         0x48, 0x89, 0x9d, 0xf0, 0x00, 0x00, 0x00,    /* 0e: mov %rbx,0xf0(%rbp) */
1245         0x48, 0x89, 0xb5, 0xf8, 0x00, 0x00, 0x00,    /* 15: mov %rsi,0xf8(%rbp) */
1246         0x90,                                        /* 1c: nop */
1247         0x48, 0x8b, 0x9d, 0xf0, 0x00, 0x00, 0x00,    /* 1d: mov 0xf0(%rbp),%rbx */
1248         0x48, 0x8b, 0xb5, 0xf8, 0x00, 0x00, 0x00,    /* 24: mov 0xf8(%rbp),%rsi */
1249         0x48, 0x8d, 0xa5, 0xe0, 0x00, 0x00, 0x00,    /* 2b: lea 0xe0(%rbp),%rsp */
1250         0x5d,                                        /* 32: pop %rbp */
1251         0xc3                                         /* 33: ret */
1252     };
1253
1254     static const BYTE unwind_info_0[] =
1255     {
1256         1 | (UNW_FLAG_EHANDLER << 3),  /* version + flags */
1257         0x1c,                          /* prolog size */
1258         8,                             /* opcode count */
1259         (0x03 << 4) | rbp,             /* frame reg rbp offset 0x30 */
1260
1261         0x1c, UWOP(SAVE_NONVOL, rsi), 0x25, 0, /* 1c: mov %rsi,0x128(%rsp) */
1262         0x15, UWOP(SAVE_NONVOL, rbx), 0x24, 0, /* 15: mov %rbx,0x120(%rsp) */
1263         0x0e, UWOP(SET_FPREG, rbp),            /* 0e: lea 0x30(%rsp),rbp */
1264         0x09, UWOP(ALLOC_LARGE, 0), 0x22, 0,   /* 09: sub $0x110,%rsp */
1265         0x02, UWOP(PUSH_NONVOL, rbp),          /* 02: push %rbp */
1266
1267         0x00, 0x02, 0x00, 0x00,  /* handler */
1268         0x05, 0x06, 0x07, 0x08,  /* data */
1269     };
1270
1271     static const struct results results_0[] =
1272     {
1273       /* offset  rbp   handler  rip   frame   registers */
1274         { 0x00,  0x40,  FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
1275         { 0x02,  0x40,  FALSE, 0x008, 0x000, { {rsp,0x010}, {rbp,0x000}, {-1,-1} }},
1276         { 0x09,  0x40,  FALSE, 0x118, 0x000, { {rsp,0x120}, {rbp,0x110}, {-1,-1} }},
1277         { 0x0e,  0x40,  FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1} }},
1278         { 0x15,  0x40,  FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {-1,-1} }},
1279         { 0x1c,  0x40,  TRUE,  0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
1280         { 0x1d,  0x40,  TRUE,  0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
1281         { 0x24,  0x40,  TRUE,  0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
1282         { 0x2b,  0x40,  FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1}}},
1283         { 0x32,  0x40,  FALSE, 0x008, 0x010, { {rsp,0x010}, {rbp,0x000}, {-1,-1}}},
1284         { 0x33,  0x40,  FALSE, 0x000, 0x010, { {rsp,0x008}, {-1,-1}}},
1285     };
1286
1287
1288     static const BYTE function_1[] =
1289     {
1290         0x53,                     /* 00: push %rbx */
1291         0x55,                     /* 01: push %rbp */
1292         0x56,                     /* 02: push %rsi */
1293         0x57,                     /* 03: push %rdi */
1294         0x41, 0x54,               /* 04: push %r12 */
1295         0x48, 0x83, 0xec, 0x30,   /* 06: sub $0x30,%rsp */
1296         0x90, 0x90,               /* 0a: nop; nop */
1297         0x48, 0x83, 0xc4, 0x30,   /* 0c: add $0x30,%rsp */
1298         0x41, 0x5c,               /* 10: pop %r12 */
1299         0x5f,                     /* 12: pop %rdi */
1300         0x5e,                     /* 13: pop %rsi */
1301         0x5d,                     /* 14: pop %rbp */
1302         0x5b,                     /* 15: pop %rbx */
1303         0xc3                      /* 16: ret */
1304      };
1305
1306     static const BYTE unwind_info_1[] =
1307     {
1308         1 | (UNW_FLAG_EHANDLER << 3),  /* version + flags */
1309         0x0a,                          /* prolog size */
1310         6,                             /* opcode count */
1311         0,                             /* frame reg */
1312
1313         0x0a, UWOP(ALLOC_SMALL, 5),   /* 0a: sub $0x30,%rsp */
1314         0x06, UWOP(PUSH_NONVOL, r12), /* 06: push %r12 */
1315         0x04, UWOP(PUSH_NONVOL, rdi), /* 04: push %rdi */
1316         0x03, UWOP(PUSH_NONVOL, rsi), /* 03: push %rsi */
1317         0x02, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */
1318         0x01, UWOP(PUSH_NONVOL, rbx), /* 01: push %rbx */
1319
1320         0x00, 0x02, 0x00, 0x00,  /* handler */
1321         0x05, 0x06, 0x07, 0x08,  /* data */
1322     };
1323
1324     static const struct results results_1[] =
1325     {
1326       /* offset  rbp   handler  rip   frame   registers */
1327         { 0x00,  0x50,  FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
1328         { 0x01,  0x50,  FALSE, 0x008, 0x000, { {rsp,0x010}, {rbx,0x000}, {-1,-1} }},
1329         { 0x02,  0x50,  FALSE, 0x010, 0x000, { {rsp,0x018}, {rbx,0x008}, {rbp,0x000}, {-1,-1} }},
1330         { 0x03,  0x50,  FALSE, 0x018, 0x000, { {rsp,0x020}, {rbx,0x010}, {rbp,0x008}, {rsi,0x000}, {-1,-1} }},
1331         { 0x04,  0x50,  FALSE, 0x020, 0x000, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }},
1332         { 0x06,  0x50,  FALSE, 0x028, 0x000, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }},
1333         { 0x0a,  0x50,  TRUE,  0x058, 0x000, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }},
1334         { 0x0c,  0x50,  FALSE, 0x058, 0x000, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }},
1335         { 0x10,  0x50,  FALSE, 0x028, 0x000, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }},
1336         { 0x12,  0x50,  FALSE, 0x020, 0x000, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }},
1337         { 0x13,  0x50,  FALSE, 0x018, 0x000, { {rsp,0x020}, {rbx,0x010}, {rbp,0x008}, {rsi,0x000}, {-1,-1} }},
1338         { 0x14,  0x50,  FALSE, 0x010, 0x000, { {rsp,0x018}, {rbx,0x008}, {rbp,0x000}, {-1,-1} }},
1339         { 0x15,  0x50,  FALSE, 0x008, 0x000, { {rsp,0x010}, {rbx,0x000}, {-1,-1} }},
1340         { 0x16,  0x50,  FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
1341     };
1342
1343     static const struct unwind_test tests[] =
1344     {
1345         { function_0, sizeof(function_0), unwind_info_0,
1346           results_0, sizeof(results_0)/sizeof(results_0[0]) },
1347         { function_1, sizeof(function_1), unwind_info_1,
1348           results_1, sizeof(results_1)/sizeof(results_1[0]) }
1349     };
1350     unsigned int i;
1351
1352     for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++)
1353         call_virtual_unwind( i, &tests[i] );
1354 }
1355
1356 #endif  /* __x86_64__ */
1357
1358 START_TEST(exception)
1359 {
1360     HMODULE hntdll = GetModuleHandleA("ntdll.dll");
1361
1362     code_mem = VirtualAlloc(NULL, 65536, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
1363     if(!code_mem) {
1364         trace("VirtualAlloc failed\n");
1365         return;
1366     }
1367
1368     pNtCurrentTeb        = (void *)GetProcAddress( hntdll, "NtCurrentTeb" );
1369     pNtGetContextThread  = (void *)GetProcAddress( hntdll, "NtGetContextThread" );
1370     pNtSetContextThread  = (void *)GetProcAddress( hntdll, "NtSetContextThread" );
1371     pNtReadVirtualMemory = (void *)GetProcAddress( hntdll, "NtReadVirtualMemory" );
1372     pRtlRaiseException   = (void *)GetProcAddress( hntdll, "RtlRaiseException" );
1373     pNtTerminateProcess  = (void *)GetProcAddress( hntdll, "NtTerminateProcess" );
1374     pRtlAddVectoredExceptionHandler    = (void *)GetProcAddress( hntdll,
1375                                                                  "RtlAddVectoredExceptionHandler" );
1376     pRtlRemoveVectoredExceptionHandler = (void *)GetProcAddress( hntdll,
1377                                                                  "RtlRemoveVectoredExceptionHandler" );
1378     pNtQueryInformationProcess         = (void*)GetProcAddress( hntdll,
1379                                                                  "NtQueryInformationProcess" );
1380     pNtSetInformationProcess           = (void*)GetProcAddress( hntdll,
1381                                                                  "NtSetInformationProcess" );
1382
1383 #ifdef __i386__
1384     if (!pNtCurrentTeb)
1385     {
1386         skip( "NtCurrentTeb not found\n" );
1387         return;
1388     }
1389
1390     if (pRtlAddVectoredExceptionHandler && pRtlRemoveVectoredExceptionHandler)
1391         have_vectored_api = TRUE;
1392     else
1393         skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n");
1394
1395     my_argc = winetest_get_mainargs( &my_argv );
1396     if (my_argc >= 4)
1397     {
1398         void *addr;
1399         sscanf( my_argv[3], "%p", &addr );
1400
1401         if (addr != &test_stage)
1402         {
1403             skip( "child process not mapped at same address (%p/%p)\n", &test_stage, addr);
1404             return;
1405         }
1406
1407         /* child must be run under a debugger */
1408         if (!pNtCurrentTeb()->Peb->BeingDebugged)
1409         {
1410             ok(FALSE, "child process not being debugged?\n");
1411             return;
1412         }
1413
1414         if (pRtlRaiseException)
1415         {
1416             test_stage = 1;
1417             run_rtlraiseexception_test(0x12345);
1418             run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
1419             run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
1420             test_stage = 2;
1421             run_rtlraiseexception_test(0x12345);
1422             run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
1423             run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
1424         }
1425         else
1426             skip( "RtlRaiseException not found\n" );
1427
1428         /* rest of tests only run in parent */
1429         return;
1430     }
1431
1432     test_prot_fault();
1433     test_exceptions();
1434     test_rtlraiseexception();
1435     test_debugger();
1436     test_simd_exceptions();
1437     test_fpu_exceptions();
1438     test_dpe_exceptions();
1439
1440 #elif defined(__x86_64__)
1441
1442     test_virtual_unwind();
1443
1444 #endif
1445
1446     VirtualFree(code_mem, 0, MEM_FREE);
1447 }