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 "wine/winternl.h"
37 #include "wine/exception.h"
38 #include "wine/test.h"
39 
40 static void *code_mem;
41 
42 static NTSTATUS  (WINAPI *pNtGetContextThread)(HANDLE,CONTEXT*);
43 static NTSTATUS  (WINAPI *pNtSetContextThread)(HANDLE,CONTEXT*);
44 static NTSTATUS  (WINAPI *pRtlRaiseException)(EXCEPTION_RECORD *rec);
45 static PVOID     (WINAPI *pRtlUnwind)(PVOID, PVOID, PEXCEPTION_RECORD, PVOID);
46 static VOID      (WINAPI *pRtlCaptureContext)(CONTEXT*);
47 static PVOID     (WINAPI *pRtlAddVectoredExceptionHandler)(ULONG first, PVECTORED_EXCEPTION_HANDLER func);
48 static ULONG     (WINAPI *pRtlRemoveVectoredExceptionHandler)(PVOID handler);
49 static PVOID     (WINAPI *pRtlAddVectoredContinueHandler)(ULONG first, PVECTORED_EXCEPTION_HANDLER func);
50 static ULONG     (WINAPI *pRtlRemoveVectoredContinueHandler)(PVOID handler);
51 static NTSTATUS  (WINAPI *pNtReadVirtualMemory)(HANDLE, const void*, void*, SIZE_T, SIZE_T*);
52 static NTSTATUS  (WINAPI *pNtTerminateProcess)(HANDLE handle, LONG exit_code);
53 static NTSTATUS  (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
54 static NTSTATUS  (WINAPI *pNtSetInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG);
55 static BOOL      (WINAPI *pIsWow64Process)(HANDLE, PBOOL);
56 static NTSTATUS  (WINAPI *pNtClose)(HANDLE);
57 
58 #if defined(__x86_64__)
59 typedef struct
60 {
61     ULONG Count;
62     struct
63     {
64         ULONG BeginAddress;
65         ULONG EndAddress;
66         ULONG HandlerAddress;
67         ULONG JumpTarget;
68     } ScopeRecord[1];
69 } SCOPE_TABLE;
70 
71 typedef struct
72 {
73     ULONG64               ControlPc;
74     ULONG64               ImageBase;
75     PRUNTIME_FUNCTION     FunctionEntry;
76     ULONG64               EstablisherFrame;
77     ULONG64               TargetIp;
78     PCONTEXT              ContextRecord;
79     void* /*PEXCEPTION_ROUTINE*/ LanguageHandler;
80     PVOID                 HandlerData;
81     PUNWIND_HISTORY_TABLE HistoryTable;
82     ULONG                 ScopeIndex;
83 } DISPATCHER_CONTEXT;
84 
85 typedef struct _SETJMP_FLOAT128
86 {
87     unsigned __int64 DECLSPEC_ALIGN(16) Part[2];
88 } SETJMP_FLOAT128;
89 
90 typedef struct _JUMP_BUFFER
91 {
92     unsigned __int64 Frame;
93     unsigned __int64 Rbx;
94     unsigned __int64 Rsp;
95     unsigned __int64 Rbp;
96     unsigned __int64 Rsi;
97     unsigned __int64 Rdi;
98     unsigned __int64 R12;
99     unsigned __int64 R13;
100     unsigned __int64 R14;
101     unsigned __int64 R15;
102     unsigned __int64 Rip;
103     unsigned __int64 Spare;
104     SETJMP_FLOAT128  Xmm6;
105     SETJMP_FLOAT128  Xmm7;
106     SETJMP_FLOAT128  Xmm8;
107     SETJMP_FLOAT128  Xmm9;
108     SETJMP_FLOAT128  Xmm10;
109     SETJMP_FLOAT128  Xmm11;
110     SETJMP_FLOAT128  Xmm12;
111     SETJMP_FLOAT128  Xmm13;
112     SETJMP_FLOAT128  Xmm14;
113     SETJMP_FLOAT128  Xmm15;
114 } _JUMP_BUFFER;
115 
116 static BOOLEAN   (CDECL *pRtlAddFunctionTable)(RUNTIME_FUNCTION*, DWORD, DWORD64);
117 static BOOLEAN   (CDECL *pRtlDeleteFunctionTable)(RUNTIME_FUNCTION*);
118 static BOOLEAN   (CDECL *pRtlInstallFunctionTableCallback)(DWORD64, DWORD64, DWORD, PGET_RUNTIME_FUNCTION_CALLBACK, PVOID, PCWSTR);
119 static PRUNTIME_FUNCTION (WINAPI *pRtlLookupFunctionEntry)(ULONG64, ULONG64*, UNWIND_HISTORY_TABLE*);
120 static EXCEPTION_DISPOSITION (WINAPI *p__C_specific_handler)(EXCEPTION_RECORD*, ULONG64, CONTEXT*, DISPATCHER_CONTEXT*);
121 static VOID      (WINAPI *pRtlCaptureContext)(CONTEXT*);
122 static VOID      (CDECL *pRtlRestoreContext)(CONTEXT*, EXCEPTION_RECORD*);
123 static VOID      (CDECL *pRtlUnwindEx)(VOID*, VOID*, EXCEPTION_RECORD*, VOID*, CONTEXT*, UNWIND_HISTORY_TABLE*);
124 static int       (CDECL *p_setjmp)(_JUMP_BUFFER*);
125 #endif
126 
127 #ifdef __i386__
128 
129 #ifndef __WINE_WINTRNL_H
130 #define ProcessExecuteFlags 0x22
131 #define MEM_EXECUTE_OPTION_DISABLE   0x01
132 #define MEM_EXECUTE_OPTION_ENABLE    0x02
133 #define MEM_EXECUTE_OPTION_PERMANENT 0x08
134 #endif
135 
136 static int      my_argc;
137 static char**   my_argv;
138 static int      test_stage;
139 
140 static BOOL is_wow64;
141 
142 /* Test various instruction combinations that cause a protection fault on the i386,
143  * and check what the resulting exception looks like.
144  */
145 
146 static const struct exception
147 {
148     BYTE     code[18];      /* asm code */
149     BYTE     offset;        /* offset of faulting instruction */
150     BYTE     length;        /* length of faulting instruction */
151     BOOL     wow64_broken;  /* broken on Wow64, should be skipped */
152     NTSTATUS status;        /* expected status code */
153     DWORD    nb_params;     /* expected number of parameters */
154     DWORD    params[4];     /* expected parameters */
155     NTSTATUS alt_status;    /* alternative status code */
156     DWORD    alt_nb_params; /* alternative number of parameters */
157     DWORD    alt_params[4]; /* alternative parameters */
158 } exceptions[] =
159 {
160 /* 0 */
161     /* test some privileged instructions */
162     { { 0xfb, 0xc3 },  /* 0: sti; ret */
163       0, 1, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
164     { { 0x6c, 0xc3 },  /* 1: insb (%dx); ret */
165       0, 1, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
166     { { 0x6d, 0xc3 },  /* 2: insl (%dx); ret */
167       0, 1, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
168     { { 0x6e, 0xc3 },  /* 3: outsb (%dx); ret */
169       0, 1, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
170     { { 0x6f, 0xc3 },  /* 4: outsl (%dx); ret */
171       0, 1, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
172 /* 5 */
173     { { 0xe4, 0x11, 0xc3 },  /* 5: inb $0x11,%al; ret */
174       0, 2, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
175     { { 0xe5, 0x11, 0xc3 },  /* 6: inl $0x11,%eax; ret */
176       0, 2, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
177     { { 0xe6, 0x11, 0xc3 },  /* 7: outb %al,$0x11; ret */
178       0, 2, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
179     { { 0xe7, 0x11, 0xc3 },  /* 8: outl %eax,$0x11; ret */
180       0, 2, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
181     { { 0xed, 0xc3 },  /* 9: inl (%dx),%eax; ret */
182       0, 1, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
183 /* 10 */
184     { { 0xee, 0xc3 },  /* 10: outb %al,(%dx); ret */
185       0, 1, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
186     { { 0xef, 0xc3 },  /* 11: outl %eax,(%dx); ret */
187       0, 1, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
188     { { 0xf4, 0xc3 },  /* 12: hlt; ret */
189       0, 1, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
190     { { 0xfa, 0xc3 },  /* 13: cli; ret */
191       0, 1, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
192 
193     /* test long jump to invalid selector */
194     { { 0xea, 0, 0, 0, 0, 0, 0, 0xc3 },  /* 14: ljmp $0,$0; ret */
195       0, 7, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
196 
197 /* 15 */
198     /* test iret to invalid selector */
199     { { 0x6a, 0x00, 0x6a, 0x00, 0x6a, 0x00, 0xcf, 0x83, 0xc4, 0x0c, 0xc3 },
200       /* 15: pushl $0; pushl $0; pushl $0; iret; addl $12,%esp; ret */
201       6, 1, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
202 
203     /* test loading an invalid selector */
204     { { 0xb8, 0xef, 0xbe, 0x00, 0x00, 0x8e, 0xe8, 0xc3 },  /* 16: mov $beef,%ax; mov %ax,%gs; ret */
205       5, 2, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xbee8 } }, /* 0xbee8 or 0xffffffff */
206 
207     /* test accessing a zero selector (%es broken on Wow64) */
208     { { 0x06, 0x31, 0xc0, 0x8e, 0xc0, 0x26, 0xa1, 0, 0, 0, 0, 0x07, 0xc3 },
209        /* push %es; xor %eax,%eax; mov %ax,%es; mov %es:(0),%ax; pop %es; ret */
210       5, 6, TRUE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
211     { { 0x0f, 0xa8, 0x31, 0xc0, 0x8e, 0xe8, 0x65, 0xa1, 0, 0, 0, 0, 0x0f, 0xa9, 0xc3 },
212       /* push %gs; xor %eax,%eax; mov %ax,%gs; mov %gs:(0),%ax; pop %gs; ret */
213       6, 6, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
214 
215     /* test moving %cs -> %ss */
216     { { 0x0e, 0x17, 0x58, 0xc3 },  /* pushl %cs; popl %ss; popl %eax; ret */
217       1, 1, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
218 
219 /* 20 */
220     /* test overlong instruction (limit is 15 bytes, 5 on Win7) */
221     { { 0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0xfa,0xc3 },
222       0, 16, TRUE, STATUS_ILLEGAL_INSTRUCTION, 0, { 0 },
223       STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
224     { { 0x64,0x64,0x64,0x64,0xfa,0xc3 },
225       0, 5, TRUE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
226 
227     /* test invalid interrupt */
228     { { 0xcd, 0xff, 0xc3 },   /* int $0xff; ret */
229       0, 2, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
230 
231     /* test moves to/from Crx */
232     { { 0x0f, 0x20, 0xc0, 0xc3 },  /* movl %cr0,%eax; ret */
233       0, 3, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
234     { { 0x0f, 0x20, 0xe0, 0xc3 },  /* movl %cr4,%eax; ret */
235       0, 3, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
236 /* 25 */
237     { { 0x0f, 0x22, 0xc0, 0xc3 },  /* movl %eax,%cr0; ret */
238       0, 3, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
239     { { 0x0f, 0x22, 0xe0, 0xc3 },  /* movl %eax,%cr4; ret */
240       0, 3, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
241 
242     /* test moves to/from Drx */
243     { { 0x0f, 0x21, 0xc0, 0xc3 },  /* movl %dr0,%eax; ret */
244       0, 3, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
245     { { 0x0f, 0x21, 0xc8, 0xc3 },  /* movl %dr1,%eax; ret */
246       0, 3, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
247     { { 0x0f, 0x21, 0xf8, 0xc3 },  /* movl %dr7,%eax; ret */
248       0, 3, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
249 /* 30 */
250     { { 0x0f, 0x23, 0xc0, 0xc3 },  /* movl %eax,%dr0; ret */
251       0, 3, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
252     { { 0x0f, 0x23, 0xc8, 0xc3 },  /* movl %eax,%dr1; ret */
253       0, 3, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
254     { { 0x0f, 0x23, 0xf8, 0xc3 },  /* movl %eax,%dr7; ret */
255       0, 3, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
256 
257     /* test memory reads */
258     { { 0xa1, 0xfc, 0xff, 0xff, 0xff, 0xc3 },  /* movl 0xfffffffc,%eax; ret */
259       0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffc } },
260     { { 0xa1, 0xfd, 0xff, 0xff, 0xff, 0xc3 },  /* movl 0xfffffffd,%eax; ret */
261       0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffd } },
262 /* 35 */
263     { { 0xa1, 0xfe, 0xff, 0xff, 0xff, 0xc3 },  /* movl 0xfffffffe,%eax; ret */
264       0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffe } },
265     { { 0xa1, 0xff, 0xff, 0xff, 0xff, 0xc3 },  /* movl 0xffffffff,%eax; ret */
266       0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
267 
268     /* test memory writes */
269     { { 0xa3, 0xfc, 0xff, 0xff, 0xff, 0xc3 },  /* movl %eax,0xfffffffc; ret */
270       0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffc } },
271     { { 0xa3, 0xfd, 0xff, 0xff, 0xff, 0xc3 },  /* movl %eax,0xfffffffd; ret */
272       0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffd } },
273     { { 0xa3, 0xfe, 0xff, 0xff, 0xff, 0xc3 },  /* movl %eax,0xfffffffe; ret */
274       0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffe } },
275 /* 40 */
276     { { 0xa3, 0xff, 0xff, 0xff, 0xff, 0xc3 },  /* movl %eax,0xffffffff; ret */
277       0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0xffffffff } },
278 
279     /* test exception with cleared %ds and %es (broken on Wow64) */
280     { { 0x1e, 0x06, 0x31, 0xc0, 0x8e, 0xd8, 0x8e, 0xc0, 0xfa, 0x07, 0x1f, 0xc3 },
281           /* push %ds; push %es; xorl %eax,%eax; mov %ax,%ds; mov %ax,%es; cli; pop %es; pop %ds; ret */
282       8, 1, TRUE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
283 
284     { { 0xf1, 0x90, 0xc3 },  /* icebp; nop; ret */
285       1, 1, FALSE, STATUS_SINGLE_STEP, 0 },
286     { { 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,          /* mov $0xb8b8b8b8, %eax */
287         0xb9, 0xb9, 0xb9, 0xb9, 0xb9,          /* mov $0xb9b9b9b9, %ecx */
288         0xba, 0xba, 0xba, 0xba, 0xba,          /* mov $0xbabababa, %edx */
289         0xcd, 0x2d, 0xc3 },                    /* int $0x2d; ret */
290       17, 0, FALSE, STATUS_BREAKPOINT, 3, { 0xb8b8b8b8, 0xb9b9b9b9, 0xbabababa } },
291 };
292 
293 static int got_exception;
294 static BOOL have_vectored_api;
295 
296 static void run_exception_test(void *handler, const void* context,
297                                const void *code, unsigned int code_size,
298                                DWORD access)
299 {
300     struct {
301         EXCEPTION_REGISTRATION_RECORD frame;
302         const void *context;
303     } exc_frame;
304     void (*func)(void) = code_mem;
305     DWORD oldaccess, oldaccess2;
306 
307     exc_frame.frame.Handler = handler;
308     exc_frame.frame.Prev = NtCurrentTeb()->Tib.ExceptionList;
309     exc_frame.context = context;
310 
311     memcpy(code_mem, code, code_size);
312     if(access)
313         VirtualProtect(code_mem, code_size, access, &oldaccess);
314 
315     NtCurrentTeb()->Tib.ExceptionList = &exc_frame.frame;
316     func();
317     NtCurrentTeb()->Tib.ExceptionList = exc_frame.frame.Prev;
318 
319     if(access)
320         VirtualProtect(code_mem, code_size, oldaccess, &oldaccess2);
321 }
322 
323 static LONG CALLBACK rtlraiseexception_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
324 {
325     PCONTEXT context = ExceptionInfo->ContextRecord;
326     PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
327     trace("vect. handler %08x addr:%p context.Eip:%x\n", rec->ExceptionCode,
328           rec->ExceptionAddress, context->Eip);
329 
330     ok(rec->ExceptionAddress == (char *)code_mem + 0xb, "ExceptionAddress at %p instead of %p\n",
331        rec->ExceptionAddress, (char *)code_mem + 0xb);
332 
333     if (NtCurrentTeb()->Peb->BeingDebugged)
334         ok((void *)context->Eax == pRtlRaiseException ||
335            broken( is_wow64 && context->Eax == 0xf00f00f1 ), /* broken on vista */
336            "debugger managed to modify Eax to %x should be %p\n",
337            context->Eax, pRtlRaiseException);
338 
339     /* check that context.Eip is fixed up only for EXCEPTION_BREAKPOINT
340      * even if raised by RtlRaiseException
341      */
342     if(rec->ExceptionCode == EXCEPTION_BREAKPOINT)
343     {
344         ok(context->Eip == (DWORD)code_mem + 0xa ||
345            broken(context->Eip == (DWORD)code_mem + 0xb), /* win2k3 */
346            "Eip at %x instead of %x or %x\n", context->Eip,
347            (DWORD)code_mem + 0xa, (DWORD)code_mem + 0xb);
348     }
349     else
350     {
351         ok(context->Eip == (DWORD)code_mem + 0xb, "Eip at %x instead of %x\n",
352            context->Eip, (DWORD)code_mem + 0xb);
353     }
354 
355     /* test if context change is preserved from vectored handler to stack handlers */
356     context->Eax = 0xf00f00f0;
357     return EXCEPTION_CONTINUE_SEARCH;
358 }
359 
360 static DWORD rtlraiseexception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
361                       CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
362 {
363     trace( "exception: %08x flags:%x addr:%p context: Eip:%x\n",
364            rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, context->Eip );
365 
366     ok(rec->ExceptionAddress == (char *)code_mem + 0xb, "ExceptionAddress at %p instead of %p\n",
367        rec->ExceptionAddress, (char *)code_mem + 0xb);
368 
369     /* check that context.Eip is fixed up only for EXCEPTION_BREAKPOINT
370      * even if raised by RtlRaiseException
371      */
372     if(rec->ExceptionCode == EXCEPTION_BREAKPOINT)
373     {
374         ok(context->Eip == (DWORD)code_mem + 0xa ||
375            broken(context->Eip == (DWORD)code_mem + 0xb), /* win2k3 */
376            "Eip at %x instead of %x or %x\n", context->Eip,
377            (DWORD)code_mem + 0xa, (DWORD)code_mem + 0xb);
378     }
379     else
380     {
381         ok(context->Eip == (DWORD)code_mem + 0xb, "Eip at %x instead of %x\n",
382            context->Eip, (DWORD)code_mem + 0xb);
383     }
384 
385     if(have_vectored_api)
386         ok(context->Eax == 0xf00f00f0, "Eax is %x, should have been set to 0xf00f00f0 in vectored handler\n",
387            context->Eax);
388 
389     /* give the debugger a chance to examine the state a second time */
390     /* without the exception handler changing Eip */
391     if (test_stage == 2)
392         return ExceptionContinueSearch;
393 
394     /* Eip in context is decreased by 1
395      * Increase it again, else execution will continue in the middle of an instruction */
396     if(rec->ExceptionCode == EXCEPTION_BREAKPOINT && (context->Eip == (DWORD)code_mem + 0xa))
397         context->Eip += 1;
398     return ExceptionContinueExecution;
399 }
400 
401 
402 static const BYTE call_one_arg_code[] = {
403         0x8b, 0x44, 0x24, 0x08, /* mov 0x8(%esp),%eax */
404         0x50,                   /* push %eax */
405         0x8b, 0x44, 0x24, 0x08, /* mov 0x8(%esp),%eax */
406         0xff, 0xd0,             /* call *%eax */
407         0x90,                   /* nop */
408         0x90,                   /* nop */
409         0x90,                   /* nop */
410         0x90,                   /* nop */
411         0xc3,                   /* ret */
412 };
413 
414 
415 static void run_rtlraiseexception_test(DWORD exceptioncode)
416 {
417     EXCEPTION_REGISTRATION_RECORD frame;
418     EXCEPTION_RECORD record;
419     PVOID vectored_handler = NULL;
420 
421     void (*func)(void* function, EXCEPTION_RECORD* record) = code_mem;
422 
423     record.ExceptionCode = exceptioncode;
424     record.ExceptionFlags = 0;
425     record.ExceptionRecord = NULL;
426     record.ExceptionAddress = NULL; /* does not matter, copied return address */
427     record.NumberParameters = 0;
428 
429     frame.Handler = rtlraiseexception_handler;
430     frame.Prev = NtCurrentTeb()->Tib.ExceptionList;
431 
432     memcpy(code_mem, call_one_arg_code, sizeof(call_one_arg_code));
433 
434     NtCurrentTeb()->Tib.ExceptionList = &frame;
435     if (have_vectored_api)
436     {
437         vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &rtlraiseexception_vectored_handler);
438         ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
439     }
440 
441     func(pRtlRaiseException, &record);
442     ok( record.ExceptionAddress == (char *)code_mem + 0x0b,
443         "address set to %p instead of %p\n", record.ExceptionAddress, (char *)code_mem + 0x0b );
444 
445     if (have_vectored_api)
446         pRtlRemoveVectoredExceptionHandler(vectored_handler);
447     NtCurrentTeb()->Tib.ExceptionList = frame.Prev;
448 }
449 
450 static void test_rtlraiseexception(void)
451 {
452     if (!pRtlRaiseException)
453     {
454         skip("RtlRaiseException not found\n");
455         return;
456     }
457 
458     /* test without debugger */
459     run_rtlraiseexception_test(0x12345);
460     run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
461     run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
462 }
463 
464 static DWORD unwind_expected_eax;
465 
466 static DWORD unwind_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
467                              CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
468 {
469     trace("exception: %08x flags:%x addr:%p context: Eip:%x\n",
470           rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, context->Eip);
471 
472     ok(rec->ExceptionCode == STATUS_UNWIND, "ExceptionCode is %08x instead of %08x\n",
473        rec->ExceptionCode, STATUS_UNWIND);
474     ok(rec->ExceptionAddress == (char *)code_mem + 0x22, "ExceptionAddress at %p instead of %p\n",
475        rec->ExceptionAddress, (char *)code_mem + 0x22);
476     ok(context->Eax == unwind_expected_eax, "context->Eax is %08x instead of %08x\n",
477        context->Eax, unwind_expected_eax);
478 
479     context->Eax += 1;
480     return ExceptionContinueSearch;
481 }
482 
483 static const BYTE call_unwind_code[] = {
484     0x55,                           /* push %ebp */
485     0x53,                           /* push %ebx */
486     0x56,                           /* push %esi */
487     0x57,                           /* push %edi */
488     0xe8, 0x00, 0x00, 0x00, 0x00,   /* call 0 */
489     0x58,                           /* 0: pop %eax */
490     0x05, 0x1e, 0x00, 0x00, 0x00,   /* add $0x1e,%eax */
491     0xff, 0x74, 0x24, 0x20,         /* push 0x20(%esp) */
492     0xff, 0x74, 0x24, 0x20,         /* push 0x20(%esp) */
493     0x50,                           /* push %eax */
494     0xff, 0x74, 0x24, 0x24,         /* push 0x24(%esp) */
495     0x8B, 0x44, 0x24, 0x24,         /* mov 0x24(%esp),%eax */
496     0xff, 0xd0,                     /* call *%eax */
497     0x5f,                           /* pop %edi */
498     0x5e,                           /* pop %esi */
499     0x5b,                           /* pop %ebx */
500     0x5d,                           /* pop %ebp */
501     0xc3,                           /* ret */
502     0xcc,                           /* int $3 */
503 };
504 
505 static void test_unwind(void)
506 {
507     EXCEPTION_REGISTRATION_RECORD frames[2], *frame2 = &frames[0], *frame1 = &frames[1];
508     DWORD (*func)(void* function, EXCEPTION_REGISTRATION_RECORD *pEndFrame, EXCEPTION_RECORD* record, DWORD retval) = code_mem;
509     DWORD retval;
510 
511     memcpy(code_mem, call_unwind_code, sizeof(call_unwind_code));
512 
513     /* add first unwind handler */
514     frame1->Handler = unwind_handler;
515     frame1->Prev = NtCurrentTeb()->Tib.ExceptionList;
516     NtCurrentTeb()->Tib.ExceptionList = frame1;
517 
518     /* add second unwind handler */
519     frame2->Handler = unwind_handler;
520     frame2->Prev = NtCurrentTeb()->Tib.ExceptionList;
521     NtCurrentTeb()->Tib.ExceptionList = frame2;
522 
523     /* test unwind to current frame */
524     unwind_expected_eax = 0xDEAD0000;
525     retval = func(pRtlUnwind, frame2, NULL, 0xDEAD0000);
526     ok(retval == 0xDEAD0000, "RtlUnwind returned eax %08x instead of %08x\n", retval, 0xDEAD0000);
527     ok(NtCurrentTeb()->Tib.ExceptionList == frame2, "Exception record points to %p instead of %p\n",
528        NtCurrentTeb()->Tib.ExceptionList, frame2);
529 
530     /* unwind to frame1 */
531     unwind_expected_eax = 0xDEAD0000;
532     retval = func(pRtlUnwind, frame1, NULL, 0xDEAD0000);
533     ok(retval == 0xDEAD0001, "RtlUnwind returned eax %08x instead of %08x\n", retval, 0xDEAD0001);
534     ok(NtCurrentTeb()->Tib.ExceptionList == frame1, "Exception record points to %p instead of %p\n",
535        NtCurrentTeb()->Tib.ExceptionList, frame1);
536 
537     /* restore original handler */
538     NtCurrentTeb()->Tib.ExceptionList = frame1->Prev;
539 }
540 
541 static DWORD handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
542                       CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
543 {
544     const struct exception *except = *(const struct exception **)(frame + 1);
545     unsigned int i, parameter_count, entry = except - exceptions;
546 
547     got_exception++;
548     trace( "exception %u: %x flags:%x addr:%p\n",
549            entry, rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
550 
551     ok( rec->ExceptionCode == except->status ||
552         (except->alt_status != 0 && rec->ExceptionCode == except->alt_status),
553         "%u: Wrong exception code %x/%x\n", entry, rec->ExceptionCode, except->status );
554     ok( context->Eip == (DWORD_PTR)code_mem + except->offset,
555         "%u: Unexpected eip %#x/%#lx\n", entry,
556         context->Eip, (DWORD_PTR)code_mem + except->offset );
557     ok( rec->ExceptionAddress == (char*)context->Eip ||
558         (rec->ExceptionCode == STATUS_BREAKPOINT && rec->ExceptionAddress == (char*)context->Eip + 1),
559         "%u: Unexpected exception address %p/%p\n", entry,
560         rec->ExceptionAddress, (char*)context->Eip );
561 
562     if (except->status == STATUS_BREAKPOINT && is_wow64)
563         parameter_count = 1;
564     else if (except->alt_status == 0 || rec->ExceptionCode != except->alt_status)
565         parameter_count = except->nb_params;
566     else
567         parameter_count = except->alt_nb_params;
568 
569     ok( rec->NumberParameters == parameter_count,
570         "%u: Unexpected parameter count %u/%u\n", entry, rec->NumberParameters, parameter_count );
571 
572     /* Most CPUs (except Intel Core apparently) report a segment limit violation */
573     /* instead of page faults for accesses beyond 0xffffffff */
574     if (except->nb_params == 2 && except->params[1] >= 0xfffffffd)
575     {
576         if (rec->ExceptionInformation[0] == 0 && rec->ExceptionInformation[1] == 0xffffffff)
577             goto skip_params;
578     }
579 
580     /* Seems that both 0xbee8 and 0xfffffffff can be returned in windows */
581     if (except->nb_params == 2 && rec->NumberParameters == 2
582         && except->params[1] == 0xbee8 && rec->ExceptionInformation[1] == 0xffffffff
583         && except->params[0] == rec->ExceptionInformation[0])
584     {
585         goto skip_params;
586     }
587 
588     if (except->alt_status == 0 || rec->ExceptionCode != except->alt_status)
589     {
590         for (i = 0; i < rec->NumberParameters; i++)
591             ok( rec->ExceptionInformation[i] == except->params[i],
592                 "%u: Wrong parameter %d: %lx/%x\n",
593                 entry, i, rec->ExceptionInformation[i], except->params[i] );
594     }
595     else
596     {
597         for (i = 0; i < rec->NumberParameters; i++)
598             ok( rec->ExceptionInformation[i] == except->alt_params[i],
599                 "%u: Wrong parameter %d: %lx/%x\n",
600                 entry, i, rec->ExceptionInformation[i], except->alt_params[i] );
601     }
602 
603 skip_params:
604     /* don't handle exception if it's not the address we expected */
605     if (context->Eip != (DWORD_PTR)code_mem + except->offset) return ExceptionContinueSearch;
606 
607     context->Eip += except->length;
608     return ExceptionContinueExecution;
609 }
610 
611 static void test_prot_fault(void)
612 {
613     unsigned int i;
614 
615     for (i = 0; i < sizeof(exceptions)/sizeof(exceptions[0]); i++)
616     {
617         if (is_wow64 && exceptions[i].wow64_broken && !strcmp( winetest_platform, "windows" ))
618         {
619             skip( "Exception %u broken on Wow64\n", i );
620             continue;
621         }
622         got_exception = 0;
623         run_exception_test(handler, &exceptions[i], &exceptions[i].code,
624                            sizeof(exceptions[i].code), 0);
625         if (!i && !got_exception)
626         {
627             trace( "No exception, assuming win9x, no point in testing further\n" );
628             break;
629         }
630         ok( got_exception == (exceptions[i].status != 0),
631             "%u: bad exception count %d\n", i, got_exception );
632     }
633 }
634 
635 struct dbgreg_test {
636     DWORD dr0, dr1, dr2, dr3, dr6, dr7;
637 };
638 
639 /* test handling of debug registers */
640 static DWORD dreg_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
641                       CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
642 {
643     const struct dbgreg_test *test = *(const struct dbgreg_test **)(frame + 1);
644 
645     context->Eip += 2;	/* Skips the popl (%eax) */
646     context->Dr0 = test->dr0;
647     context->Dr1 = test->dr1;
648     context->Dr2 = test->dr2;
649     context->Dr3 = test->dr3;
650     context->Dr6 = test->dr6;
651     context->Dr7 = test->dr7;
652     return ExceptionContinueExecution;
653 }
654 
655 #define CHECK_DEBUG_REG(n, m) \
656     ok((ctx.Dr##n & m) == test->dr##n, "(%d) failed to set debug register " #n " to %x, got %x\n", \
657        test_num, test->dr##n, ctx.Dr##n)
658 
659 static void check_debug_registers(int test_num, const struct dbgreg_test *test)
660 {
661     CONTEXT ctx;
662     NTSTATUS status;
663 
664     ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
665     status = pNtGetContextThread(GetCurrentThread(), &ctx);
666     ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %x\n", status);
667 
668     CHECK_DEBUG_REG(0, ~0);
669     CHECK_DEBUG_REG(1, ~0);
670     CHECK_DEBUG_REG(2, ~0);
671     CHECK_DEBUG_REG(3, ~0);
672     CHECK_DEBUG_REG(6, 0x0f);
673     CHECK_DEBUG_REG(7, ~0xdc00);
674 }
675 
676 static const BYTE segfault_code[5] = {
677 	0x31, 0xc0, /* xor    %eax,%eax */
678 	0x8f, 0x00, /* popl   (%eax) - cause exception */
679         0xc3        /* ret */
680 };
681 
682 /* test the single step exception behaviour */
683 static DWORD single_step_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
684                                   CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
685 {
686     got_exception++;
687     ok (!(context->EFlags & 0x100), "eflags has single stepping bit set\n");
688 
689     if( got_exception < 3)
690         context->EFlags |= 0x100;  /* single step until popf instruction */
691     else {
692         /* show that the last single step exception on the popf instruction
693          * (which removed the TF bit), still is a EXCEPTION_SINGLE_STEP exception */
694         ok( rec->ExceptionCode == EXCEPTION_SINGLE_STEP,
695             "exception is not EXCEPTION_SINGLE_STEP: %x\n", rec->ExceptionCode);
696     }
697 
698     return ExceptionContinueExecution;
699 }
700 
701 static const BYTE single_stepcode[] = {
702     0x9c,		/* pushf */
703     0x58,		/* pop   %eax */
704     0x0d,0,1,0,0,	/* or    $0x100,%eax */
705     0x50,		/* push   %eax */
706     0x9d,		/* popf    */
707     0x35,0,1,0,0,	/* xor    $0x100,%eax */
708     0x50,		/* push   %eax */
709     0x9d,		/* popf    */
710     0xc3
711 };
712 
713 /* Test the alignment check (AC) flag handling. */
714 static const BYTE align_check_code[] = {
715     0x55,                  	/* push   %ebp */
716     0x89,0xe5,             	/* mov    %esp,%ebp */
717     0x9c,                  	/* pushf   */
718     0x58,                  	/* pop    %eax */
719     0x0d,0,0,4,0,       	/* or     $0x40000,%eax */
720     0x50,                  	/* push   %eax */
721     0x9d,                  	/* popf    */
722     0x89,0xe0,                  /* mov %esp, %eax */
723     0x8b,0x40,0x1,              /* mov 0x1(%eax), %eax - cause exception */
724     0x9c,                  	/* pushf   */
725     0x58,                  	/* pop    %eax */
726     0x35,0,0,4,0,       	/* xor    $0x40000,%eax */
727     0x50,                  	/* push   %eax */
728     0x9d,                  	/* popf    */
729     0x5d,                  	/* pop    %ebp */
730     0xc3,                  	/* ret     */
731 };
732 
733 static DWORD align_check_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
734                                   CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
735 {
736     ok (!(context->EFlags & 0x40000), "eflags has AC bit set\n");
737     got_exception++;
738     return ExceptionContinueExecution;
739 }
740 
741 /* Test the direction flag handling. */
742 static const BYTE direction_flag_code[] = {
743     0x55,                  	/* push   %ebp */
744     0x89,0xe5,             	/* mov    %esp,%ebp */
745     0xfd,                  	/* std */
746     0xfa,                  	/* cli - cause exception */
747     0x5d,                  	/* pop    %ebp */
748     0xc3,                  	/* ret     */
749 };
750 
751 static DWORD direction_flag_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
752                                      CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
753 {
754 #ifdef __GNUC__
755     unsigned int flags;
756     __asm__("pushfl; popl %0; cld" : "=r" (flags) );
757     /* older windows versions don't clear DF properly so don't test */
758     if (flags & 0x400) trace( "eflags has DF bit set\n" );
759 #endif
760     ok( context->EFlags & 0x400, "context eflags has DF bit cleared\n" );
761     got_exception++;
762     context->Eip++;  /* skip cli */
763     context->EFlags &= ~0x400;  /* make sure it is cleared on return */
764     return ExceptionContinueExecution;
765 }
766 
767 /* test single stepping over hardware breakpoint */
768 static DWORD bpx_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
769                           CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
770 {
771     got_exception++;
772     ok( rec->ExceptionCode == EXCEPTION_SINGLE_STEP,
773         "wrong exception code: %x\n", rec->ExceptionCode);
774 
775     if(got_exception == 1) {
776         /* hw bp exception on first nop */
777         ok( context->Eip == (DWORD)code_mem, "eip is wrong:  %x instead of %x\n",
778                                              context->Eip, (DWORD)code_mem);
779         ok( (context->Dr6 & 0xf) == 1, "B0 flag is not set in Dr6\n");
780         ok( !(context->Dr6 & 0x4000), "BS flag is set in Dr6\n");
781         context->Dr0 = context->Dr0 + 1;  /* set hw bp again on next instruction */
782         context->EFlags |= 0x100;       /* enable single stepping */
783     } else if(  got_exception == 2) {
784         /* single step exception on second nop */
785         ok( context->Eip == (DWORD)code_mem + 1, "eip is wrong: %x instead of %x\n",
786                                                  context->Eip, (DWORD)code_mem + 1);
787         ok( (context->Dr6 & 0x4000), "BS flag is not set in Dr6\n");
788        /* depending on the win version the B0 bit is already set here as well
789         ok( (context->Dr6 & 0xf) == 0, "B0...3 flags in Dr6 shouldn't be set\n"); */
790         context->EFlags |= 0x100;
791     } else if( got_exception == 3) {
792         /* hw bp exception on second nop */
793         ok( context->Eip == (DWORD)code_mem + 1, "eip is wrong: %x instead of %x\n",
794                                                  context->Eip, (DWORD)code_mem + 1);
795         ok( (context->Dr6 & 0xf) == 1, "B0 flag is not set in Dr6\n");
796         ok( !(context->Dr6 & 0x4000), "BS flag is set in Dr6\n");
797         context->Dr0 = 0;       /* clear breakpoint */
798         context->EFlags |= 0x100;
799     } else {
800         /* single step exception on ret */
801         ok( context->Eip == (DWORD)code_mem + 2, "eip is wrong: %x instead of %x\n",
802                                                  context->Eip, (DWORD)code_mem + 2);
803         ok( (context->Dr6 & 0xf) == 0, "B0...3 flags in Dr6 shouldn't be set\n");
804         ok( (context->Dr6 & 0x4000), "BS flag is not set in Dr6\n");
805     }
806 
807     context->Dr6 = 0;  /* clear status register */
808     return ExceptionContinueExecution;
809 }
810 
811 static const BYTE dummy_code[] = { 0x90, 0x90, 0xc3 };  /* nop, nop, ret */
812 
813 /* test int3 handling */
814 static DWORD int3_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
815                            CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
816 {
817     ok( rec->ExceptionAddress == code_mem, "exception address not at: %p, but at %p\n",
818                                            code_mem,  rec->ExceptionAddress);
819     ok( context->Eip == (DWORD)code_mem, "eip not at: %p, but at %#x\n", code_mem, context->Eip);
820     if(context->Eip == (DWORD)code_mem) context->Eip++; /* skip breakpoint */
821 
822     return ExceptionContinueExecution;
823 }
824 
825 static const BYTE int3_code[] = { 0xCC, 0xc3 };  /* int 3, ret */
826 
827 static DWORD WINAPI hw_reg_exception_thread( void *arg )
828 {
829     int expect = (ULONG_PTR)arg;
830     got_exception = 0;
831     run_exception_test( bpx_handler, NULL, dummy_code, sizeof(dummy_code), 0 );
832     ok( got_exception == expect, "expected %u exceptions, got %d\n", expect, got_exception );
833     return 0;
834 }
835 
836 static void test_exceptions(void)
837 {
838     CONTEXT ctx;
839     NTSTATUS res;
840     struct dbgreg_test dreg_test;
841     HANDLE h;
842 
843     if (!pNtGetContextThread || !pNtSetContextThread)
844     {
845         skip( "NtGetContextThread/NtSetContextThread not found\n" );
846         return;
847     }
848 
849     /* test handling of debug registers */
850     memset(&dreg_test, 0, sizeof(dreg_test));
851 
852     dreg_test.dr0 = 0x42424240;
853     dreg_test.dr2 = 0x126bb070;
854     dreg_test.dr3 = 0x0badbad0;
855     dreg_test.dr7 = 0xffff0115;
856     run_exception_test(dreg_handler, &dreg_test, &segfault_code, sizeof(segfault_code), 0);
857     check_debug_registers(1, &dreg_test);
858 
859     dreg_test.dr0 = 0x42424242;
860     dreg_test.dr2 = 0x100f0fe7;
861     dreg_test.dr3 = 0x0abebabe;
862     dreg_test.dr7 = 0x115;
863     run_exception_test(dreg_handler, &dreg_test, &segfault_code, sizeof(segfault_code), 0);
864     check_debug_registers(2, &dreg_test);
865 
866     /* test single stepping behavior */
867     got_exception = 0;
868     run_exception_test(single_step_handler, NULL, &single_stepcode, sizeof(single_stepcode), 0);
869     ok(got_exception == 3, "expected 3 single step exceptions, got %d\n", got_exception);
870 
871     /* test alignment exceptions */
872     got_exception = 0;
873     run_exception_test(align_check_handler, NULL, align_check_code, sizeof(align_check_code), 0);
874     ok(got_exception == 0, "got %d alignment faults, expected 0\n", got_exception);
875 
876     /* test direction flag */
877     got_exception = 0;
878     run_exception_test(direction_flag_handler, NULL, direction_flag_code, sizeof(direction_flag_code), 0);
879     ok(got_exception == 1, "got %d exceptions, expected 1\n", got_exception);
880 
881     /* test single stepping over hardware breakpoint */
882     memset(&ctx, 0, sizeof(ctx));
883     ctx.Dr0 = (DWORD) code_mem;  /* set hw bp on first nop */
884     ctx.Dr7 = 3;
885     ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
886     res = pNtSetContextThread( GetCurrentThread(), &ctx);
887     ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", res);
888 
889     got_exception = 0;
890     run_exception_test(bpx_handler, NULL, dummy_code, sizeof(dummy_code), 0);
891     ok( got_exception == 4,"expected 4 exceptions, got %d\n", got_exception);
892 
893     /* test int3 handling */
894     run_exception_test(int3_handler, NULL, int3_code, sizeof(int3_code), 0);
895 
896     /* test that hardware breakpoints are not inherited by created threads */
897     res = pNtSetContextThread( GetCurrentThread(), &ctx );
898     ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", res );
899 
900     h = CreateThread( NULL, 0, hw_reg_exception_thread, 0, 0, NULL );
901     WaitForSingleObject( h, 10000 );
902     CloseHandle( h );
903 
904     h = CreateThread( NULL, 0, hw_reg_exception_thread, (void *)4, CREATE_SUSPENDED, NULL );
905     ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
906     res = pNtGetContextThread( h, &ctx );
907     ok( res == STATUS_SUCCESS, "NtGetContextThread failed with %x\n", res );
908     ok( ctx.Dr0 == 0, "dr0 %x\n", ctx.Dr0 );
909     ok( ctx.Dr7 == 0, "dr7 %x\n", ctx.Dr7 );
910     ctx.Dr0 = (DWORD)code_mem;
911     ctx.Dr7 = 3;
912     res = pNtSetContextThread( h, &ctx );
913     ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", res );
914     ResumeThread( h );
915     WaitForSingleObject( h, 10000 );
916     CloseHandle( h );
917 
918     ctx.Dr0 = 0;
919     ctx.Dr7 = 0;
920     res = pNtSetContextThread( GetCurrentThread(), &ctx );
921     ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", res );
922 }
923 
924 static void test_debugger(void)
925 {
926     char cmdline[MAX_PATH];
927     PROCESS_INFORMATION pi;
928     STARTUPINFOA si = { 0 };
929     DEBUG_EVENT de;
930     DWORD continuestatus;
931     PVOID code_mem_address = NULL;
932     NTSTATUS status;
933     SIZE_T size_read;
934     BOOL ret;
935     int counter = 0;
936     si.cb = sizeof(si);
937 
938     if(!pNtGetContextThread || !pNtSetContextThread || !pNtReadVirtualMemory || !pNtTerminateProcess)
939     {
940         skip("NtGetContextThread, NtSetContextThread, NtReadVirtualMemory or NtTerminateProcess not found\n");
941         return;
942     }
943 
944     sprintf(cmdline, "%s %s %s %p", my_argv[0], my_argv[1], "debuggee", &test_stage);
945     ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi);
946     ok(ret, "could not create child process error: %u\n", GetLastError());
947     if (!ret)
948         return;
949 
950     do
951     {
952         continuestatus = DBG_CONTINUE;
953         ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n");
954 
955         if (de.dwThreadId != pi.dwThreadId)
956         {
957             trace("event %d not coming from main thread, ignoring\n", de.dwDebugEventCode);
958             ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE);
959             continue;
960         }
961 
962         if (de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT)
963         {
964             if(de.u.CreateProcessInfo.lpBaseOfImage != NtCurrentTeb()->Peb->ImageBaseAddress)
965             {
966                 skip("child process loaded at different address, terminating it\n");
967                 pNtTerminateProcess(pi.hProcess, 0);
968             }
969         }
970         else if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
971         {
972             CONTEXT ctx;
973             int stage;
974 
975             counter++;
976             status = pNtReadVirtualMemory(pi.hProcess, &code_mem, &code_mem_address,
977                                           sizeof(code_mem_address), &size_read);
978             ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
979             status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
980                                           sizeof(stage), &size_read);
981             ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
982 
983             ctx.ContextFlags = CONTEXT_FULL;
984             status = pNtGetContextThread(pi.hThread, &ctx);
985             ok(!status, "NtGetContextThread failed with 0x%x\n", status);
986 
987             trace("exception 0x%x at %p firstchance=%d Eip=0x%x, Eax=0x%x\n",
988                   de.u.Exception.ExceptionRecord.ExceptionCode,
989                   de.u.Exception.ExceptionRecord.ExceptionAddress, de.u.Exception.dwFirstChance, ctx.Eip, ctx.Eax);
990 
991             if (counter > 100)
992             {
993                 ok(FALSE, "got way too many exceptions, probably caught in an infinite loop, terminating child\n");
994                 pNtTerminateProcess(pi.hProcess, 1);
995             }
996             else if (counter >= 2) /* skip startup breakpoint */
997             {
998                 if (stage == 1)
999                 {
1000                     ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at %x instead of %p\n",
1001                        ctx.Eip, (char *)code_mem_address + 0xb);
1002                     /* setting the context from debugger does not affect the context, the exception handlers gets */
1003                     /* uncomment once wine is fixed */
1004                     /* ctx.Eip = 0x12345; */
1005                     ctx.Eax = 0xf00f00f1;
1006 
1007                     /* let the debuggee handle the exception */
1008                     continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1009                 }
1010                 else if (stage == 2)
1011                 {
1012                     if (de.u.Exception.dwFirstChance)
1013                     {
1014                         /* debugger gets first chance exception with unmodified ctx.Eip */
1015                         ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at 0x%x instead of %p\n",
1016                             ctx.Eip, (char *)code_mem_address + 0xb);
1017 
1018                         /* setting the context from debugger does not affect the context, the exception handlers gets */
1019                         /* uncomment once wine is fixed */
1020                         /* ctx.Eip = 0x12345; */
1021                         ctx.Eax = 0xf00f00f1;
1022 
1023                         /* pass exception to debuggee
1024                          * exception will not be handled and
1025                          * a second chance exception will be raised */
1026                         continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1027                     }
1028                     else
1029                     {
1030                         /* debugger gets context after exception handler has played with it */
1031                         /* ctx.Eip is the same value the exception handler got */
1032                         if (de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT)
1033                         {
1034                             ok((char *)ctx.Eip == (char *)code_mem_address + 0xa ||
1035                                broken(is_wow64 && (char *)ctx.Eip == (char *)code_mem_address + 0xb),
1036                                "Eip at 0x%x instead of %p\n",
1037                                 ctx.Eip, (char *)code_mem_address + 0xa);
1038                             /* need to fixup Eip for debuggee */
1039                             if ((char *)ctx.Eip == (char *)code_mem_address + 0xa)
1040                                 ctx.Eip += 1;
1041                         }
1042                         else
1043                             ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at 0x%x instead of %p\n",
1044                                 ctx.Eip, (char *)code_mem_address + 0xb);
1045                         /* here we handle exception */
1046                     }
1047                 }
1048                 else if (stage == 7 || stage == 8)
1049                 {
1050                     ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT,
1051                        "expected EXCEPTION_BREAKPOINT, got %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode);
1052                     ok((char *)ctx.Eip == (char *)code_mem_address + 0x1d,
1053                        "expected Eip = %p, got 0x%x\n", (char *)code_mem_address + 0x1d, ctx.Eip);
1054 
1055                     if (stage == 8) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1056                 }
1057                 else if (stage == 9 || stage == 10)
1058                 {
1059                     ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT,
1060                        "expected EXCEPTION_BREAKPOINT, got %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode);
1061                     ok((char *)ctx.Eip == (char *)code_mem_address + 2,
1062                        "expected Eip = %p, got 0x%x\n", (char *)code_mem_address + 2, ctx.Eip);
1063 
1064                     if (stage == 10) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1065                 }
1066                 else if (stage == 11 || stage == 12)
1067                 {
1068                     ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_INVALID_HANDLE,
1069                        "unexpected exception code %08x, expected %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode,
1070                        EXCEPTION_INVALID_HANDLE);
1071                     ok(de.u.Exception.ExceptionRecord.NumberParameters == 0,
1072                        "unexpected number of parameters %d, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters);
1073 
1074                     if (stage == 12) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1075                 }
1076                 else
1077                     ok(FALSE, "unexpected stage %x\n", stage);
1078 
1079                 status = pNtSetContextThread(pi.hThread, &ctx);
1080                 ok(!status, "NtSetContextThread failed with 0x%x\n", status);
1081             }
1082         }
1083         else if (de.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT)
1084         {
1085             int stage;
1086 #ifdef __REACTOS__
1087             /* This will catch our DPRINTs, such as
1088              * "WARNING:  RtlpDphTargetDllsLogicInitialize at ..\..\lib\rtl\heappage.c:1283 is UNIMPLEMENTED!"
1089              * so we need a full-size buffer to avoid a stack overflow
1090              */
1091             char buffer[513];
1092 #else
1093             char buffer[64];
1094 #endif
1095 
1096             status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
1097                                           sizeof(stage), &size_read);
1098             ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
1099 
1100             ok(!de.u.DebugString.fUnicode, "unexpected unicode debug string event\n");
1101             ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) - 1, "buffer not large enough to hold %d bytes\n",
1102                de.u.DebugString.nDebugStringLength);
1103 
1104             memset(buffer, 0, sizeof(buffer));
1105             status = pNtReadVirtualMemory(pi.hProcess, de.u.DebugString.lpDebugStringData, buffer,
1106                                           de.u.DebugString.nDebugStringLength, &size_read);
1107             ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
1108 
1109             if (stage == 3 || stage == 4)
1110                 ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer);
1111             else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */
1112                 ok(strstr(buffer, "SHIMVIEW") != NULL, "unexpected stage %x, got debug string event '%s'\n", stage, buffer);
1113 
1114             if (stage == 4) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1115         }
1116         else if (de.dwDebugEventCode == RIP_EVENT)
1117         {
1118             int stage;
1119 
1120             status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
1121                                           sizeof(stage), &size_read);
1122             ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
1123 
1124             if (stage == 5 || stage == 6)
1125             {
1126                 ok(de.u.RipInfo.dwError == 0x11223344, "got unexpected rip error code %08x, expected %08x\n",
1127                    de.u.RipInfo.dwError, 0x11223344);
1128                 ok(de.u.RipInfo.dwType  == 0x55667788, "got unexpected rip type %08x, expected %08x\n",
1129                    de.u.RipInfo.dwType, 0x55667788);
1130             }
1131             else
1132                 ok(FALSE, "unexpected stage %x\n", stage);
1133 
1134             if (stage == 6) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1135         }
1136 
1137         ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus);
1138 
1139     } while (de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT);
1140 
1141     winetest_wait_child_process( pi.hProcess );
1142     ret = CloseHandle(pi.hThread);
1143     ok(ret, "error %u\n", GetLastError());
1144     ret = CloseHandle(pi.hProcess);
1145     ok(ret, "error %u\n", GetLastError());
1146 
1147     return;
1148 }
1149 
1150 static DWORD simd_fault_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
1151                                  CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
1152 {
1153     int *stage = *(int **)(frame + 1);
1154 
1155     got_exception++;
1156 
1157     if( *stage == 1) {
1158         /* fault while executing sse instruction */
1159         context->Eip += 3; /* skip addps */
1160         return ExceptionContinueExecution;
1161     }
1162     else if ( *stage == 2 || *stage == 3 ) {
1163         /* stage 2 - divide by zero fault */
1164         /* stage 3 - invalid operation fault */
1165         if( rec->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION)
1166             skip("system doesn't support SIMD exceptions\n");
1167         else {
1168             ok( rec->ExceptionCode ==  STATUS_FLOAT_MULTIPLE_TRAPS,
1169                 "exception code: %#x, should be %#x\n",
1170                 rec->ExceptionCode,  STATUS_FLOAT_MULTIPLE_TRAPS);
1171             ok( rec->NumberParameters == 1 || broken(is_wow64 && rec->NumberParameters == 2),
1172                 "# of params: %i, should be 1\n",
1173                 rec->NumberParameters);
1174             if( rec->NumberParameters == 1 )
1175                 ok( rec->ExceptionInformation[0] == 0, "param #1: %lx, should be 0\n", rec->ExceptionInformation[0]);
1176         }
1177         context->Eip += 3; /* skip divps */
1178     }
1179     else
1180         ok(FALSE, "unexpected stage %x\n", *stage);
1181 
1182     return ExceptionContinueExecution;
1183 }
1184 
1185 static const BYTE simd_exception_test[] = {
1186     0x83, 0xec, 0x4,                     /* sub $0x4, %esp       */
1187     0x0f, 0xae, 0x1c, 0x24,              /* stmxcsr (%esp)       */
1188     0x8b, 0x04, 0x24,                    /* mov    (%esp),%eax   * store mxcsr */
1189     0x66, 0x81, 0x24, 0x24, 0xff, 0xfd,  /* andw $0xfdff,(%esp)  * enable divide by */
1190     0x0f, 0xae, 0x14, 0x24,              /* ldmxcsr (%esp)       * zero exceptions  */
1191     0x6a, 0x01,                          /* push   $0x1          */
1192     0x6a, 0x01,                          /* push   $0x1          */
1193     0x6a, 0x01,                          /* push   $0x1          */
1194     0x6a, 0x01,                          /* push   $0x1          */
1195     0x0f, 0x10, 0x0c, 0x24,              /* movups (%esp),%xmm1  * fill dividend  */
1196     0x0f, 0x57, 0xc0,                    /* xorps  %xmm0,%xmm0   * clear divisor  */
1197     0x0f, 0x5e, 0xc8,                    /* divps  %xmm0,%xmm1   * generate fault */
1198     0x83, 0xc4, 0x10,                    /* add    $0x10,%esp    */
1199     0x89, 0x04, 0x24,                    /* mov    %eax,(%esp)   * restore to old mxcsr */
1200     0x0f, 0xae, 0x14, 0x24,              /* ldmxcsr (%esp)       */
1201     0x83, 0xc4, 0x04,                    /* add    $0x4,%esp     */
1202     0xc3,                                /* ret */
1203 };
1204 
1205 static const BYTE simd_exception_test2[] = {
1206     0x83, 0xec, 0x4,                     /* sub $0x4, %esp       */
1207     0x0f, 0xae, 0x1c, 0x24,              /* stmxcsr (%esp)       */
1208     0x8b, 0x04, 0x24,                    /* mov    (%esp),%eax   * store mxcsr */
1209     0x66, 0x81, 0x24, 0x24, 0x7f, 0xff,  /* andw $0xff7f,(%esp)  * enable invalid       */
1210     0x0f, 0xae, 0x14, 0x24,              /* ldmxcsr (%esp)       * operation exceptions */
1211     0x0f, 0x57, 0xc9,                    /* xorps  %xmm1,%xmm1   * clear dividend */
1212     0x0f, 0x57, 0xc0,                    /* xorps  %xmm0,%xmm0   * clear divisor  */
1213     0x0f, 0x5e, 0xc8,                    /* divps  %xmm0,%xmm1   * generate fault */
1214     0x89, 0x04, 0x24,                    /* mov    %eax,(%esp)   * restore to old mxcsr */
1215     0x0f, 0xae, 0x14, 0x24,              /* ldmxcsr (%esp)       */
1216     0x83, 0xc4, 0x04,                    /* add    $0x4,%esp     */
1217     0xc3,                                /* ret */
1218 };
1219 
1220 static const BYTE sse_check[] = {
1221     0x0f, 0x58, 0xc8,                    /* addps  %xmm0,%xmm1 */
1222     0xc3,                                /* ret */
1223 };
1224 
1225 static void test_simd_exceptions(void)
1226 {
1227     int stage;
1228 
1229     /* test if CPU & OS can do sse */
1230     stage = 1;
1231     got_exception = 0;
1232     run_exception_test(simd_fault_handler, &stage, sse_check, sizeof(sse_check), 0);
1233     if(got_exception) {
1234         skip("system doesn't support SSE\n");
1235         return;
1236     }
1237 
1238     /* generate a SIMD exception */
1239     stage = 2;
1240     got_exception = 0;
1241     run_exception_test(simd_fault_handler, &stage, simd_exception_test,
1242                        sizeof(simd_exception_test), 0);
1243     ok(got_exception == 1, "got exception: %i, should be 1\n", got_exception);
1244 
1245     /* generate a SIMD exception, test FPE_FLTINV */
1246     stage = 3;
1247     got_exception = 0;
1248     run_exception_test(simd_fault_handler, &stage, simd_exception_test2,
1249                        sizeof(simd_exception_test2), 0);
1250     ok(got_exception == 1, "got exception: %i, should be 1\n", got_exception);
1251 }
1252 
1253 struct fpu_exception_info
1254 {
1255     DWORD exception_code;
1256     DWORD exception_offset;
1257     DWORD eip_offset;
1258 };
1259 
1260 static DWORD fpu_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
1261         CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher)
1262 {
1263     struct fpu_exception_info *info = *(struct fpu_exception_info **)(frame + 1);
1264 
1265     info->exception_code = rec->ExceptionCode;
1266     info->exception_offset = (BYTE *)rec->ExceptionAddress - (BYTE *)code_mem;
1267     info->eip_offset = context->Eip - (DWORD)code_mem;
1268 
1269     ++context->Eip;
1270     return ExceptionContinueExecution;
1271 }
1272 
1273 static void test_fpu_exceptions(void)
1274 {
1275     static const BYTE fpu_exception_test_ie[] =
1276     {
1277         0x83, 0xec, 0x04,                   /* sub $0x4,%esp        */
1278         0x66, 0xc7, 0x04, 0x24, 0xfe, 0x03, /* movw $0x3fe,(%esp)   */
1279         0x9b, 0xd9, 0x7c, 0x24, 0x02,       /* fstcw 0x2(%esp)      */
1280         0xd9, 0x2c, 0x24,                   /* fldcw (%esp)         */
1281         0xd9, 0xee,                         /* fldz                 */
1282         0xd9, 0xe8,                         /* fld1                 */
1283         0xde, 0xf1,                         /* fdivp                */
1284         0xdd, 0xd8,                         /* fstp %st(0)          */
1285         0xdd, 0xd8,                         /* fstp %st(0)          */
1286         0x9b,                               /* fwait                */
1287         0xdb, 0xe2,                         /* fnclex               */
1288         0xd9, 0x6c, 0x24, 0x02,             /* fldcw 0x2(%esp)      */
1289         0x83, 0xc4, 0x04,                   /* add $0x4,%esp        */
1290         0xc3,                               /* ret                  */
1291     };
1292 
1293     static const BYTE fpu_exception_test_de[] =
1294     {
1295         0x83, 0xec, 0x04,                   /* sub $0x4,%esp        */
1296         0x66, 0xc7, 0x04, 0x24, 0xfb, 0x03, /* movw $0x3fb,(%esp)   */
1297         0x9b, 0xd9, 0x7c, 0x24, 0x02,       /* fstcw 0x2(%esp)      */
1298         0xd9, 0x2c, 0x24,                   /* fldcw (%esp)         */
1299         0xdd, 0xd8,                         /* fstp %st(0)          */
1300         0xd9, 0xee,                         /* fldz                 */
1301         0xd9, 0xe8,                         /* fld1                 */
1302         0xde, 0xf1,                         /* fdivp                */
1303         0x9b,                               /* fwait                */
1304         0xdb, 0xe2,                         /* fnclex               */
1305         0xdd, 0xd8,                         /* fstp %st(0)          */
1306         0xdd, 0xd8,                         /* fstp %st(0)          */
1307         0xd9, 0x6c, 0x24, 0x02,             /* fldcw 0x2(%esp)      */
1308         0x83, 0xc4, 0x04,                   /* add $0x4,%esp        */
1309         0xc3,                               /* ret                  */
1310     };
1311 
1312     struct fpu_exception_info info;
1313 
1314     memset(&info, 0, sizeof(info));
1315     run_exception_test(fpu_exception_handler, &info, fpu_exception_test_ie, sizeof(fpu_exception_test_ie), 0);
1316     ok(info.exception_code == EXCEPTION_FLT_STACK_CHECK,
1317             "Got exception code %#x, expected EXCEPTION_FLT_STACK_CHECK\n", info.exception_code);
1318     ok(info.exception_offset == 0x19 ||
1319        broken( info.exception_offset == info.eip_offset ),
1320        "Got exception offset %#x, expected 0x19\n", info.exception_offset);
1321     ok(info.eip_offset == 0x1b, "Got EIP offset %#x, expected 0x1b\n", info.eip_offset);
1322 
1323     memset(&info, 0, sizeof(info));
1324     run_exception_test(fpu_exception_handler, &info, fpu_exception_test_de, sizeof(fpu_exception_test_de), 0);
1325     ok(info.exception_code == EXCEPTION_FLT_DIVIDE_BY_ZERO,
1326             "Got exception code %#x, expected EXCEPTION_FLT_DIVIDE_BY_ZERO\n", info.exception_code);
1327     ok(info.exception_offset == 0x17 ||
1328        broken( info.exception_offset == info.eip_offset ),
1329        "Got exception offset %#x, expected 0x17\n", info.exception_offset);
1330     ok(info.eip_offset == 0x19, "Got EIP offset %#x, expected 0x19\n", info.eip_offset);
1331 }
1332 
1333 struct dpe_exception_info {
1334     BOOL exception_caught;
1335     DWORD exception_info;
1336 };
1337 
1338 static DWORD dpe_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
1339         CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher)
1340 {
1341     DWORD old_prot;
1342     struct dpe_exception_info *info = *(struct dpe_exception_info **)(frame + 1);
1343 
1344     ok(rec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION,
1345        "Exception code %08x\n", rec->ExceptionCode);
1346     ok(rec->NumberParameters == 2,
1347        "Parameter count: %d\n", rec->NumberParameters);
1348     ok((LPVOID)rec->ExceptionInformation[1] == code_mem,
1349        "Exception address: %p, expected %p\n",
1350        (LPVOID)rec->ExceptionInformation[1], code_mem);
1351 
1352     info->exception_info = rec->ExceptionInformation[0];
1353     info->exception_caught = TRUE;
1354 
1355     VirtualProtect(code_mem, 1, PAGE_EXECUTE_READWRITE, &old_prot);
1356     return ExceptionContinueExecution;
1357 }
1358 
1359 static void test_dpe_exceptions(void)
1360 {
1361     static const BYTE single_ret[] = {0xC3};
1362     struct dpe_exception_info info;
1363     NTSTATUS stat;
1364     BOOL has_hw_support;
1365     BOOL is_permanent = FALSE, can_test_without = TRUE, can_test_with = TRUE;
1366     DWORD val;
1367     ULONG len;
1368 
1369     /* Query DEP with len too small */
1370     stat = pNtQueryInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val - 1, &len);
1371     if(stat == STATUS_INVALID_INFO_CLASS)
1372     {
1373         skip("This software platform does not support DEP\n");
1374         return;
1375     }
1376     ok(stat == STATUS_INFO_LENGTH_MISMATCH, "buffer too small: %08x\n", stat);
1377 
1378     /* Query DEP */
1379     stat = pNtQueryInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val, &len);
1380     ok(stat == STATUS_SUCCESS, "querying DEP: status %08x\n", stat);
1381     if(stat == STATUS_SUCCESS)
1382     {
1383         ok(len == sizeof val, "returned length: %d\n", len);
1384         if(val & MEM_EXECUTE_OPTION_PERMANENT)
1385         {
1386             skip("toggling DEP impossible - status locked\n");
1387             is_permanent = TRUE;
1388             if(val & MEM_EXECUTE_OPTION_DISABLE)
1389                 can_test_without = FALSE;
1390             else
1391                 can_test_with = FALSE;
1392         }
1393     }
1394 
1395     if(!is_permanent)
1396     {
1397         /* Enable DEP */
1398         val = MEM_EXECUTE_OPTION_DISABLE;
1399         stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
1400         ok(stat == STATUS_SUCCESS, "enabling DEP: status %08x\n", stat);
1401     }
1402 
1403     if(can_test_with)
1404     {
1405         /* Try access to locked page with DEP on*/
1406         info.exception_caught = FALSE;
1407         run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_NOACCESS);
1408         ok(info.exception_caught == TRUE, "Execution of disabled memory succeeded\n");
1409         ok(info.exception_info == EXCEPTION_READ_FAULT ||
1410            info.exception_info == EXCEPTION_EXECUTE_FAULT,
1411               "Access violation type: %08x\n", (unsigned)info.exception_info);
1412         has_hw_support = info.exception_info == EXCEPTION_EXECUTE_FAULT;
1413         trace("DEP hardware support: %s\n", has_hw_support?"Yes":"No");
1414 
1415         /* Try execution of data with DEP on*/
1416         info.exception_caught = FALSE;
1417         run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_READWRITE);
1418         if(has_hw_support)
1419         {
1420             ok(info.exception_caught == TRUE, "Execution of data memory succeeded\n");
1421             ok(info.exception_info == EXCEPTION_EXECUTE_FAULT,
1422                   "Access violation type: %08x\n", (unsigned)info.exception_info);
1423         }
1424         else
1425             ok(info.exception_caught == FALSE, "Execution trapped without hardware support\n");
1426     }
1427     else
1428         skip("DEP is in AlwaysOff state\n");
1429 
1430     if(!is_permanent)
1431     {
1432         /* Disable DEP */
1433         val = MEM_EXECUTE_OPTION_ENABLE;
1434         stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
1435         ok(stat == STATUS_SUCCESS, "disabling DEP: status %08x\n", stat);
1436     }
1437 
1438     /* page is read without exec here */
1439     if(can_test_without)
1440     {
1441         /* Try execution of data with DEP off */
1442         info.exception_caught = FALSE;
1443         run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_READWRITE);
1444         ok(info.exception_caught == FALSE, "Execution trapped with DEP turned off\n");
1445 
1446         /* Try access to locked page with DEP off - error code is different than
1447            with hardware DEP on */
1448         info.exception_caught = FALSE;
1449         run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_NOACCESS);
1450         ok(info.exception_caught == TRUE, "Execution of disabled memory succeeded\n");
1451         ok(info.exception_info == EXCEPTION_READ_FAULT,
1452               "Access violation type: %08x\n", (unsigned)info.exception_info);
1453     }
1454     else
1455         skip("DEP is in AlwaysOn state\n");
1456 
1457     if(!is_permanent)
1458     {
1459         /* Turn off DEP permanently */
1460         val = MEM_EXECUTE_OPTION_ENABLE | MEM_EXECUTE_OPTION_PERMANENT;
1461         stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
1462         ok(stat == STATUS_SUCCESS, "disabling DEP permanently: status %08x\n", stat);
1463     }
1464 
1465     /* Try to turn off DEP */
1466     val = MEM_EXECUTE_OPTION_ENABLE;
1467     stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
1468     ok(stat == STATUS_ACCESS_DENIED, "disabling DEP while permanent: status %08x\n", stat);
1469 
1470     /* Try to turn on DEP */
1471     val = MEM_EXECUTE_OPTION_DISABLE;
1472     stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
1473     ok(stat == STATUS_ACCESS_DENIED, "enabling DEP while permanent: status %08x\n", stat);
1474 }
1475 
1476 static void test_thread_context(void)
1477 {
1478     CONTEXT context;
1479     NTSTATUS status;
1480     struct expected
1481     {
1482         DWORD Eax, Ebx, Ecx, Edx, Esi, Edi, Ebp, Esp, Eip,
1483             SegCs, SegDs, SegEs, SegFs, SegGs, SegSs, EFlags, prev_frame;
1484     } expect;
1485     NTSTATUS (*func_ptr)( struct expected *res, void *func, void *arg1, void *arg2 ) = (void *)code_mem;
1486 
1487     static const BYTE call_func[] =
1488     {
1489         0x55,             /* pushl  %ebp */
1490         0x89, 0xe5,       /* mov    %esp,%ebp */
1491         0x50,             /* pushl  %eax ; add a bit of offset to the stack */
1492         0x50,             /* pushl  %eax */
1493         0x50,             /* pushl  %eax */
1494         0x50,             /* pushl  %eax */
1495         0x8b, 0x45, 0x08, /* mov    0x8(%ebp),%eax */
1496         0x8f, 0x00,       /* popl   (%eax) */
1497         0x89, 0x58, 0x04, /* mov    %ebx,0x4(%eax) */
1498         0x89, 0x48, 0x08, /* mov    %ecx,0x8(%eax) */
1499         0x89, 0x50, 0x0c, /* mov    %edx,0xc(%eax) */
1500         0x89, 0x70, 0x10, /* mov    %esi,0x10(%eax) */
1501         0x89, 0x78, 0x14, /* mov    %edi,0x14(%eax) */
1502         0x89, 0x68, 0x18, /* mov    %ebp,0x18(%eax) */
1503         0x89, 0x60, 0x1c, /* mov    %esp,0x1c(%eax) */
1504         0xff, 0x75, 0x04, /* pushl  0x4(%ebp) */
1505         0x8f, 0x40, 0x20, /* popl   0x20(%eax) */
1506         0x8c, 0x48, 0x24, /* mov    %cs,0x24(%eax) */
1507         0x8c, 0x58, 0x28, /* mov    %ds,0x28(%eax) */
1508         0x8c, 0x40, 0x2c, /* mov    %es,0x2c(%eax) */
1509         0x8c, 0x60, 0x30, /* mov    %fs,0x30(%eax) */
1510         0x8c, 0x68, 0x34, /* mov    %gs,0x34(%eax) */
1511         0x8c, 0x50, 0x38, /* mov    %ss,0x38(%eax) */
1512         0x9c,             /* pushf */
1513         0x8f, 0x40, 0x3c, /* popl   0x3c(%eax) */
1514         0xff, 0x75, 0x00, /* pushl  0x0(%ebp) ; previous stack frame */
1515         0x8f, 0x40, 0x40, /* popl   0x40(%eax) */
1516         0x8b, 0x00,       /* mov    (%eax),%eax */
1517         0xff, 0x75, 0x14, /* pushl  0x14(%ebp) */
1518         0xff, 0x75, 0x10, /* pushl  0x10(%ebp) */
1519         0xff, 0x55, 0x0c, /* call   *0xc(%ebp) */
1520         0xc9,             /* leave */
1521         0xc3,             /* ret */
1522     };
1523 
1524     memcpy( func_ptr, call_func, sizeof(call_func) );
1525 
1526 #define COMPARE(reg) \
1527     ok( context.reg == expect.reg, "wrong " #reg " %08x/%08x\n", context.reg, expect.reg )
1528 
1529     memset( &context, 0xcc, sizeof(context) );
1530     memset( &expect, 0xcc, sizeof(expect) );
1531     func_ptr( &expect, pRtlCaptureContext, &context, 0 );
1532     trace( "expect: eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x ebp=%08x esp=%08x "
1533            "eip=%08x cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x prev=%08x\n",
1534            expect.Eax, expect.Ebx, expect.Ecx, expect.Edx, expect.Esi, expect.Edi,
1535            expect.Ebp, expect.Esp, expect.Eip, expect.SegCs, expect.SegDs, expect.SegEs,
1536            expect.SegFs, expect.SegGs, expect.SegSs, expect.EFlags, expect.prev_frame );
1537     trace( "actual: eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x ebp=%08x esp=%08x "
1538            "eip=%08x cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x\n",
1539            context.Eax, context.Ebx, context.Ecx, context.Edx, context.Esi, context.Edi,
1540            context.Ebp, context.Esp, context.Eip, context.SegCs, context.SegDs, context.SegEs,
1541            context.SegFs, context.SegGs, context.SegSs, context.EFlags );
1542 
1543     ok( context.ContextFlags == (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS) ||
1544         broken( context.ContextFlags == 0xcccccccc ),  /* <= vista */
1545         "wrong flags %08x\n", context.ContextFlags );
1546     COMPARE( Eax );
1547     COMPARE( Ebx );
1548     COMPARE( Ecx );
1549     COMPARE( Edx );
1550     COMPARE( Esi );
1551     COMPARE( Edi );
1552     COMPARE( Eip );
1553     COMPARE( SegCs );
1554     COMPARE( SegDs );
1555     COMPARE( SegEs );
1556     COMPARE( SegFs );
1557     COMPARE( SegGs );
1558     COMPARE( SegSs );
1559     COMPARE( EFlags );
1560     /* Ebp is from the previous stackframe */
1561     ok( context.Ebp == expect.prev_frame, "wrong Ebp %08x/%08x\n", context.Ebp, expect.prev_frame );
1562     /* Esp is the value on entry to the previous stackframe */
1563     ok( context.Esp == expect.Ebp + 8, "wrong Esp %08x/%08x\n", context.Esp, expect.Ebp + 8 );
1564 
1565     memset( &context, 0xcc, sizeof(context) );
1566     memset( &expect, 0xcc, sizeof(expect) );
1567     context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS;
1568     status = func_ptr( &expect, pNtGetContextThread, (void *)GetCurrentThread(), &context );
1569     ok( status == STATUS_SUCCESS, "NtGetContextThread failed %08x\n", status );
1570     trace( "expect: eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x ebp=%08x esp=%08x "
1571            "eip=%08x cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x prev=%08x\n",
1572            expect.Eax, expect.Ebx, expect.Ecx, expect.Edx, expect.Esi, expect.Edi,
1573            expect.Ebp, expect.Esp, expect.Eip, expect.SegCs, expect.SegDs, expect.SegEs,
1574            expect.SegFs, expect.SegGs, expect.SegSs, expect.EFlags, expect.prev_frame );
1575     trace( "actual: eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x ebp=%08x esp=%08x "
1576            "eip=%08x cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x\n",
1577            context.Eax, context.Ebx, context.Ecx, context.Edx, context.Esi, context.Edi,
1578            context.Ebp, context.Esp, context.Eip, context.SegCs, context.SegDs, context.SegEs,
1579            context.SegFs, context.SegGs, context.SegSs, context.EFlags );
1580     /* Eax, Ecx, Edx, EFlags are not preserved */
1581     COMPARE( Ebx );
1582     COMPARE( Esi );
1583     COMPARE( Edi );
1584     COMPARE( Ebp );
1585     /* Esp is the stack upon entry to NtGetContextThread */
1586     ok( context.Esp == expect.Esp - 12 || context.Esp == expect.Esp - 16,
1587         "wrong Esp %08x/%08x\n", context.Esp, expect.Esp );
1588     /* Eip is somewhere close to the NtGetContextThread implementation */
1589     ok( (char *)context.Eip >= (char *)pNtGetContextThread - 0x10000 &&
1590         (char *)context.Eip <= (char *)pNtGetContextThread + 0x10000,
1591         "wrong Eip %08x/%08x\n", context.Eip, (DWORD)pNtGetContextThread );
1592     ok( *(WORD *)context.Eip == 0xc483 || *(WORD *)context.Eip == 0x08c2 || *(WORD *)context.Eip == 0x8dc3,
1593         "expected 0xc483 or 0x08c2 or 0x8dc3, got %04x\n", *(WORD *)context.Eip );
1594     /* segment registers clear the high word */
1595     ok( context.SegCs == LOWORD(expect.SegCs), "wrong SegCs %08x/%08x\n", context.SegCs, expect.SegCs );
1596     ok( context.SegDs == LOWORD(expect.SegDs), "wrong SegDs %08x/%08x\n", context.SegDs, expect.SegDs );
1597     ok( context.SegEs == LOWORD(expect.SegEs), "wrong SegEs %08x/%08x\n", context.SegEs, expect.SegEs );
1598     ok( context.SegFs == LOWORD(expect.SegFs), "wrong SegFs %08x/%08x\n", context.SegFs, expect.SegFs );
1599     ok( context.SegGs == LOWORD(expect.SegGs), "wrong SegGs %08x/%08x\n", context.SegGs, expect.SegGs );
1600     ok( context.SegSs == LOWORD(expect.SegSs), "wrong SegSs %08x/%08x\n", context.SegSs, expect.SegGs );
1601 #undef COMPARE
1602 }
1603 
1604 #elif defined(__x86_64__)
1605 
1606 #define is_wow64 0
1607 
1608 #define UNW_FLAG_NHANDLER  0
1609 #define UNW_FLAG_EHANDLER  1
1610 #define UNW_FLAG_UHANDLER  2
1611 #define UNW_FLAG_CHAININFO 4
1612 
1613 #define UWOP_PUSH_NONVOL     0
1614 #define UWOP_ALLOC_LARGE     1
1615 #define UWOP_ALLOC_SMALL     2
1616 #define UWOP_SET_FPREG       3
1617 #define UWOP_SAVE_NONVOL     4
1618 #define UWOP_SAVE_NONVOL_FAR 5
1619 #define UWOP_SAVE_XMM128     8
1620 #define UWOP_SAVE_XMM128_FAR 9
1621 #define UWOP_PUSH_MACHFRAME  10
1622 
1623 struct results
1624 {
1625     int rip_offset;   /* rip offset from code start */
1626     int rbp_offset;   /* rbp offset from stack pointer */
1627     int handler;      /* expect handler to be set? */
1628     int rip;          /* expected final rip value */
1629     int frame;        /* expected frame return value */
1630     int regs[8][2];   /* expected values for registers */
1631 };
1632 
1633 struct unwind_test
1634 {
1635     const BYTE *function;
1636     size_t function_size;
1637     const BYTE *unwind_info;
1638     const struct results *results;
1639     unsigned int nb_results;
1640 };
1641 
1642 enum regs
1643 {
1644     rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi,
1645     r8,  r9,  r10, r11, r12, r13, r14, r15
1646 };
1647 
1648 static const char * const reg_names[16] =
1649 {
1650     "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
1651     "r8",  "r9",  "r10", "r11", "r12", "r13", "r14", "r15"
1652 };
1653 
1654 #define UWOP(code,info) (UWOP_##code | ((info) << 4))
1655 
1656 static void call_virtual_unwind( int testnum, const struct unwind_test *test )
1657 {
1658     static const int code_offset = 1024;
1659     static const int unwind_offset = 2048;
1660     void *handler, *data;
1661     CONTEXT context;
1662     RUNTIME_FUNCTION runtime_func;
1663     KNONVOLATILE_CONTEXT_POINTERS ctx_ptr;
1664     UINT i, j, k;
1665     ULONG64 fake_stack[256];
1666     ULONG64 frame, orig_rip, orig_rbp, unset_reg;
1667     UINT unwind_size = 4 + 2 * test->unwind_info[2] + 8;
1668 
1669     memcpy( (char *)code_mem + code_offset, test->function, test->function_size );
1670     memcpy( (char *)code_mem + unwind_offset, test->unwind_info, unwind_size );
1671 
1672     runtime_func.BeginAddress = code_offset;
1673     runtime_func.EndAddress = code_offset + test->function_size;
1674     runtime_func.UnwindData = unwind_offset;
1675 
1676     trace( "code: %p stack: %p\n", code_mem, fake_stack );
1677 
1678     for (i = 0; i < test->nb_results; i++)
1679     {
1680         memset( &ctx_ptr, 0, sizeof(ctx_ptr) );
1681         memset( &context, 0x55, sizeof(context) );
1682         memset( &unset_reg, 0x55, sizeof(unset_reg) );
1683         for (j = 0; j < 256; j++) fake_stack[j] = j * 8;
1684 
1685         context.Rsp = (ULONG_PTR)fake_stack;
1686         context.Rbp = (ULONG_PTR)fake_stack + test->results[i].rbp_offset;
1687         orig_rbp = context.Rbp;
1688         orig_rip = (ULONG64)code_mem + code_offset + test->results[i].rip_offset;
1689 
1690         trace( "%u/%u: rip=%p (%02x) rbp=%p rsp=%p\n", testnum, i,
1691                (void *)orig_rip, *(BYTE *)orig_rip, (void *)orig_rbp, (void *)context.Rsp );
1692 
1693         data = (void *)0xdeadbeef;
1694         handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG64)code_mem, orig_rip,
1695                                     &runtime_func, &context, &data, &frame, &ctx_ptr );
1696         if (test->results[i].handler)
1697         {
1698             ok( (char *)handler == (char *)code_mem + 0x200,
1699                 "%u/%u: wrong handler %p/%p\n", testnum, i, handler, (char *)code_mem + 0x200 );
1700             if (handler) ok( *(DWORD *)data == 0x08070605,
1701                              "%u/%u: wrong handler data %p\n", testnum, i, data );
1702         }
1703         else
1704         {
1705             ok( handler == NULL, "%u/%u: handler %p instead of NULL\n", testnum, i, handler );
1706             ok( data == (void *)0xdeadbeef, "%u/%u: handler data set to %p\n", testnum, i, data );
1707         }
1708 
1709         ok( context.Rip == test->results[i].rip, "%u/%u: wrong rip %p/%x\n",
1710             testnum, i, (void *)context.Rip, test->results[i].rip );
1711         ok( frame == (ULONG64)fake_stack + test->results[i].frame, "%u/%u: wrong frame %p/%p\n",
1712             testnum, i, (void *)frame, (char *)fake_stack + test->results[i].frame );
1713 
1714         for (j = 0; j < 16; j++)
1715         {
1716             static const UINT nb_regs = sizeof(test->results[i].regs) / sizeof(test->results[i].regs[0]);
1717 
1718             for (k = 0; k < nb_regs; k++)
1719             {
1720                 if (test->results[i].regs[k][0] == -1)
1721                 {
1722                     k = nb_regs;
1723                     break;
1724                 }
1725                 if (test->results[i].regs[k][0] == j) break;
1726             }
1727 
1728             if (j == rsp)  /* rsp is special */
1729             {
1730                 ok( !ctx_ptr.u2.IntegerContext[j],
1731                     "%u/%u: rsp should not be set in ctx_ptr\n", testnum, i );
1732                 ok( context.Rsp == (ULONG64)fake_stack + test->results[i].regs[k][1],
1733                     "%u/%u: register rsp wrong %p/%p\n",
1734                     testnum, i, (void *)context.Rsp, (char *)fake_stack + test->results[i].regs[k][1] );
1735                 continue;
1736             }
1737 
1738             if (ctx_ptr.u2.IntegerContext[j])
1739             {
1740                 ok( k < nb_regs, "%u/%u: register %s should not be set to %lx\n",
1741                     testnum, i, reg_names[j], *(&context.Rax + j) );
1742                 if (k < nb_regs)
1743                     ok( *(&context.Rax + j) == test->results[i].regs[k][1],
1744                         "%u/%u: register %s wrong %p/%x\n",
1745                         testnum, i, reg_names[j], (void *)*(&context.Rax + j), test->results[i].regs[k][1] );
1746             }
1747             else
1748             {
1749                 ok( k == nb_regs, "%u/%u: register %s should be set\n", testnum, i, reg_names[j] );
1750                 if (j == rbp)
1751                     ok( context.Rbp == orig_rbp, "%u/%u: register rbp wrong %p/unset\n",
1752                         testnum, i, (void *)context.Rbp );
1753                 else
1754                     ok( *(&context.Rax + j) == unset_reg,
1755                         "%u/%u: register %s wrong %p/unset\n",
1756                         testnum, i, reg_names[j], (void *)*(&context.Rax + j));
1757             }
1758         }
1759     }
1760 }
1761 
1762 static void test_virtual_unwind(void)
1763 {
1764     static const BYTE function_0[] =
1765     {
1766         0xff, 0xf5,                                  /* 00: push %rbp */
1767         0x48, 0x81, 0xec, 0x10, 0x01, 0x00, 0x00,    /* 02: sub $0x110,%rsp */
1768         0x48, 0x8d, 0x6c, 0x24, 0x30,                /* 09: lea 0x30(%rsp),%rbp */
1769         0x48, 0x89, 0x9d, 0xf0, 0x00, 0x00, 0x00,    /* 0e: mov %rbx,0xf0(%rbp) */
1770         0x48, 0x89, 0xb5, 0xf8, 0x00, 0x00, 0x00,    /* 15: mov %rsi,0xf8(%rbp) */
1771         0x90,                                        /* 1c: nop */
1772         0x48, 0x8b, 0x9d, 0xf0, 0x00, 0x00, 0x00,    /* 1d: mov 0xf0(%rbp),%rbx */
1773         0x48, 0x8b, 0xb5, 0xf8, 0x00, 0x00, 0x00,    /* 24: mov 0xf8(%rbp),%rsi */
1774         0x48, 0x8d, 0xa5, 0xe0, 0x00, 0x00, 0x00,    /* 2b: lea 0xe0(%rbp),%rsp */
1775         0x5d,                                        /* 32: pop %rbp */
1776         0xc3                                         /* 33: ret */
1777     };
1778 
1779     static const BYTE unwind_info_0[] =
1780     {
1781         1 | (UNW_FLAG_EHANDLER << 3),  /* version + flags */
1782         0x1c,                          /* prolog size */
1783         8,                             /* opcode count */
1784         (0x03 << 4) | rbp,             /* frame reg rbp offset 0x30 */
1785 
1786         0x1c, UWOP(SAVE_NONVOL, rsi), 0x25, 0, /* 1c: mov %rsi,0x128(%rsp) */
1787         0x15, UWOP(SAVE_NONVOL, rbx), 0x24, 0, /* 15: mov %rbx,0x120(%rsp) */
1788         0x0e, UWOP(SET_FPREG, rbp),            /* 0e: lea 0x30(%rsp),rbp */
1789         0x09, UWOP(ALLOC_LARGE, 0), 0x22, 0,   /* 09: sub $0x110,%rsp */
1790         0x02, UWOP(PUSH_NONVOL, rbp),          /* 02: push %rbp */
1791 
1792         0x00, 0x02, 0x00, 0x00,  /* handler */
1793         0x05, 0x06, 0x07, 0x08,  /* data */
1794     };
1795 
1796     static const struct results results_0[] =
1797     {
1798       /* offset  rbp   handler  rip   frame   registers */
1799         { 0x00,  0x40,  FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
1800         { 0x02,  0x40,  FALSE, 0x008, 0x000, { {rsp,0x010}, {rbp,0x000}, {-1,-1} }},
1801         { 0x09,  0x40,  FALSE, 0x118, 0x000, { {rsp,0x120}, {rbp,0x110}, {-1,-1} }},
1802         { 0x0e,  0x40,  FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1} }},
1803         { 0x15,  0x40,  FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {-1,-1} }},
1804         { 0x1c,  0x40,  TRUE,  0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
1805         { 0x1d,  0x40,  TRUE,  0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
1806         { 0x24,  0x40,  TRUE,  0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
1807         { 0x2b,  0x40,  FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1}}},
1808         { 0x32,  0x40,  FALSE, 0x008, 0x010, { {rsp,0x010}, {rbp,0x000}, {-1,-1}}},
1809         { 0x33,  0x40,  FALSE, 0x000, 0x010, { {rsp,0x008}, {-1,-1}}},
1810     };
1811 
1812 
1813     static const BYTE function_1[] =
1814     {
1815         0x53,                     /* 00: push %rbx */
1816         0x55,                     /* 01: push %rbp */
1817         0x56,                     /* 02: push %rsi */
1818         0x57,                     /* 03: push %rdi */
1819         0x41, 0x54,               /* 04: push %r12 */
1820         0x48, 0x83, 0xec, 0x30,   /* 06: sub $0x30,%rsp */
1821         0x90, 0x90,               /* 0a: nop; nop */
1822         0x48, 0x83, 0xc4, 0x30,   /* 0c: add $0x30,%rsp */
1823         0x41, 0x5c,               /* 10: pop %r12 */
1824         0x5f,                     /* 12: pop %rdi */
1825         0x5e,                     /* 13: pop %rsi */
1826         0x5d,                     /* 14: pop %rbp */
1827         0x5b,                     /* 15: pop %rbx */
1828         0xc3                      /* 16: ret */
1829      };
1830 
1831     static const BYTE unwind_info_1[] =
1832     {
1833         1 | (UNW_FLAG_EHANDLER << 3),  /* version + flags */
1834         0x0a,                          /* prolog size */
1835         6,                             /* opcode count */
1836         0,                             /* frame reg */
1837 
1838         0x0a, UWOP(ALLOC_SMALL, 5),   /* 0a: sub $0x30,%rsp */
1839         0x06, UWOP(PUSH_NONVOL, r12), /* 06: push %r12 */
1840         0x04, UWOP(PUSH_NONVOL, rdi), /* 04: push %rdi */
1841         0x03, UWOP(PUSH_NONVOL, rsi), /* 03: push %rsi */
1842         0x02, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */
1843         0x01, UWOP(PUSH_NONVOL, rbx), /* 01: push %rbx */
1844 
1845         0x00, 0x02, 0x00, 0x00,  /* handler */
1846         0x05, 0x06, 0x07, 0x08,  /* data */
1847     };
1848 
1849     static const struct results results_1[] =
1850     {
1851       /* offset  rbp   handler  rip   frame   registers */
1852         { 0x00,  0x50,  FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
1853         { 0x01,  0x50,  FALSE, 0x008, 0x000, { {rsp,0x010}, {rbx,0x000}, {-1,-1} }},
1854         { 0x02,  0x50,  FALSE, 0x010, 0x000, { {rsp,0x018}, {rbx,0x008}, {rbp,0x000}, {-1,-1} }},
1855         { 0x03,  0x50,  FALSE, 0x018, 0x000, { {rsp,0x020}, {rbx,0x010}, {rbp,0x008}, {rsi,0x000}, {-1,-1} }},
1856         { 0x04,  0x50,  FALSE, 0x020, 0x000, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }},
1857         { 0x06,  0x50,  FALSE, 0x028, 0x000, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }},
1858         { 0x0a,  0x50,  TRUE,  0x058, 0x000, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }},
1859         { 0x0c,  0x50,  FALSE, 0x058, 0x000, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }},
1860         { 0x10,  0x50,  FALSE, 0x028, 0x000, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }},
1861         { 0x12,  0x50,  FALSE, 0x020, 0x000, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }},
1862         { 0x13,  0x50,  FALSE, 0x018, 0x000, { {rsp,0x020}, {rbx,0x010}, {rbp,0x008}, {rsi,0x000}, {-1,-1} }},
1863         { 0x14,  0x50,  FALSE, 0x010, 0x000, { {rsp,0x018}, {rbx,0x008}, {rbp,0x000}, {-1,-1} }},
1864         { 0x15,  0x50,  FALSE, 0x008, 0x000, { {rsp,0x010}, {rbx,0x000}, {-1,-1} }},
1865         { 0x16,  0x50,  FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
1866     };
1867 
1868     static const struct unwind_test tests[] =
1869     {
1870         { function_0, sizeof(function_0), unwind_info_0,
1871           results_0, sizeof(results_0)/sizeof(results_0[0]) },
1872         { function_1, sizeof(function_1), unwind_info_1,
1873           results_1, sizeof(results_1)/sizeof(results_1[0]) }
1874     };
1875     unsigned int i;
1876 
1877     for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++)
1878         call_virtual_unwind( i, &tests[i] );
1879 }
1880 
1881 static int consolidate_dummy_called;
1882 static PVOID CALLBACK test_consolidate_dummy(EXCEPTION_RECORD *rec)
1883 {
1884     CONTEXT *ctx = (CONTEXT *)rec->ExceptionInformation[1];
1885     consolidate_dummy_called = 1;
1886     ok(ctx->Rip == 0xdeadbeef, "test_consolidate_dummy failed for Rip, expected: 0xdeadbeef, got: %lx\n", ctx->Rip);
1887     return (PVOID)rec->ExceptionInformation[2];
1888 }
1889 
1890 static void test_restore_context(void)
1891 {
1892     SETJMP_FLOAT128 *fltsave;
1893     EXCEPTION_RECORD rec;
1894     _JUMP_BUFFER buf;
1895     CONTEXT ctx;
1896     int i, pass;
1897 
1898     if (!pRtlUnwindEx || !pRtlRestoreContext || !pRtlCaptureContext || !p_setjmp)
1899     {
1900         skip("RtlUnwindEx/RtlCaptureContext/RtlRestoreContext/_setjmp not found\n");
1901         return;
1902     }
1903 
1904     /* RtlRestoreContext(NULL, NULL); crashes on Windows */
1905 
1906     /* test simple case of capture and restore context */
1907     pass = 0;
1908     InterlockedIncrement(&pass); /* interlocked to prevent compiler from moving after capture */
1909     pRtlCaptureContext(&ctx);
1910     if (InterlockedIncrement(&pass) == 2) /* interlocked to prevent compiler from moving before capture */
1911     {
1912         pRtlRestoreContext(&ctx, NULL);
1913         ok(0, "shouldn't be reached\n");
1914     }
1915     else
1916         ok(pass < 4, "unexpected pass %d\n", pass);
1917 
1918     /* test with jmp using RltRestoreContext */
1919     pass = 0;
1920     InterlockedIncrement(&pass);
1921     RtlCaptureContext(&ctx);
1922     InterlockedIncrement(&pass); /* only called once */
1923     p_setjmp(&buf);
1924     InterlockedIncrement(&pass);
1925     if (pass == 3)
1926     {
1927         rec.ExceptionCode = STATUS_LONGJUMP;
1928         rec.NumberParameters = 1;
1929         rec.ExceptionInformation[0] = (DWORD64)&buf;
1930 
1931         /* uses buf.Rip instead of ctx.Rip */
1932         pRtlRestoreContext(&ctx, &rec);
1933         ok(0, "shouldn't be reached\n");
1934     }
1935     else if (pass == 4)
1936     {
1937         ok(buf.Rbx == ctx.Rbx, "longjmp failed for Rbx, expected: %lx, got: %lx\n", buf.Rbx, ctx.Rbx);
1938         ok(buf.Rsp == ctx.Rsp, "longjmp failed for Rsp, expected: %lx, got: %lx\n", buf.Rsp, ctx.Rsp);
1939         ok(buf.Rbp == ctx.Rbp, "longjmp failed for Rbp, expected: %lx, got: %lx\n", buf.Rbp, ctx.Rbp);
1940         ok(buf.Rsi == ctx.Rsi, "longjmp failed for Rsi, expected: %lx, got: %lx\n", buf.Rsi, ctx.Rsi);
1941         ok(buf.Rdi == ctx.Rdi, "longjmp failed for Rdi, expected: %lx, got: %lx\n", buf.Rdi, ctx.Rdi);
1942         ok(buf.R12 == ctx.R12, "longjmp failed for R12, expected: %lx, got: %lx\n", buf.R12, ctx.R12);
1943         ok(buf.R13 == ctx.R13, "longjmp failed for R13, expected: %lx, got: %lx\n", buf.R13, ctx.R13);
1944         ok(buf.R14 == ctx.R14, "longjmp failed for R14, expected: %lx, got: %lx\n", buf.R14, ctx.R14);
1945         ok(buf.R15 == ctx.R15, "longjmp failed for R15, expected: %lx, got: %lx\n", buf.R15, ctx.R15);
1946 
1947         fltsave = &buf.Xmm6;
1948         for (i = 0; i < 10; i++)
1949         {
1950             ok(fltsave[i].Part[0] == ctx.u.FltSave.XmmRegisters[i + 6].Low,
1951                 "longjmp failed for Xmm%d, expected %lx, got %lx\n", i + 6,
1952                 fltsave[i].Part[0], ctx.u.FltSave.XmmRegisters[i + 6].Low);
1953 
1954             ok(fltsave[i].Part[1] == ctx.u.FltSave.XmmRegisters[i + 6].High,
1955                 "longjmp failed for Xmm%d, expected %lx, got %lx\n", i + 6,
1956                 fltsave[i].Part[1], ctx.u.FltSave.XmmRegisters[i + 6].High);
1957         }
1958     }
1959     else
1960         ok(0, "unexpected pass %d\n", pass);
1961 
1962     /* test with jmp through RtlUnwindEx */
1963     pass = 0;
1964     InterlockedIncrement(&pass);
1965     pRtlCaptureContext(&ctx);
1966     InterlockedIncrement(&pass); /* only called once */
1967     p_setjmp(&buf);
1968     InterlockedIncrement(&pass);
1969     if (pass == 3)
1970     {
1971         rec.ExceptionCode = STATUS_LONGJUMP;
1972         rec.NumberParameters = 1;
1973         rec.ExceptionInformation[0] = (DWORD64)&buf;
1974 
1975         /* uses buf.Rip instead of bogus 0xdeadbeef */
1976         pRtlUnwindEx((void*)buf.Rsp, (void*)0xdeadbeef, &rec, NULL, &ctx, NULL);
1977         ok(0, "shouldn't be reached\n");
1978     }
1979     else
1980         ok(pass == 4, "unexpected pass %d\n", pass);
1981 
1982 
1983     /* test with consolidate */
1984     pass = 0;
1985     InterlockedIncrement(&pass);
1986     RtlCaptureContext(&ctx);
1987     InterlockedIncrement(&pass);
1988     if (pass == 2)
1989     {
1990         rec.ExceptionCode = STATUS_UNWIND_CONSOLIDATE;
1991         rec.NumberParameters = 3;
1992         rec.ExceptionInformation[0] = (DWORD64)test_consolidate_dummy;
1993         rec.ExceptionInformation[1] = (DWORD64)&ctx;
1994         rec.ExceptionInformation[2] = ctx.Rip;
1995         ctx.Rip = 0xdeadbeef;
1996 
1997         pRtlRestoreContext(&ctx, &rec);
1998         ok(0, "shouldn't be reached\n");
1999     }
2000     else if (pass == 3)
2001         ok(consolidate_dummy_called, "test_consolidate_dummy not called\n");
2002     else
2003         ok(0, "unexpected pass %d\n", pass);
2004 }
2005 
2006 static RUNTIME_FUNCTION* CALLBACK dynamic_unwind_callback( DWORD64 pc, PVOID context )
2007 {
2008     static const int code_offset = 1024;
2009     static RUNTIME_FUNCTION runtime_func;
2010     (*(DWORD *)context)++;
2011 
2012     runtime_func.BeginAddress = code_offset + 16;
2013     runtime_func.EndAddress   = code_offset + 32;
2014     runtime_func.UnwindData   = 0;
2015     return &runtime_func;
2016 }
2017 
2018 static void test_dynamic_unwind(void)
2019 {
2020     static const int code_offset = 1024;
2021     char buf[sizeof(RUNTIME_FUNCTION) + 4];
2022     RUNTIME_FUNCTION *runtime_func, *func;
2023     ULONG_PTR table, base;
2024     DWORD count;
2025 
2026     /* Test RtlAddFunctionTable with aligned RUNTIME_FUNCTION pointer */
2027     runtime_func = (RUNTIME_FUNCTION *)buf;
2028     runtime_func->BeginAddress = code_offset;
2029     runtime_func->EndAddress   = code_offset + 16;
2030     runtime_func->UnwindData   = 0;
2031     ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
2032         "RtlAddFunctionTable failed for runtime_func = %p (aligned)\n", runtime_func );
2033 
2034     /* Lookup function outside of any function table */
2035     base = 0xdeadbeef;
2036     func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 16, &base, NULL );
2037     ok( func == NULL,
2038         "RtlLookupFunctionEntry returned unexpected function, expected: NULL, got: %p\n", func );
2039     ok( !base || broken(base == 0xdeadbeef),
2040         "RtlLookupFunctionEntry modified base address, expected: 0, got: %lx\n", base );
2041 
2042     /* Test with pointer inside of our function */
2043     base = 0xdeadbeef;
2044     func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 8, &base, NULL );
2045     ok( func == runtime_func,
2046         "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func );
2047     ok( base == (ULONG_PTR)code_mem,
2048         "RtlLookupFunctionEntry returned invalid base, expected: %lx, got: %lx\n", (ULONG_PTR)code_mem, base );
2049 
2050     /* Test RtlDeleteFunctionTable */
2051     ok( pRtlDeleteFunctionTable( runtime_func ),
2052         "RtlDeleteFunctionTable failed for runtime_func = %p (aligned)\n", runtime_func );
2053     ok( !pRtlDeleteFunctionTable( runtime_func ),
2054         "RtlDeleteFunctionTable returned success for nonexistent table runtime_func = %p\n", runtime_func );
2055 
2056     /* Unaligned RUNTIME_FUNCTION pointer */
2057     runtime_func = (RUNTIME_FUNCTION *)((ULONG_PTR)buf | 0x3);
2058     runtime_func->BeginAddress = code_offset;
2059     runtime_func->EndAddress   = code_offset + 16;
2060     runtime_func->UnwindData   = 0;
2061     ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
2062         "RtlAddFunctionTable failed for runtime_func = %p (unaligned)\n", runtime_func );
2063     ok( pRtlDeleteFunctionTable( runtime_func ),
2064         "RtlDeleteFunctionTable failed for runtime_func = %p (unaligned)\n", runtime_func );
2065 
2066     /* Attempt to insert the same entry twice */
2067     runtime_func = (RUNTIME_FUNCTION *)buf;
2068     runtime_func->BeginAddress = code_offset;
2069     runtime_func->EndAddress   = code_offset + 16;
2070     runtime_func->UnwindData   = 0;
2071     ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
2072         "RtlAddFunctionTable failed for runtime_func = %p (first attempt)\n", runtime_func );
2073     ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
2074         "RtlAddFunctionTable failed for runtime_func = %p (second attempt)\n", runtime_func );
2075     ok( pRtlDeleteFunctionTable( runtime_func ),
2076         "RtlDeleteFunctionTable failed for runtime_func = %p (first attempt)\n", runtime_func );
2077     ok( pRtlDeleteFunctionTable( runtime_func ),
2078         "RtlDeleteFunctionTable failed for runtime_func = %p (second attempt)\n", runtime_func );
2079     ok( !pRtlDeleteFunctionTable( runtime_func ),
2080         "RtlDeleteFunctionTable returned success for nonexistent table runtime_func = %p\n", runtime_func );
2081 
2082     /* Test RtlInstallFunctionTableCallback with both low bits unset */
2083     table = (ULONG_PTR)code_mem;
2084     ok( !pRtlInstallFunctionTableCallback( table, (ULONG_PTR)code_mem, code_offset + 32, &dynamic_unwind_callback, (PVOID*)&count, NULL ),
2085         "RtlInstallFunctionTableCallback returned success for table = %lx\n", table );
2086 
2087     /* Test RtlInstallFunctionTableCallback with both low bits set */
2088     table = (ULONG_PTR)code_mem | 0x3;
2089     ok( pRtlInstallFunctionTableCallback( table, (ULONG_PTR)code_mem, code_offset + 32, &dynamic_unwind_callback, (PVOID*)&count, NULL ),
2090         "RtlInstallFunctionTableCallback failed for table = %lx\n", table );
2091 
2092     /* Lookup function outside of any function table */
2093     count = 0;
2094     base = 0xdeadbeef;
2095     func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 32, &base, NULL );
2096     ok( func == NULL,
2097         "RtlLookupFunctionEntry returned unexpected function, expected: NULL, got: %p\n", func );
2098     ok( !base || broken(base == 0xdeadbeef),
2099         "RtlLookupFunctionEntry modified base address, expected: 0, got: %lx\n", base );
2100     ok( !count,
2101         "RtlLookupFunctionEntry issued %d unexpected calls to dynamic_unwind_callback\n", count );
2102 
2103     /* Test with pointer inside of our function */
2104     count = 0;
2105     base = 0xdeadbeef;
2106     func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 24, &base, NULL );
2107     ok( func != NULL && func->BeginAddress == code_offset + 16 && func->EndAddress == code_offset + 32,
2108         "RtlLookupFunctionEntry didn't return expected function, got: %p\n", func );
2109     ok( base == (ULONG_PTR)code_mem,
2110         "RtlLookupFunctionEntry returned invalid base, expected: %lx, got: %lx\n", (ULONG_PTR)code_mem, base );
2111     ok( count == 1,
2112         "RtlLookupFunctionEntry issued %d calls to dynamic_unwind_callback, expected: 1\n", count );
2113 
2114     /* Clean up again */
2115     ok( pRtlDeleteFunctionTable( (PRUNTIME_FUNCTION)table ),
2116         "RtlDeleteFunctionTable failed for table = %p\n", (PVOID)table );
2117     ok( !pRtlDeleteFunctionTable( (PRUNTIME_FUNCTION)table ),
2118         "RtlDeleteFunctionTable returned success for nonexistent table = %p\n", (PVOID)table );
2119 
2120 }
2121 
2122 static int termination_handler_called;
2123 static void WINAPI termination_handler(ULONG flags, ULONG64 frame)
2124 {
2125     termination_handler_called++;
2126 
2127     ok(flags == 1 || broken(flags == 0x401), "flags = %x\n", flags);
2128     ok(frame == 0x1234, "frame = %p\n", (void*)frame);
2129 }
2130 
2131 static void test___C_specific_handler(void)
2132 {
2133     DISPATCHER_CONTEXT dispatch;
2134     EXCEPTION_RECORD rec;
2135     CONTEXT context;
2136     ULONG64 frame;
2137     EXCEPTION_DISPOSITION ret;
2138     SCOPE_TABLE scope_table;
2139 
2140     if (!p__C_specific_handler)
2141     {
2142         win_skip("__C_specific_handler not available\n");
2143         return;
2144     }
2145 
2146     memset(&rec, 0, sizeof(rec));
2147     rec.ExceptionFlags = 2; /* EH_UNWINDING */
2148     frame = 0x1234;
2149     memset(&dispatch, 0, sizeof(dispatch));
2150     dispatch.ImageBase = (ULONG_PTR)GetModuleHandleA(NULL);
2151     dispatch.ControlPc = dispatch.ImageBase + 0x200;
2152     dispatch.HandlerData = &scope_table;
2153     dispatch.ContextRecord = &context;
2154     scope_table.Count = 1;
2155     scope_table.ScopeRecord[0].BeginAddress = 0x200;
2156     scope_table.ScopeRecord[0].EndAddress = 0x400;
2157     scope_table.ScopeRecord[0].HandlerAddress = (ULONG_PTR)termination_handler-dispatch.ImageBase;
2158     scope_table.ScopeRecord[0].JumpTarget = 0;
2159     memset(&context, 0, sizeof(context));
2160 
2161     termination_handler_called = 0;
2162     ret = p__C_specific_handler(&rec, frame, &context, &dispatch);
2163     ok(ret == ExceptionContinueSearch, "__C_specific_handler returned %x\n", ret);
2164     ok(termination_handler_called == 1, "termination_handler_called = %d\n",
2165             termination_handler_called);
2166     ok(dispatch.ScopeIndex == 1, "dispatch.ScopeIndex = %d\n", dispatch.ScopeIndex);
2167 
2168     ret = p__C_specific_handler(&rec, frame, &context, &dispatch);
2169     ok(ret == ExceptionContinueSearch, "__C_specific_handler returned %x\n", ret);
2170     ok(termination_handler_called == 1, "termination_handler_called = %d\n",
2171             termination_handler_called);
2172     ok(dispatch.ScopeIndex == 1, "dispatch.ScopeIndex = %d\n", dispatch.ScopeIndex);
2173 }
2174 
2175 #endif  /* __x86_64__ */
2176 
2177 #if defined(__i386__) || defined(__x86_64__)
2178 
2179 static DWORD WINAPI register_check_thread(void *arg)
2180 {
2181     NTSTATUS status;
2182     CONTEXT ctx;
2183 
2184     memset(&ctx, 0, sizeof(ctx));
2185     ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
2186 
2187     status = pNtGetContextThread(GetCurrentThread(), &ctx);
2188     ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %x\n", status);
2189     ok(!ctx.Dr0, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr0);
2190     ok(!ctx.Dr1, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr1);
2191     ok(!ctx.Dr2, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr2);
2192     ok(!ctx.Dr3, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr3);
2193     ok(!ctx.Dr6, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr6);
2194     ok(!ctx.Dr7, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr7);
2195 
2196     return 0;
2197 }
2198 
2199 static void test_debug_registers(void)
2200 {
2201     static const struct
2202     {
2203         ULONG_PTR dr0, dr1, dr2, dr3, dr6, dr7;
2204     }
2205     tests[] =
2206     {
2207         { 0x42424240, 0, 0x126bb070, 0x0badbad0, 0, 0xffff0115 },
2208         { 0x42424242, 0, 0x100f0fe7, 0x0abebabe, 0, 0x115 },
2209     };
2210     NTSTATUS status;
2211     CONTEXT ctx;
2212     HANDLE thread;
2213     int i;
2214 
2215     for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++)
2216     {
2217         memset(&ctx, 0, sizeof(ctx));
2218         ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
2219         ctx.Dr0 = tests[i].dr0;
2220         ctx.Dr1 = tests[i].dr1;
2221         ctx.Dr2 = tests[i].dr2;
2222         ctx.Dr3 = tests[i].dr3;
2223         ctx.Dr6 = tests[i].dr6;
2224         ctx.Dr7 = tests[i].dr7;
2225 
2226         status = pNtSetContextThread(GetCurrentThread(), &ctx);
2227         ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %08x\n", status);
2228 
2229         memset(&ctx, 0, sizeof(ctx));
2230         ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
2231 
2232         status = pNtGetContextThread(GetCurrentThread(), &ctx);
2233         ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %08x\n", status);
2234         ok(ctx.Dr0 == tests[i].dr0, "test %d: expected %lx, got %lx\n", i, tests[i].dr0, (DWORD_PTR)ctx.Dr0);
2235         ok(ctx.Dr1 == tests[i].dr1, "test %d: expected %lx, got %lx\n", i, tests[i].dr1, (DWORD_PTR)ctx.Dr1);
2236         ok(ctx.Dr2 == tests[i].dr2, "test %d: expected %lx, got %lx\n", i, tests[i].dr2, (DWORD_PTR)ctx.Dr2);
2237         ok(ctx.Dr3 == tests[i].dr3, "test %d: expected %lx, got %lx\n", i, tests[i].dr3, (DWORD_PTR)ctx.Dr3);
2238         ok((ctx.Dr6 &  0xf00f) == tests[i].dr6, "test %d: expected %lx, got %lx\n", i, tests[i].dr6, (DWORD_PTR)ctx.Dr6);
2239         ok((ctx.Dr7 & ~0xdc00) == tests[i].dr7, "test %d: expected %lx, got %lx\n", i, tests[i].dr7, (DWORD_PTR)ctx.Dr7);
2240     }
2241 
2242     memset(&ctx, 0, sizeof(ctx));
2243     ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
2244     ctx.Dr0 = 0xffffffff;
2245     ctx.Dr1 = 0xffffffff;
2246     ctx.Dr2 = 0xffffffff;
2247     ctx.Dr3 = 0xffffffff;
2248     ctx.Dr6 = 0xffffffff;
2249     ctx.Dr7 = 0x00000400;
2250     status = pNtSetContextThread(GetCurrentThread(), &ctx);
2251     ok(status == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", status);
2252 
2253     thread = CreateThread(NULL, 0, register_check_thread, NULL, CREATE_SUSPENDED, NULL);
2254     ok(thread != INVALID_HANDLE_VALUE, "CreateThread failed with %d\n", GetLastError());
2255 
2256     ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
2257     status = pNtGetContextThread(thread, &ctx);
2258     ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %x\n", status);
2259     ok(!ctx.Dr0, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr0);
2260     ok(!ctx.Dr1, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr1);
2261     ok(!ctx.Dr2, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr2);
2262     ok(!ctx.Dr3, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr3);
2263     ok(!ctx.Dr6, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr6);
2264     ok(!ctx.Dr7, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr7);
2265 
2266     ResumeThread(thread);
2267     WaitForSingleObject(thread, 10000);
2268     CloseHandle(thread);
2269 }
2270 
2271 static DWORD outputdebugstring_exceptions;
2272 
2273 static LONG CALLBACK outputdebugstring_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
2274 {
2275     PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
2276     trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress);
2277 
2278     ok(rec->ExceptionCode == DBG_PRINTEXCEPTION_C, "ExceptionCode is %08x instead of %08x\n",
2279         rec->ExceptionCode, DBG_PRINTEXCEPTION_C);
2280     ok(rec->NumberParameters == 2, "ExceptionParameters is %d instead of 2\n", rec->NumberParameters);
2281     ok(rec->ExceptionInformation[0] == 12, "ExceptionInformation[0] = %d instead of 12\n", (DWORD)rec->ExceptionInformation[0]);
2282     ok(!strcmp((char *)rec->ExceptionInformation[1], "Hello World"),
2283         "ExceptionInformation[1] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[1]);
2284 
2285     outputdebugstring_exceptions++;
2286     return EXCEPTION_CONTINUE_SEARCH;
2287 }
2288 
2289 static void test_outputdebugstring(DWORD numexc)
2290 {
2291     PVOID vectored_handler;
2292 
2293     if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler)
2294     {
2295         skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n");
2296         return;
2297     }
2298 
2299     vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &outputdebugstring_vectored_handler);
2300     ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
2301 
2302     outputdebugstring_exceptions = 0;
2303     OutputDebugStringA("Hello World");
2304 
2305     ok(outputdebugstring_exceptions == numexc, "OutputDebugStringA generated %d exceptions, expected %d\n",
2306        outputdebugstring_exceptions, numexc);
2307 
2308     pRtlRemoveVectoredExceptionHandler(vectored_handler);
2309 }
2310 
2311 static DWORD ripevent_exceptions;
2312 
2313 static LONG CALLBACK ripevent_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
2314 {
2315     PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
2316     trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress);
2317 
2318     ok(rec->ExceptionCode == DBG_RIPEXCEPTION, "ExceptionCode is %08x instead of %08x\n",
2319        rec->ExceptionCode, DBG_RIPEXCEPTION);
2320     ok(rec->NumberParameters == 2, "ExceptionParameters is %d instead of 2\n", rec->NumberParameters);
2321     ok(rec->ExceptionInformation[0] == 0x11223344, "ExceptionInformation[0] = %08x instead of %08x\n",
2322        (NTSTATUS)rec->ExceptionInformation[0], 0x11223344);
2323     ok(rec->ExceptionInformation[1] == 0x55667788, "ExceptionInformation[1] = %08x instead of %08x\n",
2324        (NTSTATUS)rec->ExceptionInformation[1], 0x55667788);
2325 
2326     ripevent_exceptions++;
2327     return (rec->ExceptionCode == DBG_RIPEXCEPTION) ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH;
2328 }
2329 
2330 static void test_ripevent(DWORD numexc)
2331 {
2332     EXCEPTION_RECORD record;
2333     PVOID vectored_handler;
2334 
2335     if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler || !pRtlRaiseException)
2336     {
2337         skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler or RtlRaiseException not found\n");
2338         return;
2339     }
2340 
2341     vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &ripevent_vectored_handler);
2342     ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
2343 
2344     record.ExceptionCode = DBG_RIPEXCEPTION;
2345     record.ExceptionFlags = 0;
2346     record.ExceptionRecord = NULL;
2347     record.ExceptionAddress = NULL;
2348     record.NumberParameters = 2;
2349     record.ExceptionInformation[0] = 0x11223344;
2350     record.ExceptionInformation[1] = 0x55667788;
2351 
2352     ripevent_exceptions = 0;
2353     pRtlRaiseException(&record);
2354     ok(ripevent_exceptions == numexc, "RtlRaiseException generated %d exceptions, expected %d\n",
2355        ripevent_exceptions, numexc);
2356 
2357     pRtlRemoveVectoredExceptionHandler(vectored_handler);
2358 }
2359 
2360 static DWORD debug_service_exceptions;
2361 
2362 static LONG CALLBACK debug_service_handler(EXCEPTION_POINTERS *ExceptionInfo)
2363 {
2364     EXCEPTION_RECORD *rec = ExceptionInfo->ExceptionRecord;
2365     trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress);
2366 
2367     ok(rec->ExceptionCode == EXCEPTION_BREAKPOINT, "ExceptionCode is %08x instead of %08x\n",
2368        rec->ExceptionCode, EXCEPTION_BREAKPOINT);
2369 
2370 #ifdef __i386__
2371     ok(ExceptionInfo->ContextRecord->Eip == (DWORD)code_mem + 0x1c,
2372        "expected Eip = %x, got %x\n", (DWORD)code_mem + 0x1c, ExceptionInfo->ContextRecord->Eip);
2373     ok(rec->NumberParameters == (is_wow64 ? 1 : 3),
2374        "ExceptionParameters is %d instead of %d\n", rec->NumberParameters, is_wow64 ? 1 : 3);
2375     ok(rec->ExceptionInformation[0] == ExceptionInfo->ContextRecord->Eax,
2376        "expected ExceptionInformation[0] = %x, got %lx\n",
2377        ExceptionInfo->ContextRecord->Eax, rec->ExceptionInformation[0]);
2378     if (!is_wow64)
2379     {
2380         ok(rec->ExceptionInformation[1] == 0x11111111,
2381            "got ExceptionInformation[1] = %lx\n", rec->ExceptionInformation[1]);
2382         ok(rec->ExceptionInformation[2] == 0x22222222,
2383            "got ExceptionInformation[2] = %lx\n", rec->ExceptionInformation[2]);
2384     }
2385 #else
2386     ok(ExceptionInfo->ContextRecord->Rip == (DWORD_PTR)code_mem + 0x2f,
2387        "expected Rip = %lx, got %lx\n", (DWORD_PTR)code_mem + 0x2f, ExceptionInfo->ContextRecord->Rip);
2388     ok(rec->NumberParameters == 1,
2389        "ExceptionParameters is %d instead of 1\n", rec->NumberParameters);
2390     ok(rec->ExceptionInformation[0] == ExceptionInfo->ContextRecord->Rax,
2391        "expected ExceptionInformation[0] = %lx, got %lx\n",
2392        ExceptionInfo->ContextRecord->Rax, rec->ExceptionInformation[0]);
2393 #endif
2394 
2395     debug_service_exceptions++;
2396     return (rec->ExceptionCode == EXCEPTION_BREAKPOINT) ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH;
2397 }
2398 
2399 #ifdef __i386__
2400 
2401 static const BYTE call_debug_service_code[] = {
2402     0x53,                         /* pushl %ebx */
2403     0x57,                         /* pushl %edi */
2404     0x8b, 0x44, 0x24, 0x0c,       /* movl 12(%esp),%eax */
2405     0xb9, 0x11, 0x11, 0x11, 0x11, /* movl $0x11111111,%ecx */
2406     0xba, 0x22, 0x22, 0x22, 0x22, /* movl $0x22222222,%edx */
2407     0xbb, 0x33, 0x33, 0x33, 0x33, /* movl $0x33333333,%ebx */
2408     0xbf, 0x44, 0x44, 0x44, 0x44, /* movl $0x44444444,%edi */
2409     0xcd, 0x2d,                   /* int $0x2d */
2410     0xeb,                         /* jmp $+17 */
2411     0x0f, 0x1f, 0x00,             /* nop */
2412     0x31, 0xc0,                   /* xorl %eax,%eax */
2413     0xeb, 0x0c,                   /* jmp $+14 */
2414     0x90, 0x90, 0x90, 0x90,       /* nop */
2415     0x90, 0x90, 0x90, 0x90,
2416     0x90,
2417     0x31, 0xc0,                   /* xorl %eax,%eax */
2418     0x40,                         /* incl %eax */
2419     0x5f,                         /* popl %edi */
2420     0x5b,                         /* popl %ebx */
2421     0xc3,                         /* ret */
2422 };
2423 
2424 #else
2425 
2426 static const BYTE call_debug_service_code[] = {
2427     0x53,                         /* push %rbx */
2428     0x57,                         /* push %rdi */
2429     0x48, 0x89, 0xc8,             /* movl %rcx,%rax */
2430     0x48, 0xb9, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, /* movabs $0x1111111111111111,%rcx */
2431     0x48, 0xba, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* movabs $0x2222222222222222,%rdx */
2432     0x48, 0xbb, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, /* movabs $0x3333333333333333,%rbx */
2433     0x48, 0xbf, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, /* movabs $0x4444444444444444,%rdi */
2434     0xcd, 0x2d,                   /* int $0x2d */
2435     0xeb,                         /* jmp $+17 */
2436     0x0f, 0x1f, 0x00,             /* nop */
2437     0x48, 0x31, 0xc0,             /* xor %rax,%rax */
2438     0xeb, 0x0e,                   /* jmp $+16 */
2439     0x90, 0x90, 0x90, 0x90,       /* nop */
2440     0x90, 0x90, 0x90, 0x90,
2441     0x48, 0x31, 0xc0,             /* xor %rax,%rax */
2442     0x48, 0xff, 0xc0,             /* inc %rax */
2443     0x5f,                         /* pop %rdi */
2444     0x5b,                         /* pop %rbx */
2445     0xc3,                         /* ret */
2446 };
2447 
2448 #endif
2449 
2450 static void test_debug_service(DWORD numexc)
2451 {
2452     DWORD (CDECL *func)(DWORD_PTR) = code_mem;
2453     DWORD expected_exc, expected_ret;
2454     void *vectored_handler;
2455     DWORD ret;
2456 
2457     /* code will return 0 if execution resumes immediately after "int $0x2d", otherwise 1 */
2458     memcpy(code_mem, call_debug_service_code, sizeof(call_debug_service_code));
2459 
2460     vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &debug_service_handler);
2461     ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
2462 
2463     expected_exc = numexc;
2464     expected_ret = (numexc != 0);
2465 
2466     /* BREAKPOINT_BREAK */
2467     debug_service_exceptions = 0;
2468     ret = func(0);
2469     ok(debug_service_exceptions == expected_exc,
2470        "BREAKPOINT_BREAK generated %u exceptions, expected %u\n",
2471        debug_service_exceptions, expected_exc);
2472     ok(ret == expected_ret,
2473        "BREAKPOINT_BREAK returned %u, expected %u\n", ret, expected_ret);
2474 
2475     /* BREAKPOINT_PROMPT */
2476     debug_service_exceptions = 0;
2477     ret = func(2);
2478     ok(debug_service_exceptions == expected_exc,
2479        "BREAKPOINT_PROMPT generated %u exceptions, expected %u\n",
2480        debug_service_exceptions, expected_exc);
2481     ok(ret == expected_ret,
2482        "BREAKPOINT_PROMPT returned %u, expected %u\n", ret, expected_ret);
2483 
2484     /* invalid debug service */
2485     debug_service_exceptions = 0;
2486     ret = func(6);
2487     ok(debug_service_exceptions == expected_exc,
2488        "invalid debug service generated %u exceptions, expected %u\n",
2489        debug_service_exceptions, expected_exc);
2490     ok(ret == expected_ret,
2491       "invalid debug service returned %u, expected %u\n", ret, expected_ret);
2492 
2493     expected_exc = (is_wow64 ? numexc : 0);
2494     expected_ret = (is_wow64 && numexc);
2495 
2496     /* BREAKPOINT_PRINT */
2497     debug_service_exceptions = 0;
2498     ret = func(1);
2499     ok(debug_service_exceptions == expected_exc,
2500        "BREAKPOINT_PRINT generated %u exceptions, expected %u\n",
2501        debug_service_exceptions, expected_exc);
2502     ok(ret == expected_ret,
2503        "BREAKPOINT_PRINT returned %u, expected %u\n", ret, expected_ret);
2504 
2505     /* BREAKPOINT_LOAD_SYMBOLS */
2506     debug_service_exceptions = 0;
2507     ret = func(3);
2508     ok(debug_service_exceptions == expected_exc,
2509        "BREAKPOINT_LOAD_SYMBOLS generated %u exceptions, expected %u\n",
2510        debug_service_exceptions, expected_exc);
2511     ok(ret == expected_ret,
2512        "BREAKPOINT_LOAD_SYMBOLS returned %u, expected %u\n", ret, expected_ret);
2513 
2514     /* BREAKPOINT_UNLOAD_SYMBOLS */
2515     debug_service_exceptions = 0;
2516     ret = func(4);
2517     ok(debug_service_exceptions == expected_exc,
2518        "BREAKPOINT_UNLOAD_SYMBOLS generated %u exceptions, expected %u\n",
2519        debug_service_exceptions, expected_exc);
2520     ok(ret == expected_ret,
2521        "BREAKPOINT_UNLOAD_SYMBOLS returned %u, expected %u\n", ret, expected_ret);
2522 
2523     /* BREAKPOINT_COMMAND_STRING */
2524     debug_service_exceptions = 0;
2525     ret = func(5);
2526     ok(debug_service_exceptions == expected_exc || broken(debug_service_exceptions == numexc),
2527        "BREAKPOINT_COMMAND_STRING generated %u exceptions, expected %u\n",
2528        debug_service_exceptions, expected_exc);
2529     ok(ret == expected_ret || broken(ret == (numexc != 0)),
2530        "BREAKPOINT_COMMAND_STRING returned %u, expected %u\n", ret, expected_ret);
2531 
2532     pRtlRemoveVectoredExceptionHandler(vectored_handler);
2533 }
2534 
2535 static DWORD breakpoint_exceptions;
2536 
2537 static LONG CALLBACK breakpoint_handler(EXCEPTION_POINTERS *ExceptionInfo)
2538 {
2539     EXCEPTION_RECORD *rec = ExceptionInfo->ExceptionRecord;
2540     trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress);
2541 
2542     ok(rec->ExceptionCode == EXCEPTION_BREAKPOINT, "ExceptionCode is %08x instead of %08x\n",
2543        rec->ExceptionCode, EXCEPTION_BREAKPOINT);
2544 
2545 #ifdef __i386__
2546     ok(ExceptionInfo->ContextRecord->Eip == (DWORD)code_mem + 1,
2547        "expected Eip = %x, got %x\n", (DWORD)code_mem + 1, ExceptionInfo->ContextRecord->Eip);
2548     ok(rec->NumberParameters == (is_wow64 ? 1 : 3),
2549        "ExceptionParameters is %d instead of %d\n", rec->NumberParameters, is_wow64 ? 1 : 3);
2550     ok(rec->ExceptionInformation[0] == 0,
2551        "got ExceptionInformation[0] = %lx\n", rec->ExceptionInformation[0]);
2552     ExceptionInfo->ContextRecord->Eip = (DWORD)code_mem + 2;
2553 #else
2554     ok(ExceptionInfo->ContextRecord->Rip == (DWORD_PTR)code_mem + 1,
2555        "expected Rip = %lx, got %lx\n", (DWORD_PTR)code_mem + 1, ExceptionInfo->ContextRecord->Rip);
2556     ok(rec->NumberParameters == 1,
2557        "ExceptionParameters is %d instead of 1\n", rec->NumberParameters);
2558     ok(rec->ExceptionInformation[0] == 0,
2559        "got ExceptionInformation[0] = %lx\n", rec->ExceptionInformation[0]);
2560     ExceptionInfo->ContextRecord->Rip = (DWORD_PTR)code_mem + 2;
2561 #endif
2562 
2563     breakpoint_exceptions++;
2564     return (rec->ExceptionCode == EXCEPTION_BREAKPOINT) ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH;
2565 }
2566 
2567 static const BYTE breakpoint_code[] = {
2568     0xcd, 0x03,                   /* int $0x3 */
2569     0xc3,                         /* ret */
2570 };
2571 
2572 static void test_breakpoint(DWORD numexc)
2573 {
2574     DWORD (CDECL *func)(void) = code_mem;
2575     void *vectored_handler;
2576 
2577     memcpy(code_mem, breakpoint_code, sizeof(breakpoint_code));
2578 
2579     vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &breakpoint_handler);
2580     ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
2581 
2582     breakpoint_exceptions = 0;
2583     func();
2584     ok(breakpoint_exceptions == numexc, "int $0x3 generated %u exceptions, expected %u\n",
2585        breakpoint_exceptions, numexc);
2586 
2587     pRtlRemoveVectoredExceptionHandler(vectored_handler);
2588 }
2589 
2590 static DWORD invalid_handle_exceptions;
2591 
2592 static LONG CALLBACK invalid_handle_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
2593 {
2594     PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
2595     trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress);
2596 
2597     ok(rec->ExceptionCode == EXCEPTION_INVALID_HANDLE, "ExceptionCode is %08x instead of %08x\n",
2598        rec->ExceptionCode, EXCEPTION_INVALID_HANDLE);
2599     ok(rec->NumberParameters == 0, "ExceptionParameters is %d instead of 0\n", rec->NumberParameters);
2600 
2601     invalid_handle_exceptions++;
2602     return (rec->ExceptionCode == EXCEPTION_INVALID_HANDLE) ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH;
2603 }
2604 
2605 static void test_closehandle(DWORD numexc)
2606 {
2607     PVOID vectored_handler;
2608     NTSTATUS status;
2609     DWORD res;
2610 
2611     if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler || !pRtlRaiseException)
2612     {
2613         skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler or RtlRaiseException not found\n");
2614         return;
2615     }
2616 
2617     vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &invalid_handle_vectored_handler);
2618     ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
2619 
2620     invalid_handle_exceptions = 0;
2621     res = CloseHandle((HANDLE)0xdeadbeef);
2622     ok(!res, "CloseHandle(0xdeadbeef) unexpectedly succeeded\n");
2623     ok(GetLastError() == ERROR_INVALID_HANDLE, "wrong error code %d instead of %d\n",
2624        GetLastError(), ERROR_INVALID_HANDLE);
2625     ok(invalid_handle_exceptions == numexc, "CloseHandle generated %d exceptions, expected %d\n",
2626        invalid_handle_exceptions, numexc);
2627 
2628     invalid_handle_exceptions = 0;
2629     status = pNtClose((HANDLE)0xdeadbeef);
2630     ok(status == STATUS_INVALID_HANDLE, "NtClose(0xdeadbeef) returned status %08x\n", status);
2631     ok(invalid_handle_exceptions == numexc, "NtClose generated %d exceptions, expected %d\n",
2632        invalid_handle_exceptions, numexc);
2633 
2634     pRtlRemoveVectoredExceptionHandler(vectored_handler);
2635 }
2636 
2637 static void test_vectored_continue_handler(void)
2638 {
2639     PVOID handler1, handler2;
2640     ULONG ret;
2641 
2642     if (!pRtlAddVectoredContinueHandler || !pRtlRemoveVectoredContinueHandler)
2643     {
2644         skip("RtlAddVectoredContinueHandler or RtlRemoveVectoredContinueHandler not found\n");
2645         return;
2646     }
2647 
2648     handler1 = pRtlAddVectoredContinueHandler(TRUE, (void *)0xdeadbeef);
2649     ok(handler1 != 0, "RtlAddVectoredContinueHandler failed\n");
2650 
2651     handler2 = pRtlAddVectoredContinueHandler(TRUE, (void *)0xdeadbeef);
2652     ok(handler2 != 0, "RtlAddVectoredContinueHandler failed\n");
2653     ok(handler1 != handler2, "RtlAddVectoredContinueHandler returned same handler\n");
2654 
2655     if (pRtlRemoveVectoredExceptionHandler)
2656     {
2657         ret = pRtlRemoveVectoredExceptionHandler(handler1);
2658         ok(!ret, "RtlRemoveVectoredExceptionHandler succeeded\n");
2659     }
2660 
2661     ret = pRtlRemoveVectoredContinueHandler(handler1);
2662     ok(ret, "RtlRemoveVectoredContinueHandler failed\n");
2663 
2664     ret = pRtlRemoveVectoredContinueHandler(handler2);
2665     ok(ret, "RtlRemoveVectoredContinueHandler failed\n");
2666 
2667     ret = pRtlRemoveVectoredContinueHandler(handler1);
2668     ok(!ret, "RtlRemoveVectoredContinueHandler succeeded\n");
2669 
2670     ret = pRtlRemoveVectoredContinueHandler((void *)0x11223344);
2671     ok(!ret, "RtlRemoveVectoredContinueHandler succeeded\n");
2672 }
2673 #endif /* defined(__i386__) || defined(__x86_64__) */
2674 
2675 START_TEST(exception)
2676 {
2677     HMODULE hntdll = GetModuleHandleA("ntdll.dll");
2678 #if defined(__x86_64__)
2679     HMODULE hmsvcrt = LoadLibraryA("msvcrt.dll");
2680 #endif
2681 
2682 #ifdef __REACTOS__
2683     if (!winetest_interactive &&
2684         !strcmp(winetest_platform, "windows"))
2685     {
2686         skip("ROSTESTS-240: Skipping ntdll_winetest:exception because it hangs on WHS-Testbot. Set winetest_interactive to run it anyway.\n");
2687         return;
2688     }
2689 #endif
2690     code_mem = VirtualAlloc(NULL, 65536, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
2691     if(!code_mem) {
2692         trace("VirtualAlloc failed\n");
2693         return;
2694     }
2695 
2696     pNtGetContextThread  = (void *)GetProcAddress( hntdll, "NtGetContextThread" );
2697     pNtSetContextThread  = (void *)GetProcAddress( hntdll, "NtSetContextThread" );
2698     pNtReadVirtualMemory = (void *)GetProcAddress( hntdll, "NtReadVirtualMemory" );
2699     pNtClose             = (void *)GetProcAddress( hntdll, "NtClose" );
2700     pRtlUnwind           = (void *)GetProcAddress( hntdll, "RtlUnwind" );
2701     pRtlRaiseException   = (void *)GetProcAddress( hntdll, "RtlRaiseException" );
2702     pRtlCaptureContext   = (void *)GetProcAddress( hntdll, "RtlCaptureContext" );
2703     pNtTerminateProcess  = (void *)GetProcAddress( hntdll, "NtTerminateProcess" );
2704     pRtlAddVectoredExceptionHandler    = (void *)GetProcAddress( hntdll,
2705                                                                  "RtlAddVectoredExceptionHandler" );
2706     pRtlRemoveVectoredExceptionHandler = (void *)GetProcAddress( hntdll,
2707                                                                  "RtlRemoveVectoredExceptionHandler" );
2708     pRtlAddVectoredContinueHandler     = (void *)GetProcAddress( hntdll,
2709                                                                  "RtlAddVectoredContinueHandler" );
2710     pRtlRemoveVectoredContinueHandler  = (void *)GetProcAddress( hntdll,
2711                                                                  "RtlRemoveVectoredContinueHandler" );
2712     pNtQueryInformationProcess         = (void*)GetProcAddress( hntdll,
2713                                                                  "NtQueryInformationProcess" );
2714     pNtSetInformationProcess           = (void*)GetProcAddress( hntdll,
2715                                                                  "NtSetInformationProcess" );
2716     pIsWow64Process = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");
2717 
2718 #ifdef __i386__
2719     if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE;
2720 
2721     if (pRtlAddVectoredExceptionHandler && pRtlRemoveVectoredExceptionHandler)
2722         have_vectored_api = TRUE;
2723     else
2724         skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n");
2725 
2726     my_argc = winetest_get_mainargs( &my_argv );
2727     if (my_argc >= 4)
2728     {
2729         void *addr;
2730         sscanf( my_argv[3], "%p", &addr );
2731 
2732         if (addr != &test_stage)
2733         {
2734             skip( "child process not mapped at same address (%p/%p)\n", &test_stage, addr);
2735             return;
2736         }
2737 
2738         /* child must be run under a debugger */
2739         if (!NtCurrentTeb()->Peb->BeingDebugged)
2740         {
2741             ok(FALSE, "child process not being debugged?\n");
2742             return;
2743         }
2744 
2745         if (pRtlRaiseException)
2746         {
2747             test_stage = 1;
2748             run_rtlraiseexception_test(0x12345);
2749             run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
2750             run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
2751             test_stage = 2;
2752             run_rtlraiseexception_test(0x12345);
2753             run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
2754             run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
2755             test_stage = 3;
2756             test_outputdebugstring(0);
2757             test_stage = 4;
2758             test_outputdebugstring(2);
2759             test_stage = 5;
2760             test_ripevent(0);
2761             test_stage = 6;
2762             test_ripevent(1);
2763             test_stage = 7;
2764             test_debug_service(0);
2765             test_stage = 8;
2766             test_debug_service(1);
2767             test_stage = 9;
2768             test_breakpoint(0);
2769             test_stage = 10;
2770             test_breakpoint(1);
2771             test_stage = 11;
2772             test_closehandle(0);
2773             test_stage = 12;
2774             test_closehandle(1);
2775         }
2776         else
2777             skip( "RtlRaiseException not found\n" );
2778 
2779         /* rest of tests only run in parent */
2780         return;
2781     }
2782 
2783     test_unwind();
2784     test_exceptions();
2785     test_rtlraiseexception();
2786     test_debug_registers();
2787     test_outputdebugstring(1);
2788     test_ripevent(1);
2789     test_debug_service(1);
2790     test_breakpoint(1);
2791     test_closehandle(0);
2792     test_vectored_continue_handler();
2793     test_debugger();
2794     test_simd_exceptions();
2795     test_fpu_exceptions();
2796     test_dpe_exceptions();
2797     test_prot_fault();
2798     test_thread_context();
2799 
2800 #elif defined(__x86_64__)
2801     pRtlAddFunctionTable               = (void *)GetProcAddress( hntdll,
2802                                                                  "RtlAddFunctionTable" );
2803     pRtlDeleteFunctionTable            = (void *)GetProcAddress( hntdll,
2804                                                                  "RtlDeleteFunctionTable" );
2805     pRtlInstallFunctionTableCallback   = (void *)GetProcAddress( hntdll,
2806                                                                  "RtlInstallFunctionTableCallback" );
2807     pRtlLookupFunctionEntry            = (void *)GetProcAddress( hntdll,
2808                                                                  "RtlLookupFunctionEntry" );
2809     p__C_specific_handler              = (void *)GetProcAddress( hntdll,
2810                                                                  "__C_specific_handler" );
2811     pRtlCaptureContext                 = (void *)GetProcAddress( hntdll,
2812                                                                  "RtlCaptureContext" );
2813     pRtlRestoreContext                 = (void *)GetProcAddress( hntdll,
2814                                                                  "RtlRestoreContext" );
2815     pRtlUnwindEx                       = (void *)GetProcAddress( hntdll,
2816                                                                  "RtlUnwindEx" );
2817     p_setjmp                           = (void *)GetProcAddress( hmsvcrt,
2818                                                                  "_setjmp" );
2819 
2820     test_debug_registers();
2821     test_outputdebugstring(1);
2822     test_ripevent(1);
2823     test_debug_service(1);
2824     test_breakpoint(1);
2825     test_closehandle(0);
2826     test_vectored_continue_handler();
2827     test_virtual_unwind();
2828     test___C_specific_handler();
2829     test_restore_context();
2830 
2831     if (pRtlAddFunctionTable && pRtlDeleteFunctionTable && pRtlInstallFunctionTableCallback && pRtlLookupFunctionEntry)
2832       test_dynamic_unwind();
2833     else
2834       skip( "Dynamic unwind functions not found\n" );
2835 
2836 #endif
2837 
2838     VirtualFree(code_mem, 0, MEM_RELEASE);
2839 }
2840