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