ntdll/tests: Add a broken result for Win7u in test_extended_context().
[wine/zf.git] / dlls / ntdll / tests / exception.c
blobeb03ee64bacec3cb69e9c9463f4b672bf6bc9da5
1 /*
2 * Unit test suite for ntdll exceptions
4 * Copyright 2005 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
22 #include <stdio.h>
24 #include "ntstatus.h"
25 #define WIN32_NO_STATUS
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winnt.h"
29 #include "winreg.h"
30 #include "winternl.h"
31 #include "ddk/wdm.h"
32 #include "excpt.h"
33 #include "wine/test.h"
34 #include "intrin.h"
36 static void *code_mem;
38 static NTSTATUS (WINAPI *pNtGetContextThread)(HANDLE,CONTEXT*);
39 static NTSTATUS (WINAPI *pNtSetContextThread)(HANDLE,CONTEXT*);
40 static NTSTATUS (WINAPI *pRtlRaiseException)(EXCEPTION_RECORD *rec);
41 static PVOID (WINAPI *pRtlUnwind)(PVOID, PVOID, PEXCEPTION_RECORD, PVOID);
42 static VOID (WINAPI *pRtlCaptureContext)(CONTEXT*);
43 static PVOID (WINAPI *pRtlAddVectoredExceptionHandler)(ULONG first, PVECTORED_EXCEPTION_HANDLER func);
44 static ULONG (WINAPI *pRtlRemoveVectoredExceptionHandler)(PVOID handler);
45 static PVOID (WINAPI *pRtlAddVectoredContinueHandler)(ULONG first, PVECTORED_EXCEPTION_HANDLER func);
46 static ULONG (WINAPI *pRtlRemoveVectoredContinueHandler)(PVOID handler);
47 static void (WINAPI *pRtlSetUnhandledExceptionFilter)(PRTL_EXCEPTION_FILTER filter);
48 static ULONG64 (WINAPI *pRtlGetEnabledExtendedFeatures)(ULONG64);
49 static NTSTATUS (WINAPI *pRtlGetExtendedContextLength)(ULONG context_flags, ULONG *length);
50 static NTSTATUS (WINAPI *pRtlGetExtendedContextLength2)(ULONG context_flags, ULONG *length, ULONG64 compaction_mask);
51 static NTSTATUS (WINAPI *pRtlInitializeExtendedContext)(void *context, ULONG context_flags, CONTEXT_EX **context_ex);
52 static NTSTATUS (WINAPI *pRtlInitializeExtendedContext2)(void *context, ULONG context_flags, CONTEXT_EX **context_ex,
53 ULONG64 compaction_mask);
54 static NTSTATUS (WINAPI *pRtlCopyExtendedContext)(CONTEXT_EX *dst, ULONG context_flags, CONTEXT_EX *src);
55 static void * (WINAPI *pRtlLocateExtendedFeature)(CONTEXT_EX *context_ex, ULONG feature_id, ULONG *length);
56 static void * (WINAPI *pRtlLocateLegacyContext)(CONTEXT_EX *context_ex, ULONG *length);
57 static void (WINAPI *pRtlSetExtendedFeaturesMask)(CONTEXT_EX *context_ex, ULONG64 feature_mask);
58 static ULONG64 (WINAPI *pRtlGetExtendedFeaturesMask)(CONTEXT_EX *context_ex);
59 static NTSTATUS (WINAPI *pNtReadVirtualMemory)(HANDLE, const void*, void*, SIZE_T, SIZE_T*);
60 static NTSTATUS (WINAPI *pNtTerminateProcess)(HANDLE handle, LONG exit_code);
61 static NTSTATUS (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
62 static NTSTATUS (WINAPI *pNtQueryInformationThread)(HANDLE, THREADINFOCLASS, PVOID, ULONG, PULONG);
63 static NTSTATUS (WINAPI *pNtSetInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG);
64 static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL);
65 static NTSTATUS (WINAPI *pNtClose)(HANDLE);
66 static NTSTATUS (WINAPI *pNtSuspendProcess)(HANDLE process);
67 static NTSTATUS (WINAPI *pNtResumeProcess)(HANDLE process);
68 static BOOL (WINAPI *pInitializeContext)(void *buffer, DWORD context_flags, CONTEXT **context,
69 DWORD *length);
70 static BOOL (WINAPI *pInitializeContext2)(void *buffer, DWORD context_flags, CONTEXT **context,
71 DWORD *length, ULONG64 compaction_mask);
72 static void * (WINAPI *pLocateXStateFeature)(CONTEXT *context, DWORD feature_id, DWORD *length);
73 static BOOL (WINAPI *pSetXStateFeaturesMask)(CONTEXT *context, DWORD64 feature_mask);
74 static BOOL (WINAPI *pGetXStateFeaturesMask)(CONTEXT *context, DWORD64 *feature_mask);
75 static BOOL (WINAPI *pCopyContext)(CONTEXT *dst, DWORD context_flags, CONTEXT *src);
77 #define RTL_UNLOAD_EVENT_TRACE_NUMBER 64
79 typedef struct _RTL_UNLOAD_EVENT_TRACE
81 void *BaseAddress;
82 SIZE_T SizeOfImage;
83 ULONG Sequence;
84 ULONG TimeDateStamp;
85 ULONG CheckSum;
86 WCHAR ImageName[32];
87 } RTL_UNLOAD_EVENT_TRACE, *PRTL_UNLOAD_EVENT_TRACE;
89 static RTL_UNLOAD_EVENT_TRACE *(WINAPI *pRtlGetUnloadEventTrace)(void);
90 static void (WINAPI *pRtlGetUnloadEventTraceEx)(ULONG **element_size, ULONG **element_count, void **event_trace);
92 #ifndef EH_NESTED_CALL
93 #define EH_NESTED_CALL 0x10
94 #endif
96 #if defined(__x86_64__)
97 typedef struct
99 ULONG Count;
100 struct
102 ULONG BeginAddress;
103 ULONG EndAddress;
104 ULONG HandlerAddress;
105 ULONG JumpTarget;
106 } ScopeRecord[1];
107 } SCOPE_TABLE;
109 typedef struct _SETJMP_FLOAT128
111 unsigned __int64 DECLSPEC_ALIGN(16) Part[2];
112 } SETJMP_FLOAT128;
114 typedef struct _JUMP_BUFFER
116 unsigned __int64 Frame;
117 unsigned __int64 Rbx;
118 unsigned __int64 Rsp;
119 unsigned __int64 Rbp;
120 unsigned __int64 Rsi;
121 unsigned __int64 Rdi;
122 unsigned __int64 R12;
123 unsigned __int64 R13;
124 unsigned __int64 R14;
125 unsigned __int64 R15;
126 unsigned __int64 Rip;
127 unsigned __int64 Spare;
128 SETJMP_FLOAT128 Xmm6;
129 SETJMP_FLOAT128 Xmm7;
130 SETJMP_FLOAT128 Xmm8;
131 SETJMP_FLOAT128 Xmm9;
132 SETJMP_FLOAT128 Xmm10;
133 SETJMP_FLOAT128 Xmm11;
134 SETJMP_FLOAT128 Xmm12;
135 SETJMP_FLOAT128 Xmm13;
136 SETJMP_FLOAT128 Xmm14;
137 SETJMP_FLOAT128 Xmm15;
138 } _JUMP_BUFFER;
140 typedef union _UNWIND_CODE
142 struct
144 BYTE CodeOffset;
145 BYTE UnwindOp : 4;
146 BYTE OpInfo : 4;
147 } s;
148 USHORT FrameOffset;
149 } UNWIND_CODE;
151 typedef struct _UNWIND_INFO
153 BYTE Version : 3;
154 BYTE Flags : 5;
155 BYTE SizeOfProlog;
156 BYTE CountOfCodes;
157 BYTE FrameRegister : 4;
158 BYTE FrameOffset : 4;
159 UNWIND_CODE UnwindCode[1]; /* actually CountOfCodes (aligned) */
161 * union
163 * OPTIONAL ULONG ExceptionHandler;
164 * OPTIONAL ULONG FunctionEntry;
165 * };
166 * OPTIONAL ULONG ExceptionData[];
168 } UNWIND_INFO;
170 static BOOLEAN (CDECL *pRtlAddFunctionTable)(RUNTIME_FUNCTION*, DWORD, DWORD64);
171 static BOOLEAN (CDECL *pRtlDeleteFunctionTable)(RUNTIME_FUNCTION*);
172 static BOOLEAN (CDECL *pRtlInstallFunctionTableCallback)(DWORD64, DWORD64, DWORD, PGET_RUNTIME_FUNCTION_CALLBACK, PVOID, PCWSTR);
173 static PRUNTIME_FUNCTION (WINAPI *pRtlLookupFunctionEntry)(ULONG64, ULONG64*, UNWIND_HISTORY_TABLE*);
174 static DWORD (WINAPI *pRtlAddGrowableFunctionTable)(void**, RUNTIME_FUNCTION*, DWORD, DWORD, ULONG_PTR, ULONG_PTR);
175 static void (WINAPI *pRtlGrowFunctionTable)(void*, DWORD);
176 static void (WINAPI *pRtlDeleteGrowableFunctionTable)(void*);
177 static EXCEPTION_DISPOSITION (WINAPI *p__C_specific_handler)(EXCEPTION_RECORD*, ULONG64, CONTEXT*, DISPATCHER_CONTEXT*);
178 static VOID (WINAPI *pRtlCaptureContext)(CONTEXT*);
179 static VOID (CDECL *pRtlRestoreContext)(CONTEXT*, EXCEPTION_RECORD*);
180 static NTSTATUS (WINAPI *pRtlWow64GetThreadContext)(HANDLE, WOW64_CONTEXT *);
181 static NTSTATUS (WINAPI *pRtlWow64SetThreadContext)(HANDLE, const WOW64_CONTEXT *);
182 static VOID (WINAPI *pRtlUnwindEx)(VOID*, VOID*, EXCEPTION_RECORD*, VOID*, CONTEXT*, UNWIND_HISTORY_TABLE*);
183 static int (CDECL *p_setjmp)(_JUMP_BUFFER*);
184 #endif
186 static int my_argc;
187 static char** my_argv;
188 static BOOL is_wow64;
189 static BOOL have_vectored_api;
190 static int test_stage;
192 #if defined(__i386__) || defined(__x86_64__)
193 static void test_debugger_xstate(HANDLE thread, CONTEXT *ctx, int stage)
195 char context_buffer[sizeof(CONTEXT) + sizeof(CONTEXT_EX) + sizeof(XSTATE) + 63];
196 CONTEXT_EX *c_ex;
197 NTSTATUS status;
198 YMMCONTEXT *ymm;
199 CONTEXT *xctx;
200 DWORD length;
201 XSTATE *xs;
202 M128A *xmm;
203 BOOL bret;
205 if (!pRtlGetEnabledExtendedFeatures || !pRtlGetEnabledExtendedFeatures(1 << XSTATE_AVX))
206 return;
208 if (stage == 14)
209 return;
211 length = sizeof(context_buffer);
212 bret = pInitializeContext(context_buffer, ctx->ContextFlags | CONTEXT_XSTATE, &xctx, &length);
213 ok(bret, "Got unexpected bret %#x, GetLastError() %u.\n", bret, GetLastError());
215 ymm = pLocateXStateFeature(xctx, XSTATE_AVX, &length);
216 ok(!!ymm, "Got zero ymm.\n");
217 memset(ymm, 0xcc, sizeof(*ymm));
219 xmm = pLocateXStateFeature(xctx, XSTATE_LEGACY_SSE, &length);
220 ok(length == sizeof(*xmm) * (sizeof(void *) == 8 ? 16 : 8), "Got unexpected length %#x.\n", length);
221 ok(!!xmm, "Got zero xmm.\n");
222 memset(xmm, 0xcc, length);
224 status = pNtGetContextThread(thread, xctx);
225 ok(!status, "NtSetContextThread failed with 0x%x\n", status);
227 c_ex = (CONTEXT_EX *)(xctx + 1);
228 xs = (XSTATE *)((char *)c_ex + c_ex->XState.Offset);
229 ok((xs->Mask & 7) == 4 || broken(!xs->Mask) /* Win7 */,
230 "Got unexpected xs->Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
232 ok(xmm[0].Low == 0x200000001, "Got unexpected data %s.\n", wine_dbgstr_longlong(xmm[0].Low));
233 ok(xmm[0].High == 0x400000003, "Got unexpected data %s.\n", wine_dbgstr_longlong(xmm[0].High));
235 ok(ymm->Ymm0.Low == 0x600000005 || broken(!xs->Mask && ymm->Ymm0.Low == 0xcccccccccccccccc) /* Win7 */,
236 "Got unexpected data %s.\n", wine_dbgstr_longlong(ymm->Ymm0.Low));
237 ok(ymm->Ymm0.High == 0x800000007 || broken(!xs->Mask && ymm->Ymm0.High == 0xcccccccccccccccc) /* Win7 */,
238 "Got unexpected data %s.\n", wine_dbgstr_longlong(ymm->Ymm0.High));
240 xmm = pLocateXStateFeature(ctx, XSTATE_LEGACY_SSE, &length);
241 ok(!!xmm, "Got zero xmm.\n");
243 xmm[0].Low = 0x2828282828282828;
244 xmm[0].High = xmm[0].Low;
245 ymm->Ymm0.Low = 0x4848484848484848;
246 ymm->Ymm0.High = ymm->Ymm0.Low;
248 status = pNtSetContextThread(thread, xctx);
249 ok(!status, "NtSetContextThread failed with 0x%x\n", status);
251 #endif
253 #ifdef __i386__
255 #ifndef __WINE_WINTRNL_H
256 #define ProcessExecuteFlags 0x22
257 #define MEM_EXECUTE_OPTION_DISABLE 0x01
258 #define MEM_EXECUTE_OPTION_ENABLE 0x02
259 #define MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION 0x04
260 #define MEM_EXECUTE_OPTION_PERMANENT 0x08
261 #endif
263 /* Test various instruction combinations that cause a protection fault on the i386,
264 * and check what the resulting exception looks like.
267 static const struct exception
269 BYTE code[18]; /* asm code */
270 BYTE offset; /* offset of faulting instruction */
271 BYTE length; /* length of faulting instruction */
272 BOOL wow64_broken; /* broken on Wow64, should be skipped */
273 NTSTATUS status; /* expected status code */
274 DWORD nb_params; /* expected number of parameters */
275 DWORD params[4]; /* expected parameters */
276 NTSTATUS alt_status; /* alternative status code */
277 DWORD alt_nb_params; /* alternative number of parameters */
278 DWORD alt_params[4]; /* alternative parameters */
279 } exceptions[] =
281 /* 0 */
282 /* test some privileged instructions */
283 { { 0xfb, 0xc3 }, /* 0: sti; ret */
284 0, 1, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
285 { { 0x6c, 0xc3 }, /* 1: insb (%dx); ret */
286 0, 1, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
287 { { 0x6d, 0xc3 }, /* 2: insl (%dx); ret */
288 0, 1, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
289 { { 0x6e, 0xc3 }, /* 3: outsb (%dx); ret */
290 0, 1, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
291 { { 0x6f, 0xc3 }, /* 4: outsl (%dx); ret */
292 0, 1, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
293 /* 5 */
294 { { 0xe4, 0x11, 0xc3 }, /* 5: inb $0x11,%al; ret */
295 0, 2, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
296 { { 0xe5, 0x11, 0xc3 }, /* 6: inl $0x11,%eax; ret */
297 0, 2, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
298 { { 0xe6, 0x11, 0xc3 }, /* 7: outb %al,$0x11; ret */
299 0, 2, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
300 { { 0xe7, 0x11, 0xc3 }, /* 8: outl %eax,$0x11; ret */
301 0, 2, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
302 { { 0xed, 0xc3 }, /* 9: inl (%dx),%eax; ret */
303 0, 1, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
304 /* 10 */
305 { { 0xee, 0xc3 }, /* 10: outb %al,(%dx); ret */
306 0, 1, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
307 { { 0xef, 0xc3 }, /* 11: outl %eax,(%dx); ret */
308 0, 1, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
309 { { 0xf4, 0xc3 }, /* 12: hlt; ret */
310 0, 1, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
311 { { 0xfa, 0xc3 }, /* 13: cli; ret */
312 0, 1, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
314 /* test long jump to invalid selector */
315 { { 0xea, 0, 0, 0, 0, 0, 0, 0xc3 }, /* 14: ljmp $0,$0; ret */
316 0, 7, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
318 /* 15 */
319 /* test iret to invalid selector */
320 { { 0x6a, 0x00, 0x6a, 0x00, 0x6a, 0x00, 0xcf, 0x83, 0xc4, 0x0c, 0xc3 },
321 /* 15: pushl $0; pushl $0; pushl $0; iret; addl $12,%esp; ret */
322 6, 1, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
324 /* test loading an invalid selector */
325 { { 0xb8, 0xef, 0xbe, 0x00, 0x00, 0x8e, 0xe8, 0xc3 }, /* 16: mov $beef,%ax; mov %ax,%gs; ret */
326 5, 2, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xbee8 } }, /* 0xbee8 or 0xffffffff */
328 /* test accessing a zero selector (%es broken on Wow64) */
329 { { 0x06, 0x31, 0xc0, 0x8e, 0xc0, 0x26, 0xa1, 0, 0, 0, 0, 0x07, 0xc3 },
330 /* push %es; xor %eax,%eax; mov %ax,%es; mov %es:(0),%ax; pop %es; ret */
331 5, 6, TRUE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
332 { { 0x0f, 0xa8, 0x31, 0xc0, 0x8e, 0xe8, 0x65, 0xa1, 0, 0, 0, 0, 0x0f, 0xa9, 0xc3 },
333 /* push %gs; xor %eax,%eax; mov %ax,%gs; mov %gs:(0),%ax; pop %gs; ret */
334 6, 6, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
336 /* test moving %cs -> %ss */
337 { { 0x0e, 0x17, 0x58, 0xc3 }, /* pushl %cs; popl %ss; popl %eax; ret */
338 1, 1, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
340 /* 20 */
341 /* test overlong instruction (limit is 15 bytes, 5 on Win7) */
342 { { 0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0xfa,0xc3 },
343 0, 16, TRUE, STATUS_ILLEGAL_INSTRUCTION, 0, { 0 },
344 STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
345 { { 0x64,0x64,0x64,0x64,0xfa,0xc3 },
346 0, 5, TRUE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
348 /* test invalid interrupt */
349 { { 0xcd, 0xff, 0xc3 }, /* int $0xff; ret */
350 0, 2, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
352 /* test moves to/from Crx */
353 { { 0x0f, 0x20, 0xc0, 0xc3 }, /* movl %cr0,%eax; ret */
354 0, 3, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
355 { { 0x0f, 0x20, 0xe0, 0xc3 }, /* movl %cr4,%eax; ret */
356 0, 3, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
357 /* 25 */
358 { { 0x0f, 0x22, 0xc0, 0xc3 }, /* movl %eax,%cr0; ret */
359 0, 3, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
360 { { 0x0f, 0x22, 0xe0, 0xc3 }, /* movl %eax,%cr4; ret */
361 0, 3, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
363 /* test moves to/from Drx */
364 { { 0x0f, 0x21, 0xc0, 0xc3 }, /* movl %dr0,%eax; ret */
365 0, 3, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
366 { { 0x0f, 0x21, 0xc8, 0xc3 }, /* movl %dr1,%eax; ret */
367 0, 3, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
368 { { 0x0f, 0x21, 0xf8, 0xc3 }, /* movl %dr7,%eax; ret */
369 0, 3, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
370 /* 30 */
371 { { 0x0f, 0x23, 0xc0, 0xc3 }, /* movl %eax,%dr0; ret */
372 0, 3, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
373 { { 0x0f, 0x23, 0xc8, 0xc3 }, /* movl %eax,%dr1; ret */
374 0, 3, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
375 { { 0x0f, 0x23, 0xf8, 0xc3 }, /* movl %eax,%dr7; ret */
376 0, 3, FALSE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
378 /* test memory reads */
379 { { 0xa1, 0xfc, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xfffffffc,%eax; ret */
380 0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffc } },
381 { { 0xa1, 0xfd, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xfffffffd,%eax; ret */
382 0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffd } },
383 /* 35 */
384 { { 0xa1, 0xfe, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xfffffffe,%eax; ret */
385 0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffe } },
386 { { 0xa1, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xffffffff,%eax; ret */
387 0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
389 /* test memory writes */
390 { { 0xa3, 0xfc, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xfffffffc; ret */
391 0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffc } },
392 { { 0xa3, 0xfd, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xfffffffd; ret */
393 0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffd } },
394 { { 0xa3, 0xfe, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xfffffffe; ret */
395 0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffe } },
396 /* 40 */
397 { { 0xa3, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xffffffff; ret */
398 0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0xffffffff } },
400 /* test exception with cleared %ds and %es (broken on Wow64) */
401 { { 0x1e, 0x06, 0x31, 0xc0, 0x8e, 0xd8, 0x8e, 0xc0, 0xfa, 0x07, 0x1f, 0xc3 },
402 /* push %ds; push %es; xorl %eax,%eax; mov %ax,%ds; mov %ax,%es; cli; pop %es; pop %ds; ret */
403 8, 1, TRUE, STATUS_PRIVILEGED_INSTRUCTION, 0 },
405 { { 0xf1, 0x90, 0xc3 }, /* icebp; nop; ret */
406 1, 1, FALSE, STATUS_SINGLE_STEP, 0 },
407 { { 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, /* mov $0xb8b8b8b8, %eax */
408 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, /* mov $0xb9b9b9b9, %ecx */
409 0xba, 0xba, 0xba, 0xba, 0xba, /* mov $0xbabababa, %edx */
410 0xcd, 0x2d, 0xc3 }, /* int $0x2d; ret */
411 17, 0, FALSE, STATUS_BREAKPOINT, 3, { 0xb8b8b8b8, 0xb9b9b9b9, 0xbabababa } },
414 static int got_exception;
416 static void run_exception_test(void *handler, const void* context,
417 const void *code, unsigned int code_size,
418 DWORD access)
420 struct {
421 EXCEPTION_REGISTRATION_RECORD frame;
422 const void *context;
423 } exc_frame;
424 void (*func)(void) = code_mem;
425 DWORD oldaccess, oldaccess2;
427 exc_frame.frame.Handler = handler;
428 exc_frame.frame.Prev = NtCurrentTeb()->Tib.ExceptionList;
429 exc_frame.context = context;
431 memcpy(code_mem, code, code_size);
432 if(access)
433 VirtualProtect(code_mem, code_size, access, &oldaccess);
435 NtCurrentTeb()->Tib.ExceptionList = &exc_frame.frame;
436 func();
437 NtCurrentTeb()->Tib.ExceptionList = exc_frame.frame.Prev;
439 if(access)
440 VirtualProtect(code_mem, code_size, oldaccess, &oldaccess2);
443 static LONG CALLBACK rtlraiseexception_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
445 PCONTEXT context = ExceptionInfo->ContextRecord;
446 PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
447 trace("vect. handler %08x addr:%p context.Eip:%x\n", rec->ExceptionCode,
448 rec->ExceptionAddress, context->Eip);
450 ok(rec->ExceptionAddress == (char *)code_mem + 0xb
451 || broken(rec->ExceptionAddress == code_mem || !rec->ExceptionAddress) /* 2008 */,
452 "ExceptionAddress at %p instead of %p\n", rec->ExceptionAddress, (char *)code_mem + 0xb);
454 if (NtCurrentTeb()->Peb->BeingDebugged)
455 ok((void *)context->Eax == pRtlRaiseException ||
456 broken( is_wow64 && context->Eax == 0xf00f00f1 ), /* broken on vista */
457 "debugger managed to modify Eax to %x should be %p\n",
458 context->Eax, pRtlRaiseException);
460 /* check that context.Eip is fixed up only for EXCEPTION_BREAKPOINT
461 * even if raised by RtlRaiseException
463 if(rec->ExceptionCode == EXCEPTION_BREAKPOINT)
465 ok(context->Eip == (DWORD)code_mem + 0xa ||
466 broken(context->Eip == (DWORD)code_mem + 0xb) /* win2k3 */ ||
467 broken(context->Eip == (DWORD)code_mem + 0xd) /* w2008 */,
468 "Eip at %x instead of %x or %x\n", context->Eip,
469 (DWORD)code_mem + 0xa, (DWORD)code_mem + 0xb);
471 else
473 ok(context->Eip == (DWORD)code_mem + 0xb ||
474 broken(context->Eip == (DWORD)code_mem + 0xd) /* w2008 */,
475 "Eip at %x instead of %x\n", context->Eip, (DWORD)code_mem + 0xb);
478 /* test if context change is preserved from vectored handler to stack handlers */
479 context->Eax = 0xf00f00f0;
480 return EXCEPTION_CONTINUE_SEARCH;
483 static DWORD rtlraiseexception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
484 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
486 trace( "exception: %08x flags:%x addr:%p context: Eip:%x\n",
487 rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, context->Eip );
489 ok(rec->ExceptionAddress == (char *)code_mem + 0xb
490 || broken(rec->ExceptionAddress == code_mem || !rec->ExceptionAddress) /* 2008 */,
491 "ExceptionAddress at %p instead of %p\n", rec->ExceptionAddress, (char *)code_mem + 0xb);
493 ok( context->ContextFlags == CONTEXT_ALL || context->ContextFlags == (CONTEXT_ALL | CONTEXT_XSTATE) ||
494 broken(context->ContextFlags == CONTEXT_FULL), /* win2003 */
495 "wrong context flags %x\n", context->ContextFlags );
497 /* check that context.Eip is fixed up only for EXCEPTION_BREAKPOINT
498 * even if raised by RtlRaiseException
500 if(rec->ExceptionCode == EXCEPTION_BREAKPOINT)
502 ok(context->Eip == (DWORD)code_mem + 0xa ||
503 broken(context->Eip == (DWORD)code_mem + 0xb) /* win2k3 */ ||
504 broken(context->Eip == (DWORD)code_mem + 0xd) /* w2008 */,
505 "Eip at %x instead of %x or %x\n", context->Eip,
506 (DWORD)code_mem + 0xa, (DWORD)code_mem + 0xb);
508 else
510 ok(context->Eip == (DWORD)code_mem + 0xb ||
511 broken(context->Eip == (DWORD)code_mem + 0xd) /* w2008 */,
512 "Eip at %x instead of %x\n", context->Eip, (DWORD)code_mem + 0xb);
515 if(have_vectored_api)
516 ok(context->Eax == 0xf00f00f0, "Eax is %x, should have been set to 0xf00f00f0 in vectored handler\n",
517 context->Eax);
519 /* give the debugger a chance to examine the state a second time */
520 /* without the exception handler changing Eip */
521 if (test_stage == 2)
522 return ExceptionContinueSearch;
524 /* Eip in context is decreased by 1
525 * Increase it again, else execution will continue in the middle of an instruction */
526 if(rec->ExceptionCode == EXCEPTION_BREAKPOINT && (context->Eip == (DWORD)code_mem + 0xa))
527 context->Eip += 1;
528 return ExceptionContinueExecution;
532 static const BYTE call_one_arg_code[] = {
533 0x8b, 0x44, 0x24, 0x08, /* mov 0x8(%esp),%eax */
534 0x50, /* push %eax */
535 0x8b, 0x44, 0x24, 0x08, /* mov 0x8(%esp),%eax */
536 0xff, 0xd0, /* call *%eax */
537 0x90, /* nop */
538 0x90, /* nop */
539 0x90, /* nop */
540 0x90, /* nop */
541 0xc3, /* ret */
545 static void run_rtlraiseexception_test(DWORD exceptioncode)
547 EXCEPTION_REGISTRATION_RECORD frame;
548 EXCEPTION_RECORD record;
549 PVOID vectored_handler = NULL;
551 void (*func)(void* function, EXCEPTION_RECORD* record) = code_mem;
553 record.ExceptionCode = exceptioncode;
554 record.ExceptionFlags = 0;
555 record.ExceptionRecord = NULL;
556 record.ExceptionAddress = NULL; /* does not matter, copied return address */
557 record.NumberParameters = 0;
559 frame.Handler = rtlraiseexception_handler;
560 frame.Prev = NtCurrentTeb()->Tib.ExceptionList;
562 memcpy(code_mem, call_one_arg_code, sizeof(call_one_arg_code));
564 NtCurrentTeb()->Tib.ExceptionList = &frame;
565 if (have_vectored_api)
567 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &rtlraiseexception_vectored_handler);
568 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
571 func(pRtlRaiseException, &record);
572 ok( record.ExceptionAddress == (char *)code_mem + 0x0b,
573 "address set to %p instead of %p\n", record.ExceptionAddress, (char *)code_mem + 0x0b );
575 if (have_vectored_api)
576 pRtlRemoveVectoredExceptionHandler(vectored_handler);
577 NtCurrentTeb()->Tib.ExceptionList = frame.Prev;
580 static void test_rtlraiseexception(void)
582 if (!pRtlRaiseException)
584 skip("RtlRaiseException not found\n");
585 return;
588 /* test without debugger */
589 run_rtlraiseexception_test(0x12345);
590 run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
591 run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
594 static DWORD unwind_expected_eax;
596 static DWORD unwind_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
597 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
599 trace("exception: %08x flags:%x addr:%p context: Eip:%x\n",
600 rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, context->Eip);
602 ok(rec->ExceptionCode == STATUS_UNWIND, "ExceptionCode is %08x instead of %08x\n",
603 rec->ExceptionCode, STATUS_UNWIND);
604 ok(rec->ExceptionAddress == (char *)code_mem + 0x22 || broken(TRUE) /* Win10 1709 */,
605 "ExceptionAddress at %p instead of %p\n", rec->ExceptionAddress, (char *)code_mem + 0x22);
606 ok(context->Eip == (DWORD)code_mem + 0x22, "context->Eip is %08x instead of %08x\n",
607 context->Eip, (DWORD)code_mem + 0x22);
608 ok(context->Eax == unwind_expected_eax, "context->Eax is %08x instead of %08x\n",
609 context->Eax, unwind_expected_eax);
611 context->Eax += 1;
612 return ExceptionContinueSearch;
615 static const BYTE call_unwind_code[] = {
616 0x55, /* push %ebp */
617 0x53, /* push %ebx */
618 0x56, /* push %esi */
619 0x57, /* push %edi */
620 0xe8, 0x00, 0x00, 0x00, 0x00, /* call 0 */
621 0x58, /* 0: pop %eax */
622 0x05, 0x1e, 0x00, 0x00, 0x00, /* add $0x1e,%eax */
623 0xff, 0x74, 0x24, 0x20, /* push 0x20(%esp) */
624 0xff, 0x74, 0x24, 0x20, /* push 0x20(%esp) */
625 0x50, /* push %eax */
626 0xff, 0x74, 0x24, 0x24, /* push 0x24(%esp) */
627 0x8B, 0x44, 0x24, 0x24, /* mov 0x24(%esp),%eax */
628 0xff, 0xd0, /* call *%eax */
629 0x5f, /* pop %edi */
630 0x5e, /* pop %esi */
631 0x5b, /* pop %ebx */
632 0x5d, /* pop %ebp */
633 0xc3, /* ret */
634 0xcc, /* int $3 */
637 static void test_unwind(void)
639 EXCEPTION_REGISTRATION_RECORD frames[2], *frame2 = &frames[0], *frame1 = &frames[1];
640 DWORD (*func)(void* function, EXCEPTION_REGISTRATION_RECORD *pEndFrame, EXCEPTION_RECORD* record, DWORD retval) = code_mem;
641 DWORD retval;
643 memcpy(code_mem, call_unwind_code, sizeof(call_unwind_code));
645 /* add first unwind handler */
646 frame1->Handler = unwind_handler;
647 frame1->Prev = NtCurrentTeb()->Tib.ExceptionList;
648 NtCurrentTeb()->Tib.ExceptionList = frame1;
650 /* add second unwind handler */
651 frame2->Handler = unwind_handler;
652 frame2->Prev = NtCurrentTeb()->Tib.ExceptionList;
653 NtCurrentTeb()->Tib.ExceptionList = frame2;
655 /* test unwind to current frame */
656 unwind_expected_eax = 0xDEAD0000;
657 retval = func(pRtlUnwind, frame2, NULL, 0xDEAD0000);
658 ok(retval == 0xDEAD0000, "RtlUnwind returned eax %08x instead of %08x\n", retval, 0xDEAD0000);
659 ok(NtCurrentTeb()->Tib.ExceptionList == frame2, "Exception record points to %p instead of %p\n",
660 NtCurrentTeb()->Tib.ExceptionList, frame2);
662 /* unwind to frame1 */
663 unwind_expected_eax = 0xDEAD0000;
664 retval = func(pRtlUnwind, frame1, NULL, 0xDEAD0000);
665 ok(retval == 0xDEAD0001, "RtlUnwind returned eax %08x instead of %08x\n", retval, 0xDEAD0001);
666 ok(NtCurrentTeb()->Tib.ExceptionList == frame1, "Exception record points to %p instead of %p\n",
667 NtCurrentTeb()->Tib.ExceptionList, frame1);
669 /* restore original handler */
670 NtCurrentTeb()->Tib.ExceptionList = frame1->Prev;
673 static DWORD handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
674 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
676 const struct exception *except = *(const struct exception **)(frame + 1);
677 unsigned int i, parameter_count, entry = except - exceptions;
679 got_exception++;
680 trace( "exception %u: %x flags:%x addr:%p\n",
681 entry, rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
683 ok( rec->ExceptionCode == except->status ||
684 (except->alt_status != 0 && rec->ExceptionCode == except->alt_status),
685 "%u: Wrong exception code %x/%x\n", entry, rec->ExceptionCode, except->status );
686 ok( context->Eip == (DWORD_PTR)code_mem + except->offset,
687 "%u: Unexpected eip %#x/%#lx\n", entry,
688 context->Eip, (DWORD_PTR)code_mem + except->offset );
689 ok( rec->ExceptionAddress == (char*)context->Eip ||
690 (rec->ExceptionCode == STATUS_BREAKPOINT && rec->ExceptionAddress == (char*)context->Eip + 1),
691 "%u: Unexpected exception address %p/%p\n", entry,
692 rec->ExceptionAddress, (char*)context->Eip );
694 if (except->status == STATUS_BREAKPOINT && is_wow64)
695 parameter_count = 1;
696 else if (except->alt_status == 0 || rec->ExceptionCode != except->alt_status)
697 parameter_count = except->nb_params;
698 else
699 parameter_count = except->alt_nb_params;
701 ok( rec->NumberParameters == parameter_count,
702 "%u: Unexpected parameter count %u/%u\n", entry, rec->NumberParameters, parameter_count );
704 /* Most CPUs (except Intel Core apparently) report a segment limit violation */
705 /* instead of page faults for accesses beyond 0xffffffff */
706 if (except->nb_params == 2 && except->params[1] >= 0xfffffffd)
708 if (rec->ExceptionInformation[0] == 0 && rec->ExceptionInformation[1] == 0xffffffff)
709 goto skip_params;
712 /* Seems that both 0xbee8 and 0xfffffffff can be returned in windows */
713 if (except->nb_params == 2 && rec->NumberParameters == 2
714 && except->params[1] == 0xbee8 && rec->ExceptionInformation[1] == 0xffffffff
715 && except->params[0] == rec->ExceptionInformation[0])
717 goto skip_params;
720 if (except->alt_status == 0 || rec->ExceptionCode != except->alt_status)
722 for (i = 0; i < rec->NumberParameters; i++)
723 ok( rec->ExceptionInformation[i] == except->params[i],
724 "%u: Wrong parameter %d: %lx/%x\n",
725 entry, i, rec->ExceptionInformation[i], except->params[i] );
727 else
729 for (i = 0; i < rec->NumberParameters; i++)
730 ok( rec->ExceptionInformation[i] == except->alt_params[i],
731 "%u: Wrong parameter %d: %lx/%x\n",
732 entry, i, rec->ExceptionInformation[i], except->alt_params[i] );
735 skip_params:
736 /* don't handle exception if it's not the address we expected */
737 if (context->Eip != (DWORD_PTR)code_mem + except->offset) return ExceptionContinueSearch;
739 context->Eip += except->length;
740 return ExceptionContinueExecution;
743 static void test_prot_fault(void)
745 unsigned int i;
747 for (i = 0; i < ARRAY_SIZE(exceptions); i++)
749 if (is_wow64 && exceptions[i].wow64_broken && !strcmp( winetest_platform, "windows" ))
751 skip( "Exception %u broken on Wow64\n", i );
752 continue;
754 got_exception = 0;
755 run_exception_test(handler, &exceptions[i], &exceptions[i].code,
756 sizeof(exceptions[i].code), 0);
757 if (!i && !got_exception)
759 trace( "No exception, assuming win9x, no point in testing further\n" );
760 break;
762 ok( got_exception == (exceptions[i].status != 0),
763 "%u: bad exception count %d\n", i, got_exception );
767 struct dbgreg_test {
768 DWORD dr0, dr1, dr2, dr3, dr6, dr7;
771 /* test handling of debug registers */
772 static DWORD dreg_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
773 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
775 const struct dbgreg_test *test = *(const struct dbgreg_test **)(frame + 1);
777 context->Eip += 2; /* Skips the popl (%eax) */
778 context->Dr0 = test->dr0;
779 context->Dr1 = test->dr1;
780 context->Dr2 = test->dr2;
781 context->Dr3 = test->dr3;
782 context->Dr6 = test->dr6;
783 context->Dr7 = test->dr7;
784 return ExceptionContinueExecution;
787 #define CHECK_DEBUG_REG(n, m) \
788 ok((ctx.Dr##n & m) == test->dr##n, "(%d) failed to set debug register " #n " to %x, got %x\n", \
789 test_num, test->dr##n, ctx.Dr##n)
791 static void check_debug_registers(int test_num, const struct dbgreg_test *test)
793 CONTEXT ctx;
794 NTSTATUS status;
796 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
797 status = pNtGetContextThread(GetCurrentThread(), &ctx);
798 ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %x\n", status);
800 CHECK_DEBUG_REG(0, ~0);
801 CHECK_DEBUG_REG(1, ~0);
802 CHECK_DEBUG_REG(2, ~0);
803 CHECK_DEBUG_REG(3, ~0);
804 CHECK_DEBUG_REG(6, 0x0f);
805 CHECK_DEBUG_REG(7, ~0xdc00);
808 static const BYTE segfault_code[5] = {
809 0x31, 0xc0, /* xor %eax,%eax */
810 0x8f, 0x00, /* popl (%eax) - cause exception */
811 0xc3 /* ret */
814 /* test the single step exception behaviour */
815 static DWORD single_step_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
816 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
818 got_exception++;
819 ok (!(context->EFlags & 0x100), "eflags has single stepping bit set\n");
821 if( got_exception < 3)
822 context->EFlags |= 0x100; /* single step until popf instruction */
823 else {
824 /* show that the last single step exception on the popf instruction
825 * (which removed the TF bit), still is a EXCEPTION_SINGLE_STEP exception */
826 ok( rec->ExceptionCode == EXCEPTION_SINGLE_STEP,
827 "exception is not EXCEPTION_SINGLE_STEP: %x\n", rec->ExceptionCode);
830 return ExceptionContinueExecution;
833 static const BYTE single_stepcode[] = {
834 0x9c, /* pushf */
835 0x58, /* pop %eax */
836 0x0d,0,1,0,0, /* or $0x100,%eax */
837 0x50, /* push %eax */
838 0x9d, /* popf */
839 0x35,0,1,0,0, /* xor $0x100,%eax */
840 0x50, /* push %eax */
841 0x9d, /* popf */
842 0xc3
845 /* Test the alignment check (AC) flag handling. */
846 static const BYTE align_check_code[] = {
847 0x55, /* push %ebp */
848 0x89,0xe5, /* mov %esp,%ebp */
849 0x9c, /* pushf */
850 0x58, /* pop %eax */
851 0x0d,0,0,4,0, /* or $0x40000,%eax */
852 0x50, /* push %eax */
853 0x9d, /* popf */
854 0x89,0xe0, /* mov %esp, %eax */
855 0x8b,0x40,0x1, /* mov 0x1(%eax), %eax - cause exception */
856 0x9c, /* pushf */
857 0x58, /* pop %eax */
858 0x35,0,0,4,0, /* xor $0x40000,%eax */
859 0x50, /* push %eax */
860 0x9d, /* popf */
861 0x5d, /* pop %ebp */
862 0xc3, /* ret */
865 static DWORD align_check_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
866 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
868 ok (!(context->EFlags & 0x40000), "eflags has AC bit set\n");
869 got_exception++;
870 return ExceptionContinueExecution;
873 /* Test the direction flag handling. */
874 static const BYTE direction_flag_code[] = {
875 0x55, /* push %ebp */
876 0x89,0xe5, /* mov %esp,%ebp */
877 0xfd, /* std */
878 0xfa, /* cli - cause exception */
879 0x5d, /* pop %ebp */
880 0xc3, /* ret */
883 static DWORD direction_flag_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
884 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
886 #ifdef __GNUC__
887 unsigned int flags;
888 __asm__("pushfl; popl %0; cld" : "=r" (flags) );
889 /* older windows versions don't clear DF properly so don't test */
890 if (flags & 0x400) trace( "eflags has DF bit set\n" );
891 #endif
892 ok( context->EFlags & 0x400, "context eflags has DF bit cleared\n" );
893 got_exception++;
894 context->Eip++; /* skip cli */
895 context->EFlags &= ~0x400; /* make sure it is cleared on return */
896 return ExceptionContinueExecution;
899 /* test single stepping over hardware breakpoint */
900 static DWORD bpx_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
901 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
903 got_exception++;
904 ok( rec->ExceptionCode == EXCEPTION_SINGLE_STEP,
905 "wrong exception code: %x\n", rec->ExceptionCode);
907 if(got_exception == 1) {
908 /* hw bp exception on first nop */
909 ok( context->Eip == (DWORD)code_mem, "eip is wrong: %x instead of %x\n",
910 context->Eip, (DWORD)code_mem);
911 ok( (context->Dr6 & 0xf) == 1, "B0 flag is not set in Dr6\n");
912 ok( !(context->Dr6 & 0x4000), "BS flag is set in Dr6\n");
913 context->Dr0 = context->Dr0 + 1; /* set hw bp again on next instruction */
914 context->EFlags |= 0x100; /* enable single stepping */
915 } else if( got_exception == 2) {
916 /* single step exception on second nop */
917 ok( context->Eip == (DWORD)code_mem + 1, "eip is wrong: %x instead of %x\n",
918 context->Eip, (DWORD)code_mem + 1);
919 ok( (context->Dr6 & 0x4000), "BS flag is not set in Dr6\n");
920 /* depending on the win version the B0 bit is already set here as well
921 ok( (context->Dr6 & 0xf) == 0, "B0...3 flags in Dr6 shouldn't be set\n"); */
922 context->EFlags |= 0x100;
923 } else if( got_exception == 3) {
924 /* hw bp exception on second nop */
925 ok( context->Eip == (DWORD)code_mem + 1, "eip is wrong: %x instead of %x\n",
926 context->Eip, (DWORD)code_mem + 1);
927 ok( (context->Dr6 & 0xf) == 1, "B0 flag is not set in Dr6\n");
928 ok( !(context->Dr6 & 0x4000), "BS flag is set in Dr6\n");
929 context->Dr0 = 0; /* clear breakpoint */
930 context->EFlags |= 0x100;
931 } else {
932 /* single step exception on ret */
933 ok( context->Eip == (DWORD)code_mem + 2, "eip is wrong: %x instead of %x\n",
934 context->Eip, (DWORD)code_mem + 2);
935 ok( (context->Dr6 & 0xf) == 0, "B0...3 flags in Dr6 shouldn't be set\n");
936 ok( (context->Dr6 & 0x4000), "BS flag is not set in Dr6\n");
939 context->Dr6 = 0; /* clear status register */
940 return ExceptionContinueExecution;
943 static const BYTE dummy_code[] = { 0x90, 0x90, 0xc3 }; /* nop, nop, ret */
945 /* test int3 handling */
946 static DWORD int3_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
947 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
949 ok( rec->ExceptionAddress == code_mem, "exception address not at: %p, but at %p\n",
950 code_mem, rec->ExceptionAddress);
951 ok( context->Eip == (DWORD)code_mem, "eip not at: %p, but at %#x\n", code_mem, context->Eip);
952 if(context->Eip == (DWORD)code_mem) context->Eip++; /* skip breakpoint */
954 return ExceptionContinueExecution;
957 static const BYTE int3_code[] = { 0xCC, 0xc3 }; /* int 3, ret */
959 static DWORD WINAPI hw_reg_exception_thread( void *arg )
961 int expect = (ULONG_PTR)arg;
962 got_exception = 0;
963 run_exception_test( bpx_handler, NULL, dummy_code, sizeof(dummy_code), 0 );
964 ok( got_exception == expect, "expected %u exceptions, got %d\n", expect, got_exception );
965 return 0;
968 static void test_exceptions(void)
970 CONTEXT ctx;
971 NTSTATUS res;
972 struct dbgreg_test dreg_test;
973 HANDLE h;
975 if (!pNtGetContextThread || !pNtSetContextThread)
977 skip( "NtGetContextThread/NtSetContextThread not found\n" );
978 return;
981 /* test handling of debug registers */
982 memset(&dreg_test, 0, sizeof(dreg_test));
984 dreg_test.dr0 = 0x42424240;
985 dreg_test.dr2 = 0x126bb070;
986 dreg_test.dr3 = 0x0badbad0;
987 dreg_test.dr7 = 0xffff0115;
988 run_exception_test(dreg_handler, &dreg_test, &segfault_code, sizeof(segfault_code), 0);
989 check_debug_registers(1, &dreg_test);
991 dreg_test.dr0 = 0x42424242;
992 dreg_test.dr2 = 0x100f0fe7;
993 dreg_test.dr3 = 0x0abebabe;
994 dreg_test.dr7 = 0x115;
995 run_exception_test(dreg_handler, &dreg_test, &segfault_code, sizeof(segfault_code), 0);
996 check_debug_registers(2, &dreg_test);
998 /* test single stepping behavior */
999 got_exception = 0;
1000 run_exception_test(single_step_handler, NULL, &single_stepcode, sizeof(single_stepcode), 0);
1001 ok(got_exception == 3, "expected 3 single step exceptions, got %d\n", got_exception);
1003 /* test alignment exceptions */
1004 got_exception = 0;
1005 run_exception_test(align_check_handler, NULL, align_check_code, sizeof(align_check_code), 0);
1006 ok(got_exception == 0, "got %d alignment faults, expected 0\n", got_exception);
1008 /* test direction flag */
1009 got_exception = 0;
1010 run_exception_test(direction_flag_handler, NULL, direction_flag_code, sizeof(direction_flag_code), 0);
1011 ok(got_exception == 1, "got %d exceptions, expected 1\n", got_exception);
1013 /* test single stepping over hardware breakpoint */
1014 memset(&ctx, 0, sizeof(ctx));
1015 ctx.Dr0 = (DWORD) code_mem; /* set hw bp on first nop */
1016 ctx.Dr7 = 3;
1017 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
1018 res = pNtSetContextThread( GetCurrentThread(), &ctx);
1019 ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", res);
1021 got_exception = 0;
1022 run_exception_test(bpx_handler, NULL, dummy_code, sizeof(dummy_code), 0);
1023 ok( got_exception == 4,"expected 4 exceptions, got %d\n", got_exception);
1025 /* test int3 handling */
1026 run_exception_test(int3_handler, NULL, int3_code, sizeof(int3_code), 0);
1028 /* test that hardware breakpoints are not inherited by created threads */
1029 res = pNtSetContextThread( GetCurrentThread(), &ctx );
1030 ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", res );
1032 h = CreateThread( NULL, 0, hw_reg_exception_thread, 0, 0, NULL );
1033 WaitForSingleObject( h, 10000 );
1034 CloseHandle( h );
1036 h = CreateThread( NULL, 0, hw_reg_exception_thread, (void *)4, CREATE_SUSPENDED, NULL );
1037 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
1038 res = pNtGetContextThread( h, &ctx );
1039 ok( res == STATUS_SUCCESS, "NtGetContextThread failed with %x\n", res );
1040 ok( ctx.Dr0 == 0, "dr0 %x\n", ctx.Dr0 );
1041 ok( ctx.Dr7 == 0, "dr7 %x\n", ctx.Dr7 );
1042 ctx.Dr0 = (DWORD)code_mem;
1043 ctx.Dr7 = 3;
1044 res = pNtSetContextThread( h, &ctx );
1045 ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", res );
1046 ResumeThread( h );
1047 WaitForSingleObject( h, 10000 );
1048 CloseHandle( h );
1050 ctx.Dr0 = 0;
1051 ctx.Dr7 = 0;
1052 res = pNtSetContextThread( GetCurrentThread(), &ctx );
1053 ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", res );
1056 static void test_debugger(void)
1058 char cmdline[MAX_PATH];
1059 PROCESS_INFORMATION pi;
1060 STARTUPINFOA si = { 0 };
1061 DEBUG_EVENT de;
1062 DWORD continuestatus;
1063 PVOID code_mem_address = NULL;
1064 NTSTATUS status;
1065 SIZE_T size_read;
1066 BOOL ret;
1067 int counter = 0;
1068 si.cb = sizeof(si);
1070 if(!pNtGetContextThread || !pNtSetContextThread || !pNtReadVirtualMemory || !pNtTerminateProcess)
1072 skip("NtGetContextThread, NtSetContextThread, NtReadVirtualMemory or NtTerminateProcess not found\n");
1073 return;
1076 sprintf(cmdline, "%s %s %s %p", my_argv[0], my_argv[1], "debuggee", &test_stage);
1077 ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi);
1078 ok(ret, "could not create child process error: %u\n", GetLastError());
1079 if (!ret)
1080 return;
1084 continuestatus = DBG_CONTINUE;
1085 ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n");
1087 ret = ContinueDebugEvent(de.dwProcessId, de.dwThreadId, 0xdeadbeef);
1088 ok(!ret, "ContinueDebugEvent unexpectedly succeeded\n");
1089 ok(GetLastError() == ERROR_INVALID_PARAMETER, "Unexpected last error: %u\n", GetLastError());
1091 if (de.dwThreadId != pi.dwThreadId)
1093 trace("event %d not coming from main thread, ignoring\n", de.dwDebugEventCode);
1094 ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE);
1095 continue;
1098 if (de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT)
1100 if(de.u.CreateProcessInfo.lpBaseOfImage != NtCurrentTeb()->Peb->ImageBaseAddress)
1102 skip("child process loaded at different address, terminating it\n");
1103 pNtTerminateProcess(pi.hProcess, 0);
1106 else if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
1108 CONTEXT ctx;
1109 int stage;
1111 counter++;
1112 status = pNtReadVirtualMemory(pi.hProcess, &code_mem, &code_mem_address,
1113 sizeof(code_mem_address), &size_read);
1114 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
1115 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
1116 sizeof(stage), &size_read);
1117 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
1119 ctx.ContextFlags = CONTEXT_FULL | CONTEXT_EXTENDED_REGISTERS;
1120 status = pNtGetContextThread(pi.hThread, &ctx);
1121 ok(!status, "NtGetContextThread failed with 0x%x\n", status);
1123 trace("exception 0x%x at %p firstchance=%d Eip=0x%x, Eax=0x%x\n",
1124 de.u.Exception.ExceptionRecord.ExceptionCode,
1125 de.u.Exception.ExceptionRecord.ExceptionAddress, de.u.Exception.dwFirstChance, ctx.Eip, ctx.Eax);
1127 if (counter > 100)
1129 ok(FALSE, "got way too many exceptions, probably caught in an infinite loop, terminating child\n");
1130 pNtTerminateProcess(pi.hProcess, 1);
1132 else if (counter < 2) /* startup breakpoint */
1134 /* breakpoint is inside ntdll */
1135 void *ntdll = GetModuleHandleA( "ntdll.dll" );
1136 IMAGE_NT_HEADERS *nt = RtlImageNtHeader( ntdll );
1138 ok( (char *)ctx.Eip >= (char *)ntdll &&
1139 (char *)ctx.Eip < (char *)ntdll + nt->OptionalHeader.SizeOfImage,
1140 "wrong eip %p ntdll %p-%p\n", (void *)ctx.Eip, ntdll,
1141 (char *)ntdll + nt->OptionalHeader.SizeOfImage );
1143 else
1145 if (stage == 1)
1147 ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at %x instead of %p\n",
1148 ctx.Eip, (char *)code_mem_address + 0xb);
1149 /* setting the context from debugger does not affect the context that the
1150 * exception handler gets, except on w2008 */
1151 ctx.Eip = (UINT_PTR)code_mem_address + 0xd;
1152 ctx.Eax = 0xf00f00f1;
1153 /* let the debuggee handle the exception */
1154 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1156 else if (stage == 2)
1158 if (de.u.Exception.dwFirstChance)
1160 /* debugger gets first chance exception with unmodified ctx.Eip */
1161 ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at 0x%x instead of %p\n",
1162 ctx.Eip, (char *)code_mem_address + 0xb);
1163 ctx.Eip = (UINT_PTR)code_mem_address + 0xd;
1164 ctx.Eax = 0xf00f00f1;
1165 /* pass exception to debuggee
1166 * exception will not be handled and a second chance exception will be raised */
1167 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1169 else
1171 /* debugger gets context after exception handler has played with it */
1172 /* ctx.Eip is the same value the exception handler got */
1173 if (de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT)
1175 ok((char *)ctx.Eip == (char *)code_mem_address + 0xa ||
1176 broken(is_wow64 && (char *)ctx.Eip == (char *)code_mem_address + 0xb) ||
1177 broken((char *)ctx.Eip == (char *)code_mem_address + 0xd) /* w2008 */,
1178 "Eip at 0x%x instead of %p\n",
1179 ctx.Eip, (char *)code_mem_address + 0xa);
1180 /* need to fixup Eip for debuggee */
1181 if ((char *)ctx.Eip == (char *)code_mem_address + 0xa)
1182 ctx.Eip += 1;
1184 else
1185 ok((char *)ctx.Eip == (char *)code_mem_address + 0xb ||
1186 broken((char *)ctx.Eip == (char *)code_mem_address + 0xd) /* w2008 */,
1187 "Eip at 0x%x instead of %p\n",
1188 ctx.Eip, (char *)code_mem_address + 0xb);
1189 /* here we handle exception */
1192 else if (stage == 7 || stage == 8)
1194 ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT,
1195 "expected EXCEPTION_BREAKPOINT, got %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode);
1196 ok((char *)ctx.Eip == (char *)code_mem_address + 0x1d,
1197 "expected Eip = %p, got 0x%x\n", (char *)code_mem_address + 0x1d, ctx.Eip);
1199 if (stage == 8) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1201 else if (stage == 9 || stage == 10)
1203 ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT,
1204 "expected EXCEPTION_BREAKPOINT, got %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode);
1205 ok((char *)ctx.Eip == (char *)code_mem_address + 2,
1206 "expected Eip = %p, got 0x%x\n", (char *)code_mem_address + 2, ctx.Eip);
1208 if (stage == 10) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1210 else if (stage == 11 || stage == 12 || stage == 13)
1212 ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_INVALID_HANDLE,
1213 "unexpected exception code %08x, expected %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode,
1214 EXCEPTION_INVALID_HANDLE);
1215 ok(de.u.Exception.ExceptionRecord.NumberParameters == 0,
1216 "unexpected number of parameters %d, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters);
1218 if (stage == 12|| stage == 13) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1220 else if (stage == 14 || stage == 15)
1222 test_debugger_xstate(pi.hThread, &ctx, stage);
1224 else
1225 ok(FALSE, "unexpected stage %x\n", stage);
1227 status = pNtSetContextThread(pi.hThread, &ctx);
1228 ok(!status, "NtSetContextThread failed with 0x%x\n", status);
1231 else if (de.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT)
1233 int stage;
1234 char buffer[64];
1236 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
1237 sizeof(stage), &size_read);
1238 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
1240 ok(!de.u.DebugString.fUnicode, "unexpected unicode debug string event\n");
1241 ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) - 1, "buffer not large enough to hold %d bytes\n",
1242 de.u.DebugString.nDebugStringLength);
1244 memset(buffer, 0, sizeof(buffer));
1245 status = pNtReadVirtualMemory(pi.hProcess, de.u.DebugString.lpDebugStringData, buffer,
1246 de.u.DebugString.nDebugStringLength, &size_read);
1247 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
1249 if (stage == 3 || stage == 4)
1250 ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer);
1251 else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */
1252 ok(strstr(buffer, "SHIMVIEW") != NULL, "unexpected stage %x, got debug string event '%s'\n", stage, buffer);
1254 if (stage == 4) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1256 else if (de.dwDebugEventCode == RIP_EVENT)
1258 int stage;
1260 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
1261 sizeof(stage), &size_read);
1262 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
1264 if (stage == 5 || stage == 6)
1266 ok(de.u.RipInfo.dwError == 0x11223344, "got unexpected rip error code %08x, expected %08x\n",
1267 de.u.RipInfo.dwError, 0x11223344);
1268 ok(de.u.RipInfo.dwType == 0x55667788, "got unexpected rip type %08x, expected %08x\n",
1269 de.u.RipInfo.dwType, 0x55667788);
1271 else
1272 ok(FALSE, "unexpected stage %x\n", stage);
1274 if (stage == 6) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1277 ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus);
1279 } while (de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT);
1281 wait_child_process( pi.hProcess );
1282 ret = CloseHandle(pi.hThread);
1283 ok(ret, "error %u\n", GetLastError());
1284 ret = CloseHandle(pi.hProcess);
1285 ok(ret, "error %u\n", GetLastError());
1287 return;
1290 static DWORD simd_fault_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
1291 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
1293 int *stage = *(int **)(frame + 1);
1295 got_exception++;
1297 if( *stage == 1) {
1298 /* fault while executing sse instruction */
1299 context->Eip += 3; /* skip addps */
1300 return ExceptionContinueExecution;
1302 else if ( *stage == 2 || *stage == 3 ) {
1303 /* stage 2 - divide by zero fault */
1304 /* stage 3 - invalid operation fault */
1305 if( rec->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION)
1306 skip("system doesn't support SIMD exceptions\n");
1307 else {
1308 ok( rec->ExceptionCode == STATUS_FLOAT_MULTIPLE_TRAPS,
1309 "exception code: %#x, should be %#x\n",
1310 rec->ExceptionCode, STATUS_FLOAT_MULTIPLE_TRAPS);
1311 ok( rec->NumberParameters == 1 || broken(is_wow64 && rec->NumberParameters == 2),
1312 "# of params: %i, should be 1\n",
1313 rec->NumberParameters);
1314 if( rec->NumberParameters == 1 )
1315 ok( rec->ExceptionInformation[0] == 0, "param #1: %lx, should be 0\n", rec->ExceptionInformation[0]);
1317 context->Eip += 3; /* skip divps */
1319 else
1320 ok(FALSE, "unexpected stage %x\n", *stage);
1322 return ExceptionContinueExecution;
1325 static const BYTE simd_exception_test[] = {
1326 0x83, 0xec, 0x4, /* sub $0x4, %esp */
1327 0x0f, 0xae, 0x1c, 0x24, /* stmxcsr (%esp) */
1328 0x8b, 0x04, 0x24, /* mov (%esp),%eax * store mxcsr */
1329 0x66, 0x81, 0x24, 0x24, 0xff, 0xfd, /* andw $0xfdff,(%esp) * enable divide by */
1330 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%esp) * zero exceptions */
1331 0x6a, 0x01, /* push $0x1 */
1332 0x6a, 0x01, /* push $0x1 */
1333 0x6a, 0x01, /* push $0x1 */
1334 0x6a, 0x01, /* push $0x1 */
1335 0x0f, 0x10, 0x0c, 0x24, /* movups (%esp),%xmm1 * fill dividend */
1336 0x0f, 0x57, 0xc0, /* xorps %xmm0,%xmm0 * clear divisor */
1337 0x0f, 0x5e, 0xc8, /* divps %xmm0,%xmm1 * generate fault */
1338 0x83, 0xc4, 0x10, /* add $0x10,%esp */
1339 0x89, 0x04, 0x24, /* mov %eax,(%esp) * restore to old mxcsr */
1340 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%esp) */
1341 0x83, 0xc4, 0x04, /* add $0x4,%esp */
1342 0xc3, /* ret */
1345 static const BYTE simd_exception_test2[] = {
1346 0x83, 0xec, 0x4, /* sub $0x4, %esp */
1347 0x0f, 0xae, 0x1c, 0x24, /* stmxcsr (%esp) */
1348 0x8b, 0x04, 0x24, /* mov (%esp),%eax * store mxcsr */
1349 0x66, 0x81, 0x24, 0x24, 0x7f, 0xff, /* andw $0xff7f,(%esp) * enable invalid */
1350 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%esp) * operation exceptions */
1351 0x0f, 0x57, 0xc9, /* xorps %xmm1,%xmm1 * clear dividend */
1352 0x0f, 0x57, 0xc0, /* xorps %xmm0,%xmm0 * clear divisor */
1353 0x0f, 0x5e, 0xc8, /* divps %xmm0,%xmm1 * generate fault */
1354 0x89, 0x04, 0x24, /* mov %eax,(%esp) * restore to old mxcsr */
1355 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%esp) */
1356 0x83, 0xc4, 0x04, /* add $0x4,%esp */
1357 0xc3, /* ret */
1360 static const BYTE sse_check[] = {
1361 0x0f, 0x58, 0xc8, /* addps %xmm0,%xmm1 */
1362 0xc3, /* ret */
1365 static void test_simd_exceptions(void)
1367 int stage;
1369 /* test if CPU & OS can do sse */
1370 stage = 1;
1371 got_exception = 0;
1372 run_exception_test(simd_fault_handler, &stage, sse_check, sizeof(sse_check), 0);
1373 if(got_exception) {
1374 skip("system doesn't support SSE\n");
1375 return;
1378 /* generate a SIMD exception */
1379 stage = 2;
1380 got_exception = 0;
1381 run_exception_test(simd_fault_handler, &stage, simd_exception_test,
1382 sizeof(simd_exception_test), 0);
1383 ok(got_exception == 1, "got exception: %i, should be 1\n", got_exception);
1385 /* generate a SIMD exception, test FPE_FLTINV */
1386 stage = 3;
1387 got_exception = 0;
1388 run_exception_test(simd_fault_handler, &stage, simd_exception_test2,
1389 sizeof(simd_exception_test2), 0);
1390 ok(got_exception == 1, "got exception: %i, should be 1\n", got_exception);
1393 struct fpu_exception_info
1395 DWORD exception_code;
1396 DWORD exception_offset;
1397 DWORD eip_offset;
1400 static DWORD fpu_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
1401 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher)
1403 struct fpu_exception_info *info = *(struct fpu_exception_info **)(frame + 1);
1405 info->exception_code = rec->ExceptionCode;
1406 info->exception_offset = (BYTE *)rec->ExceptionAddress - (BYTE *)code_mem;
1407 info->eip_offset = context->Eip - (DWORD)code_mem;
1409 ++context->Eip;
1410 return ExceptionContinueExecution;
1413 static void test_fpu_exceptions(void)
1415 static const BYTE fpu_exception_test_ie[] =
1417 0x83, 0xec, 0x04, /* sub $0x4,%esp */
1418 0x66, 0xc7, 0x04, 0x24, 0xfe, 0x03, /* movw $0x3fe,(%esp) */
1419 0x9b, 0xd9, 0x7c, 0x24, 0x02, /* fstcw 0x2(%esp) */
1420 0xd9, 0x2c, 0x24, /* fldcw (%esp) */
1421 0xd9, 0xee, /* fldz */
1422 0xd9, 0xe8, /* fld1 */
1423 0xde, 0xf1, /* fdivp */
1424 0xdd, 0xd8, /* fstp %st(0) */
1425 0xdd, 0xd8, /* fstp %st(0) */
1426 0x9b, /* fwait */
1427 0xdb, 0xe2, /* fnclex */
1428 0xd9, 0x6c, 0x24, 0x02, /* fldcw 0x2(%esp) */
1429 0x83, 0xc4, 0x04, /* add $0x4,%esp */
1430 0xc3, /* ret */
1433 static const BYTE fpu_exception_test_de[] =
1435 0x83, 0xec, 0x04, /* sub $0x4,%esp */
1436 0x66, 0xc7, 0x04, 0x24, 0xfb, 0x03, /* movw $0x3fb,(%esp) */
1437 0x9b, 0xd9, 0x7c, 0x24, 0x02, /* fstcw 0x2(%esp) */
1438 0xd9, 0x2c, 0x24, /* fldcw (%esp) */
1439 0xdd, 0xd8, /* fstp %st(0) */
1440 0xd9, 0xee, /* fldz */
1441 0xd9, 0xe8, /* fld1 */
1442 0xde, 0xf1, /* fdivp */
1443 0x9b, /* fwait */
1444 0xdb, 0xe2, /* fnclex */
1445 0xdd, 0xd8, /* fstp %st(0) */
1446 0xdd, 0xd8, /* fstp %st(0) */
1447 0xd9, 0x6c, 0x24, 0x02, /* fldcw 0x2(%esp) */
1448 0x83, 0xc4, 0x04, /* add $0x4,%esp */
1449 0xc3, /* ret */
1452 struct fpu_exception_info info;
1454 memset(&info, 0, sizeof(info));
1455 run_exception_test(fpu_exception_handler, &info, fpu_exception_test_ie, sizeof(fpu_exception_test_ie), 0);
1456 ok(info.exception_code == EXCEPTION_FLT_STACK_CHECK,
1457 "Got exception code %#x, expected EXCEPTION_FLT_STACK_CHECK\n", info.exception_code);
1458 ok(info.exception_offset == 0x19 ||
1459 broken( info.exception_offset == info.eip_offset ),
1460 "Got exception offset %#x, expected 0x19\n", info.exception_offset);
1461 ok(info.eip_offset == 0x1b, "Got EIP offset %#x, expected 0x1b\n", info.eip_offset);
1463 memset(&info, 0, sizeof(info));
1464 run_exception_test(fpu_exception_handler, &info, fpu_exception_test_de, sizeof(fpu_exception_test_de), 0);
1465 ok(info.exception_code == EXCEPTION_FLT_DIVIDE_BY_ZERO,
1466 "Got exception code %#x, expected EXCEPTION_FLT_DIVIDE_BY_ZERO\n", info.exception_code);
1467 ok(info.exception_offset == 0x17 ||
1468 broken( info.exception_offset == info.eip_offset ),
1469 "Got exception offset %#x, expected 0x17\n", info.exception_offset);
1470 ok(info.eip_offset == 0x19, "Got EIP offset %#x, expected 0x19\n", info.eip_offset);
1473 struct dpe_exception_info {
1474 BOOL exception_caught;
1475 DWORD exception_info;
1478 static DWORD dpe_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
1479 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher)
1481 DWORD old_prot;
1482 struct dpe_exception_info *info = *(struct dpe_exception_info **)(frame + 1);
1484 ok(rec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION,
1485 "Exception code %08x\n", rec->ExceptionCode);
1486 ok(rec->NumberParameters == 2,
1487 "Parameter count: %d\n", rec->NumberParameters);
1488 ok((LPVOID)rec->ExceptionInformation[1] == code_mem,
1489 "Exception address: %p, expected %p\n",
1490 (LPVOID)rec->ExceptionInformation[1], code_mem);
1492 info->exception_info = rec->ExceptionInformation[0];
1493 info->exception_caught = TRUE;
1495 VirtualProtect(code_mem, 1, PAGE_EXECUTE_READWRITE, &old_prot);
1496 return ExceptionContinueExecution;
1499 static void test_dpe_exceptions(void)
1501 static const BYTE single_ret[] = {0xC3};
1502 struct dpe_exception_info info;
1503 NTSTATUS stat;
1504 BOOL has_hw_support;
1505 BOOL is_permanent = FALSE, can_test_without = TRUE, can_test_with = TRUE;
1506 DWORD val;
1507 ULONG len;
1509 /* Query DEP with len too small */
1510 stat = pNtQueryInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val - 1, &len);
1511 if(stat == STATUS_INVALID_INFO_CLASS)
1513 skip("This software platform does not support DEP\n");
1514 return;
1516 ok(stat == STATUS_INFO_LENGTH_MISMATCH, "buffer too small: %08x\n", stat);
1518 /* Query DEP */
1519 stat = pNtQueryInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val, &len);
1520 ok(stat == STATUS_SUCCESS, "querying DEP: status %08x\n", stat);
1521 if(stat == STATUS_SUCCESS)
1523 ok(len == sizeof val, "returned length: %d\n", len);
1524 if(val & MEM_EXECUTE_OPTION_PERMANENT)
1526 skip("toggling DEP impossible - status locked\n");
1527 is_permanent = TRUE;
1528 if(val & MEM_EXECUTE_OPTION_DISABLE)
1529 can_test_without = FALSE;
1530 else
1531 can_test_with = FALSE;
1535 if(!is_permanent)
1537 /* Enable DEP */
1538 val = MEM_EXECUTE_OPTION_DISABLE;
1539 stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
1540 ok(stat == STATUS_SUCCESS, "enabling DEP: status %08x\n", stat);
1543 if(can_test_with)
1545 /* Try access to locked page with DEP on*/
1546 info.exception_caught = FALSE;
1547 run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_NOACCESS);
1548 ok(info.exception_caught == TRUE, "Execution of disabled memory succeeded\n");
1549 ok(info.exception_info == EXCEPTION_READ_FAULT ||
1550 info.exception_info == EXCEPTION_EXECUTE_FAULT,
1551 "Access violation type: %08x\n", (unsigned)info.exception_info);
1552 has_hw_support = info.exception_info == EXCEPTION_EXECUTE_FAULT;
1553 trace("DEP hardware support: %s\n", has_hw_support?"Yes":"No");
1555 /* Try execution of data with DEP on*/
1556 info.exception_caught = FALSE;
1557 run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_READWRITE);
1558 if(has_hw_support)
1560 ok(info.exception_caught == TRUE, "Execution of data memory succeeded\n");
1561 ok(info.exception_info == EXCEPTION_EXECUTE_FAULT,
1562 "Access violation type: %08x\n", (unsigned)info.exception_info);
1564 else
1565 ok(info.exception_caught == FALSE, "Execution trapped without hardware support\n");
1567 else
1568 skip("DEP is in AlwaysOff state\n");
1570 if(!is_permanent)
1572 /* Disable DEP */
1573 val = MEM_EXECUTE_OPTION_ENABLE;
1574 stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
1575 ok(stat == STATUS_SUCCESS, "disabling DEP: status %08x\n", stat);
1578 /* page is read without exec here */
1579 if(can_test_without)
1581 /* Try execution of data with DEP off */
1582 info.exception_caught = FALSE;
1583 run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_READWRITE);
1584 ok(info.exception_caught == FALSE, "Execution trapped with DEP turned off\n");
1586 /* Try access to locked page with DEP off - error code is different than
1587 with hardware DEP on */
1588 info.exception_caught = FALSE;
1589 run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_NOACCESS);
1590 ok(info.exception_caught == TRUE, "Execution of disabled memory succeeded\n");
1591 ok(info.exception_info == EXCEPTION_READ_FAULT,
1592 "Access violation type: %08x\n", (unsigned)info.exception_info);
1594 else
1595 skip("DEP is in AlwaysOn state\n");
1597 if(!is_permanent)
1599 /* Turn off DEP permanently */
1600 val = MEM_EXECUTE_OPTION_ENABLE | MEM_EXECUTE_OPTION_PERMANENT;
1601 stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
1602 ok(stat == STATUS_SUCCESS, "disabling DEP permanently: status %08x\n", stat);
1605 /* Try to turn off DEP */
1606 val = MEM_EXECUTE_OPTION_ENABLE;
1607 stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
1608 ok(stat == STATUS_ACCESS_DENIED, "disabling DEP while permanent: status %08x\n", stat);
1610 /* Try to turn on DEP */
1611 val = MEM_EXECUTE_OPTION_DISABLE;
1612 stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
1613 ok(stat == STATUS_ACCESS_DENIED, "enabling DEP while permanent: status %08x\n", stat);
1616 static void test_thread_context(void)
1618 CONTEXT context;
1619 NTSTATUS status;
1620 struct expected
1622 DWORD Eax, Ebx, Ecx, Edx, Esi, Edi, Ebp, Esp, Eip,
1623 SegCs, SegDs, SegEs, SegFs, SegGs, SegSs, EFlags, prev_frame,
1624 x87_control;
1625 } expect;
1626 NTSTATUS (*func_ptr)( struct expected *res, void *func, void *arg1, void *arg2,
1627 DWORD *new_x87_control ) = (void *)code_mem;
1628 DWORD new_x87_control;
1630 static const BYTE call_func[] =
1632 0x55, /* pushl %ebp */
1633 0x89, 0xe5, /* mov %esp,%ebp */
1634 0x50, /* pushl %eax ; add a bit of offset to the stack */
1635 0x50, /* pushl %eax */
1636 0x50, /* pushl %eax */
1637 0x50, /* pushl %eax */
1638 0x8b, 0x45, 0x08, /* mov 0x8(%ebp),%eax */
1639 0x8f, 0x00, /* popl (%eax) */
1640 0x89, 0x58, 0x04, /* mov %ebx,0x4(%eax) */
1641 0x89, 0x48, 0x08, /* mov %ecx,0x8(%eax) */
1642 0x89, 0x50, 0x0c, /* mov %edx,0xc(%eax) */
1643 0x89, 0x70, 0x10, /* mov %esi,0x10(%eax) */
1644 0x89, 0x78, 0x14, /* mov %edi,0x14(%eax) */
1645 0x89, 0x68, 0x18, /* mov %ebp,0x18(%eax) */
1646 0x89, 0x60, 0x1c, /* mov %esp,0x1c(%eax) */
1647 0xff, 0x75, 0x04, /* pushl 0x4(%ebp) */
1648 0x8f, 0x40, 0x20, /* popl 0x20(%eax) */
1649 0x8c, 0x48, 0x24, /* mov %cs,0x24(%eax) */
1650 0x8c, 0x58, 0x28, /* mov %ds,0x28(%eax) */
1651 0x8c, 0x40, 0x2c, /* mov %es,0x2c(%eax) */
1652 0x8c, 0x60, 0x30, /* mov %fs,0x30(%eax) */
1653 0x8c, 0x68, 0x34, /* mov %gs,0x34(%eax) */
1654 0x8c, 0x50, 0x38, /* mov %ss,0x38(%eax) */
1655 0x9c, /* pushf */
1656 0x8f, 0x40, 0x3c, /* popl 0x3c(%eax) */
1657 0xff, 0x75, 0x00, /* pushl 0x0(%ebp) ; previous stack frame */
1658 0x8f, 0x40, 0x40, /* popl 0x40(%eax) */
1659 /* pushl $0x47f */
1660 0x68, 0x7f, 0x04, 0x00, 0x00,
1661 0x8f, 0x40, 0x44, /* popl 0x44(%eax) */
1662 0xd9, 0x68, 0x44, /* fldcw 0x44(%eax) */
1664 0x8b, 0x00, /* mov (%eax),%eax */
1665 0xff, 0x75, 0x14, /* pushl 0x14(%ebp) */
1666 0xff, 0x75, 0x10, /* pushl 0x10(%ebp) */
1667 0xff, 0x55, 0x0c, /* call *0xc(%ebp) */
1669 0x8b, 0x55, 0x18, /* mov 0x18(%ebp),%edx */
1670 0x9b, 0xd9, 0x3a, /* fstcw (%edx) */
1671 0xdb, 0xe3, /* fninit */
1673 0xc9, /* leave */
1674 0xc3, /* ret */
1677 memcpy( func_ptr, call_func, sizeof(call_func) );
1679 #define COMPARE(reg) \
1680 ok( context.reg == expect.reg, "wrong " #reg " %08x/%08x\n", context.reg, expect.reg )
1682 memset( &context, 0xcc, sizeof(context) );
1683 memset( &expect, 0xcc, sizeof(expect) );
1684 func_ptr( &expect, pRtlCaptureContext, &context, 0, &new_x87_control );
1685 trace( "expect: eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x ebp=%08x esp=%08x "
1686 "eip=%08x cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x prev=%08x\n",
1687 expect.Eax, expect.Ebx, expect.Ecx, expect.Edx, expect.Esi, expect.Edi,
1688 expect.Ebp, expect.Esp, expect.Eip, expect.SegCs, expect.SegDs, expect.SegEs,
1689 expect.SegFs, expect.SegGs, expect.SegSs, expect.EFlags, expect.prev_frame );
1690 trace( "actual: eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x ebp=%08x esp=%08x "
1691 "eip=%08x cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x\n",
1692 context.Eax, context.Ebx, context.Ecx, context.Edx, context.Esi, context.Edi,
1693 context.Ebp, context.Esp, context.Eip, context.SegCs, context.SegDs, context.SegEs,
1694 context.SegFs, context.SegGs, context.SegSs, context.EFlags );
1696 ok( context.ContextFlags == (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS) ||
1697 broken( context.ContextFlags == 0xcccccccc ), /* <= vista */
1698 "wrong flags %08x\n", context.ContextFlags );
1699 COMPARE( Eax );
1700 COMPARE( Ebx );
1701 COMPARE( Ecx );
1702 COMPARE( Edx );
1703 COMPARE( Esi );
1704 COMPARE( Edi );
1705 COMPARE( Eip );
1706 COMPARE( SegCs );
1707 COMPARE( SegDs );
1708 COMPARE( SegEs );
1709 COMPARE( SegFs );
1710 COMPARE( SegGs );
1711 COMPARE( SegSs );
1712 COMPARE( EFlags );
1713 /* Ebp is from the previous stackframe */
1714 ok( context.Ebp == expect.prev_frame, "wrong Ebp %08x/%08x\n", context.Ebp, expect.prev_frame );
1715 /* Esp is the value on entry to the previous stackframe */
1716 ok( context.Esp == expect.Ebp + 8, "wrong Esp %08x/%08x\n", context.Esp, expect.Ebp + 8 );
1718 memset( &context, 0xcc, sizeof(context) );
1719 memset( &expect, 0xcc, sizeof(expect) );
1720 context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT;
1722 status = func_ptr( &expect, pNtGetContextThread, (void *)GetCurrentThread(), &context, &new_x87_control );
1723 ok( status == STATUS_SUCCESS, "NtGetContextThread failed %08x\n", status );
1724 trace( "expect: eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x ebp=%08x esp=%08x "
1725 "eip=%08x cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x prev=%08x\n",
1726 expect.Eax, expect.Ebx, expect.Ecx, expect.Edx, expect.Esi, expect.Edi,
1727 expect.Ebp, expect.Esp, expect.Eip, expect.SegCs, expect.SegDs, expect.SegEs,
1728 expect.SegFs, expect.SegGs, expect.SegSs, expect.EFlags, expect.prev_frame );
1729 trace( "actual: eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x ebp=%08x esp=%08x "
1730 "eip=%08x cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x\n",
1731 context.Eax, context.Ebx, context.Ecx, context.Edx, context.Esi, context.Edi,
1732 context.Ebp, context.Esp, context.Eip, context.SegCs, context.SegDs, context.SegEs,
1733 context.SegFs, context.SegGs, context.SegSs, context.EFlags );
1734 /* Eax, Ecx, Edx, EFlags are not preserved */
1735 COMPARE( Ebx );
1736 COMPARE( Esi );
1737 COMPARE( Edi );
1738 COMPARE( Ebp );
1739 /* Esp is the stack upon entry to NtGetContextThread */
1740 ok( context.Esp == expect.Esp - 12 || context.Esp == expect.Esp - 16,
1741 "wrong Esp %08x/%08x\n", context.Esp, expect.Esp );
1742 /* Eip is somewhere close to the NtGetContextThread implementation */
1743 ok( (char *)context.Eip >= (char *)pNtGetContextThread - 0x40000 &&
1744 (char *)context.Eip <= (char *)pNtGetContextThread + 0x40000,
1745 "wrong Eip %08x/%08x\n", context.Eip, (DWORD)pNtGetContextThread );
1746 /* segment registers clear the high word */
1747 ok( context.SegCs == LOWORD(expect.SegCs), "wrong SegCs %08x/%08x\n", context.SegCs, expect.SegCs );
1748 ok( context.SegDs == LOWORD(expect.SegDs), "wrong SegDs %08x/%08x\n", context.SegDs, expect.SegDs );
1749 ok( context.SegEs == LOWORD(expect.SegEs), "wrong SegEs %08x/%08x\n", context.SegEs, expect.SegEs );
1750 ok( context.SegFs == LOWORD(expect.SegFs), "wrong SegFs %08x/%08x\n", context.SegFs, expect.SegFs );
1751 ok( context.SegGs == LOWORD(expect.SegGs), "wrong SegGs %08x/%08x\n", context.SegGs, expect.SegGs );
1752 ok( context.SegSs == LOWORD(expect.SegSs), "wrong SegSs %08x/%08x\n", context.SegSs, expect.SegGs );
1754 ok( LOWORD(context.FloatSave.ControlWord) == LOWORD(expect.x87_control),
1755 "wrong x87 control word %#x/%#x.\n", context.FloatSave.ControlWord, expect.x87_control );
1756 ok( LOWORD(expect.x87_control) == LOWORD(new_x87_control),
1757 "x87 control word changed in NtGetContextThread() %#x/%#x.\n",
1758 LOWORD(expect.x87_control), LOWORD(new_x87_control) );
1760 #undef COMPARE
1763 static BYTE saved_KiUserExceptionDispatcher_bytes[7];
1764 static void *pKiUserExceptionDispatcher;
1765 static BOOL hook_called;
1766 static void *hook_KiUserExceptionDispatcher_eip;
1767 static void *dbg_except_continue_handler_eip;
1768 static void *hook_exception_address;
1770 static struct
1772 DWORD old_eax;
1773 DWORD old_edx;
1774 DWORD old_esi;
1775 DWORD old_edi;
1776 DWORD old_ebp;
1777 DWORD old_esp;
1778 DWORD new_eax;
1779 DWORD new_edx;
1780 DWORD new_esi;
1781 DWORD new_edi;
1782 DWORD new_ebp;
1783 DWORD new_esp;
1785 test_kiuserexceptiondispatcher_regs;
1787 static DWORD dbg_except_continue_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
1788 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher)
1790 ok(hook_called, "Hook was not called.\n");
1792 ok(rec->ExceptionCode == 0x80000003, "Got unexpected ExceptionCode %#x.\n", rec->ExceptionCode);
1794 got_exception = 1;
1795 dbg_except_continue_handler_eip = (void *)context->Eip;
1796 ++context->Eip;
1798 context->Eip = (DWORD)code_mem + 0x1c;
1799 context->Eax = 0xdeadbeef;
1800 context->Esi = 0xdeadbeef;
1801 pRtlUnwind(NtCurrentTeb()->Tib.ExceptionList, (void *)context->Eip, rec, (void *)0xdeadbeef);
1802 return ExceptionContinueExecution;
1805 static LONG WINAPI dbg_except_continue_vectored_handler(struct _EXCEPTION_POINTERS *e)
1807 EXCEPTION_RECORD *rec = e->ExceptionRecord;
1808 CONTEXT *context = e->ContextRecord;
1810 trace("dbg_except_continue_vectored_handler, code %#x, eip %#x, ExceptionAddress %p.\n",
1811 rec->ExceptionCode, context->Eip, rec->ExceptionAddress);
1813 ok(rec->ExceptionCode == 0x80000003, "Got unexpected ExceptionCode %#x.\n", rec->ExceptionCode);
1815 got_exception = 1;
1817 if ((ULONG_PTR)rec->ExceptionAddress == context->Eip + 1)
1819 /* XP and Vista+ have ExceptionAddress == Eip + 1, Eip is adjusted even
1820 * for software raised breakpoint exception.
1821 * Win2003 has Eip not adjusted and matching ExceptionAddress.
1822 * Win2008 has Eip not adjuated and ExceptionAddress not filled for
1823 * software raised exception. */
1824 context->Eip = (ULONG_PTR)rec->ExceptionAddress;
1827 return EXCEPTION_CONTINUE_EXECUTION;
1830 /* Use CDECL to leave arguments on stack. */
1831 void CDECL hook_KiUserExceptionDispatcher(EXCEPTION_RECORD *rec, CONTEXT *context)
1833 trace("rec %p, context %p.\n", rec, context);
1834 trace("context->Eip %#x, context->Esp %#x, ContextFlags %#x.\n",
1835 context->Eip, context->Esp, context->ContextFlags);
1837 hook_called = TRUE;
1838 /* Broken on Win2008, probably rec offset in stack is different. */
1839 ok(rec->ExceptionCode == 0x80000003 || broken(!rec->ExceptionCode),
1840 "Got unexpected ExceptionCode %#x.\n", rec->ExceptionCode);
1842 hook_KiUserExceptionDispatcher_eip = (void *)context->Eip;
1843 hook_exception_address = rec->ExceptionAddress;
1844 memcpy(pKiUserExceptionDispatcher, saved_KiUserExceptionDispatcher_bytes,
1845 sizeof(saved_KiUserExceptionDispatcher_bytes));
1848 static void test_kiuserexceptiondispatcher(void)
1850 PVOID vectored_handler;
1851 HMODULE hntdll = GetModuleHandleA("ntdll.dll");
1852 static BYTE except_code[] =
1854 0xb9, /* mov imm32, %ecx */
1855 /* offset: 0x1 */
1856 0x00, 0x00, 0x00, 0x00,
1858 0x89, 0x01, /* mov %eax, (%ecx) */
1859 0x89, 0x51, 0x04, /* mov %edx, 0x4(%ecx) */
1860 0x89, 0x71, 0x08, /* mov %esi, 0x8(%ecx) */
1861 0x89, 0x79, 0x0c, /* mov %edi, 0xc(%ecx) */
1862 0x89, 0x69, 0x10, /* mov %ebp, 0x10(%ecx) */
1863 0x89, 0x61, 0x14, /* mov %esp, 0x14(%ecx) */
1864 0x83, 0xc1, 0x18, /* add $0x18, %ecx */
1866 /* offset: 0x19 */
1867 0xcc, /* int3 */
1869 0x0f, 0x0b, /* ud2, illegal instruction */
1871 /* offset: 0x1c */
1872 0xb9, /* mov imm32, %ecx */
1873 /* offset: 0x1d */
1874 0x00, 0x00, 0x00, 0x00,
1876 0x89, 0x01, /* mov %eax, (%ecx) */
1877 0x89, 0x51, 0x04, /* mov %edx, 0x4(%ecx) */
1878 0x89, 0x71, 0x08, /* mov %esi, 0x8(%ecx) */
1879 0x89, 0x79, 0x0c, /* mov %edi, 0xc(%ecx) */
1880 0x89, 0x69, 0x10, /* mov %ebp, 0x10(%ecx) */
1881 0x89, 0x61, 0x14, /* mov %esp, 0x14(%ecx) */
1883 0x67, 0x48, 0x8b, 0x71, 0xf0, /* mov -0x10(%ecx),%esi */
1885 0xc3, /* ret */
1887 static BYTE hook_trampoline[] =
1889 0xff, 0x15,
1890 /* offset: 2 bytes */
1891 0x00, 0x00, 0x00, 0x00, /* callq *addr */ /* call hook implementation. */
1893 0xff, 0x25,
1894 /* offset: 8 bytes */
1895 0x00, 0x00, 0x00, 0x00, /* jmpq *addr */ /* jump to original function. */
1897 void *phook_KiUserExceptionDispatcher = hook_KiUserExceptionDispatcher;
1898 BYTE patched_KiUserExceptionDispatcher_bytes[7];
1899 void *phook_trampoline = hook_trampoline;
1900 DWORD old_protect1, old_protect2;
1901 EXCEPTION_RECORD record;
1902 void *bpt_address;
1903 BYTE *ptr;
1904 BOOL ret;
1906 pKiUserExceptionDispatcher = (void *)GetProcAddress(hntdll, "KiUserExceptionDispatcher");
1907 if (!pKiUserExceptionDispatcher)
1909 win_skip("KiUserExceptionDispatcher is not available.\n");
1910 return;
1913 if (!pRtlUnwind)
1915 win_skip("RtlUnwind is not available.\n");
1916 return;
1919 *(DWORD *)(except_code + 1) = (DWORD)&test_kiuserexceptiondispatcher_regs;
1920 *(DWORD *)(except_code + 0x1d) = (DWORD)&test_kiuserexceptiondispatcher_regs.new_eax;
1922 *(unsigned int *)(hook_trampoline + 2) = (ULONG_PTR)&phook_KiUserExceptionDispatcher;
1923 *(unsigned int *)(hook_trampoline + 8) = (ULONG_PTR)&pKiUserExceptionDispatcher;
1925 ret = VirtualProtect(hook_trampoline, ARRAY_SIZE(hook_trampoline), PAGE_EXECUTE_READWRITE, &old_protect1);
1926 ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
1928 ret = VirtualProtect(pKiUserExceptionDispatcher, sizeof(saved_KiUserExceptionDispatcher_bytes),
1929 PAGE_EXECUTE_READWRITE, &old_protect2);
1930 ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
1932 memcpy(saved_KiUserExceptionDispatcher_bytes, pKiUserExceptionDispatcher,
1933 sizeof(saved_KiUserExceptionDispatcher_bytes));
1935 ptr = patched_KiUserExceptionDispatcher_bytes;
1936 /* mov hook_trampoline, %eax */
1937 *ptr++ = 0xa1;
1938 *(void **)ptr = &phook_trampoline;
1939 ptr += sizeof(void *);
1940 /* jmp *eax */
1941 *ptr++ = 0xff;
1942 *ptr++ = 0xe0;
1944 memcpy(pKiUserExceptionDispatcher, patched_KiUserExceptionDispatcher_bytes,
1945 sizeof(patched_KiUserExceptionDispatcher_bytes));
1946 got_exception = 0;
1947 run_exception_test(dbg_except_continue_handler, NULL, except_code, ARRAY_SIZE(except_code),
1948 PAGE_EXECUTE_READ);
1950 ok(got_exception, "Handler was not called.\n");
1951 ok(hook_called, "Hook was not called.\n");
1953 ok(test_kiuserexceptiondispatcher_regs.new_eax == 0xdeadbeef, "Got unexpected eax %#x.\n",
1954 test_kiuserexceptiondispatcher_regs.new_eax);
1955 ok(test_kiuserexceptiondispatcher_regs.new_esi == 0xdeadbeef, "Got unexpected esi %#x.\n",
1956 test_kiuserexceptiondispatcher_regs.new_esi);
1957 ok(test_kiuserexceptiondispatcher_regs.old_edi
1958 == test_kiuserexceptiondispatcher_regs.new_edi, "edi does not match.\n");
1959 ok(test_kiuserexceptiondispatcher_regs.old_ebp
1960 == test_kiuserexceptiondispatcher_regs.new_ebp, "ebp does not match.\n");
1962 bpt_address = (BYTE *)code_mem + 0x19;
1964 ok(hook_exception_address == bpt_address || broken(!hook_exception_address) /* Win2008 */,
1965 "Got unexpected exception address %p, expected %p.\n",
1966 hook_exception_address, bpt_address);
1967 ok(hook_KiUserExceptionDispatcher_eip == bpt_address, "Got unexpected exception address %p, expected %p.\n",
1968 hook_KiUserExceptionDispatcher_eip, bpt_address);
1969 ok(dbg_except_continue_handler_eip == bpt_address, "Got unexpected exception address %p, expected %p.\n",
1970 dbg_except_continue_handler_eip, bpt_address);
1972 record.ExceptionCode = 0x80000003;
1973 record.ExceptionFlags = 0;
1974 record.ExceptionRecord = NULL;
1975 record.ExceptionAddress = NULL; /* does not matter, copied return address */
1976 record.NumberParameters = 0;
1978 vectored_handler = AddVectoredExceptionHandler(TRUE, dbg_except_continue_vectored_handler);
1980 memcpy(pKiUserExceptionDispatcher, patched_KiUserExceptionDispatcher_bytes,
1981 sizeof(patched_KiUserExceptionDispatcher_bytes));
1982 got_exception = 0;
1983 hook_called = FALSE;
1985 pRtlRaiseException(&record);
1987 ok(got_exception, "Handler was not called.\n");
1988 ok(hook_called || broken(!hook_called) /* 2003 */, "Hook was not called.\n");
1990 memcpy(pKiUserExceptionDispatcher, saved_KiUserExceptionDispatcher_bytes,
1991 sizeof(saved_KiUserExceptionDispatcher_bytes));
1993 RemoveVectoredExceptionHandler(vectored_handler);
1994 ret = VirtualProtect(pKiUserExceptionDispatcher, sizeof(saved_KiUserExceptionDispatcher_bytes),
1995 old_protect2, &old_protect2);
1996 ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
1997 ret = VirtualProtect(hook_trampoline, ARRAY_SIZE(hook_trampoline), old_protect1, &old_protect1);
1998 ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
2001 #elif defined(__x86_64__)
2003 #define UNW_FLAG_NHANDLER 0
2004 #define UNW_FLAG_EHANDLER 1
2005 #define UNW_FLAG_UHANDLER 2
2006 #define UNW_FLAG_CHAININFO 4
2008 #define UWOP_PUSH_NONVOL 0
2009 #define UWOP_ALLOC_LARGE 1
2010 #define UWOP_ALLOC_SMALL 2
2011 #define UWOP_SET_FPREG 3
2012 #define UWOP_SAVE_NONVOL 4
2013 #define UWOP_SAVE_NONVOL_FAR 5
2014 #define UWOP_SAVE_XMM128 8
2015 #define UWOP_SAVE_XMM128_FAR 9
2016 #define UWOP_PUSH_MACHFRAME 10
2018 struct results
2020 int rip_offset; /* rip offset from code start */
2021 int rbp_offset; /* rbp offset from stack pointer */
2022 int handler; /* expect handler to be set? */
2023 int rip; /* expected final rip value */
2024 int frame; /* expected frame return value */
2025 int regs[8][2]; /* expected values for registers */
2028 struct unwind_test
2030 const BYTE *function;
2031 size_t function_size;
2032 const BYTE *unwind_info;
2033 const struct results *results;
2034 unsigned int nb_results;
2035 const struct results *broken_results;
2038 enum regs
2040 rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi,
2041 r8, r9, r10, r11, r12, r13, r14, r15
2044 static const char * const reg_names[16] =
2046 "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
2047 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
2050 #define UWOP(code,info) (UWOP_##code | ((info) << 4))
2052 static void call_virtual_unwind( int testnum, const struct unwind_test *test )
2054 static const int code_offset = 1024;
2055 static const int unwind_offset = 2048;
2056 void *handler, *data;
2057 CONTEXT context;
2058 RUNTIME_FUNCTION runtime_func;
2059 KNONVOLATILE_CONTEXT_POINTERS ctx_ptr;
2060 UINT i, j, k, broken_k;
2061 ULONG64 fake_stack[256];
2062 ULONG64 frame, orig_rip, orig_rbp, unset_reg;
2063 UINT unwind_size = 4 + 2 * test->unwind_info[2] + 8;
2064 void *expected_handler, *broken_handler;
2066 memcpy( (char *)code_mem + code_offset, test->function, test->function_size );
2067 memcpy( (char *)code_mem + unwind_offset, test->unwind_info, unwind_size );
2069 runtime_func.BeginAddress = code_offset;
2070 runtime_func.EndAddress = code_offset + test->function_size;
2071 runtime_func.UnwindData = unwind_offset;
2073 trace( "code: %p stack: %p\n", code_mem, fake_stack );
2075 for (i = 0; i < test->nb_results; i++)
2077 memset( &ctx_ptr, 0, sizeof(ctx_ptr) );
2078 memset( &context, 0x55, sizeof(context) );
2079 memset( &unset_reg, 0x55, sizeof(unset_reg) );
2080 for (j = 0; j < 256; j++) fake_stack[j] = j * 8;
2082 context.Rsp = (ULONG_PTR)fake_stack;
2083 context.Rbp = (ULONG_PTR)fake_stack + test->results[i].rbp_offset;
2084 orig_rbp = context.Rbp;
2085 orig_rip = (ULONG64)code_mem + code_offset + test->results[i].rip_offset;
2087 trace( "%u/%u: rip=%p (%02x) rbp=%p rsp=%p\n", testnum, i,
2088 (void *)orig_rip, *(BYTE *)orig_rip, (void *)orig_rbp, (void *)context.Rsp );
2090 data = (void *)0xdeadbeef;
2091 handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG64)code_mem, orig_rip,
2092 &runtime_func, &context, &data, &frame, &ctx_ptr );
2094 expected_handler = test->results[i].handler ? (char *)code_mem + 0x200 : NULL;
2095 broken_handler = test->broken_results && test->broken_results[i].handler ? (char *)code_mem + 0x200 : NULL;
2097 ok( handler == expected_handler || broken( test->broken_results && handler == broken_handler ),
2098 "%u/%u: wrong handler %p/%p\n", testnum, i, handler, expected_handler );
2099 if (handler)
2100 ok( *(DWORD *)data == 0x08070605, "%u/%u: wrong handler data %p\n", testnum, i, data );
2101 else
2102 ok( data == (void *)0xdeadbeef, "%u/%u: handler data set to %p\n", testnum, i, data );
2104 ok( context.Rip == test->results[i].rip
2105 || broken( test->broken_results && context.Rip == test->broken_results[i].rip ),
2106 "%u/%u: wrong rip %p/%x\n", testnum, i, (void *)context.Rip, test->results[i].rip );
2107 ok( frame == (ULONG64)fake_stack + test->results[i].frame
2108 || broken( test->broken_results && frame == (ULONG64)fake_stack + test->broken_results[i].frame ),
2109 "%u/%u: wrong frame %p/%p\n",
2110 testnum, i, (void *)frame, (char *)fake_stack + test->results[i].frame );
2112 for (j = 0; j < 16; j++)
2114 static const UINT nb_regs = ARRAY_SIZE(test->results[i].regs);
2116 for (k = 0; k < nb_regs; k++)
2118 if (test->results[i].regs[k][0] == -1)
2120 k = nb_regs;
2121 break;
2123 if (test->results[i].regs[k][0] == j) break;
2126 if (test->broken_results)
2128 for (broken_k = 0; broken_k < nb_regs; broken_k++)
2130 if (test->broken_results[i].regs[broken_k][0] == -1)
2132 broken_k = nb_regs;
2133 break;
2135 if (test->broken_results[i].regs[broken_k][0] == j)
2136 break;
2139 else
2141 broken_k = k;
2144 if (j == rsp) /* rsp is special */
2146 ULONG64 expected_rsp, broken_rsp;
2148 ok( !ctx_ptr.IntegerContext[j],
2149 "%u/%u: rsp should not be set in ctx_ptr\n", testnum, i );
2150 expected_rsp = test->results[i].regs[k][1] < 0
2151 ? -test->results[i].regs[k][1] : (ULONG64)fake_stack + test->results[i].regs[k][1];
2152 if (test->broken_results)
2153 broken_rsp = test->broken_results[i].regs[k][1] < 0
2154 ? -test->broken_results[i].regs[k][1]
2155 : (ULONG64)fake_stack + test->broken_results[i].regs[k][1];
2156 else
2157 broken_rsp = expected_rsp;
2159 ok( context.Rsp == expected_rsp || broken( context.Rsp == broken_rsp ),
2160 "%u/%u: register rsp wrong %p/%p\n",
2161 testnum, i, (void *)context.Rsp, (void *)expected_rsp );
2162 continue;
2165 if (ctx_ptr.IntegerContext[j])
2167 ok( k < nb_regs || broken( broken_k < nb_regs ), "%u/%u: register %s should not be set to %lx\n",
2168 testnum, i, reg_names[j], *(&context.Rax + j) );
2169 ok( k == nb_regs || *(&context.Rax + j) == test->results[i].regs[k][1]
2170 || broken( broken_k == nb_regs || *(&context.Rax + j)
2171 == test->broken_results[i].regs[broken_k][1] ),
2172 "%u/%u: register %s wrong %p/%x\n",
2173 testnum, i, reg_names[j], (void *)*(&context.Rax + j), test->results[i].regs[k][1] );
2175 else
2177 ok( k == nb_regs || broken( broken_k == nb_regs ), "%u/%u: register %s should be set\n",
2178 testnum, i, reg_names[j] );
2179 if (j == rbp)
2180 ok( context.Rbp == orig_rbp, "%u/%u: register rbp wrong %p/unset\n",
2181 testnum, i, (void *)context.Rbp );
2182 else
2183 ok( *(&context.Rax + j) == unset_reg,
2184 "%u/%u: register %s wrong %p/unset\n",
2185 testnum, i, reg_names[j], (void *)*(&context.Rax + j));
2191 static void test_virtual_unwind(void)
2193 static const BYTE function_0[] =
2195 0xff, 0xf5, /* 00: push %rbp */
2196 0x48, 0x81, 0xec, 0x10, 0x01, 0x00, 0x00, /* 02: sub $0x110,%rsp */
2197 0x48, 0x8d, 0x6c, 0x24, 0x30, /* 09: lea 0x30(%rsp),%rbp */
2198 0x48, 0x89, 0x9d, 0xf0, 0x00, 0x00, 0x00, /* 0e: mov %rbx,0xf0(%rbp) */
2199 0x48, 0x89, 0xb5, 0xf8, 0x00, 0x00, 0x00, /* 15: mov %rsi,0xf8(%rbp) */
2200 0x90, /* 1c: nop */
2201 0x48, 0x8b, 0x9d, 0xf0, 0x00, 0x00, 0x00, /* 1d: mov 0xf0(%rbp),%rbx */
2202 0x48, 0x8b, 0xb5, 0xf8, 0x00, 0x00, 0x00, /* 24: mov 0xf8(%rbp),%rsi */
2203 0x48, 0x8d, 0xa5, 0xe0, 0x00, 0x00, 0x00, /* 2b: lea 0xe0(%rbp),%rsp */
2204 0x5d, /* 32: pop %rbp */
2205 0xc3 /* 33: ret */
2208 static const BYTE unwind_info_0[] =
2210 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */
2211 0x1c, /* prolog size */
2212 8, /* opcode count */
2213 (0x03 << 4) | rbp, /* frame reg rbp offset 0x30 */
2215 0x1c, UWOP(SAVE_NONVOL, rsi), 0x25, 0, /* 1c: mov %rsi,0x128(%rsp) */
2216 0x15, UWOP(SAVE_NONVOL, rbx), 0x24, 0, /* 15: mov %rbx,0x120(%rsp) */
2217 0x0e, UWOP(SET_FPREG, rbp), /* 0e: lea 0x30(%rsp),rbp */
2218 0x09, UWOP(ALLOC_LARGE, 0), 0x22, 0, /* 09: sub $0x110,%rsp */
2219 0x02, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */
2221 0x00, 0x02, 0x00, 0x00, /* handler */
2222 0x05, 0x06, 0x07, 0x08, /* data */
2225 static const struct results results_0[] =
2227 /* offset rbp handler rip frame registers */
2228 { 0x00, 0x40, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
2229 { 0x02, 0x40, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbp,0x000}, {-1,-1} }},
2230 { 0x09, 0x40, FALSE, 0x118, 0x000, { {rsp,0x120}, {rbp,0x110}, {-1,-1} }},
2231 { 0x0e, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1} }},
2232 { 0x15, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {-1,-1} }},
2233 { 0x1c, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
2234 { 0x1d, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
2235 { 0x24, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}},
2236 { 0x2b, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1}}},
2237 { 0x32, 0x40, FALSE, 0x008, 0x010, { {rsp,0x010}, {rbp,0x000}, {-1,-1}}},
2238 { 0x33, 0x40, FALSE, 0x000, 0x010, { {rsp,0x008}, {-1,-1}}},
2242 static const BYTE function_1[] =
2244 0x53, /* 00: push %rbx */
2245 0x55, /* 01: push %rbp */
2246 0x56, /* 02: push %rsi */
2247 0x57, /* 03: push %rdi */
2248 0x41, 0x54, /* 04: push %r12 */
2249 0x48, 0x83, 0xec, 0x30, /* 06: sub $0x30,%rsp */
2250 0x90, 0x90, /* 0a: nop; nop */
2251 0x48, 0x83, 0xc4, 0x30, /* 0c: add $0x30,%rsp */
2252 0x41, 0x5c, /* 10: pop %r12 */
2253 0x5f, /* 12: pop %rdi */
2254 0x5e, /* 13: pop %rsi */
2255 0x5d, /* 14: pop %rbp */
2256 0x5b, /* 15: pop %rbx */
2257 0xc3 /* 16: ret */
2260 static const BYTE unwind_info_1[] =
2262 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */
2263 0x0a, /* prolog size */
2264 6, /* opcode count */
2265 0, /* frame reg */
2267 0x0a, UWOP(ALLOC_SMALL, 5), /* 0a: sub $0x30,%rsp */
2268 0x06, UWOP(PUSH_NONVOL, r12), /* 06: push %r12 */
2269 0x04, UWOP(PUSH_NONVOL, rdi), /* 04: push %rdi */
2270 0x03, UWOP(PUSH_NONVOL, rsi), /* 03: push %rsi */
2271 0x02, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */
2272 0x01, UWOP(PUSH_NONVOL, rbx), /* 01: push %rbx */
2274 0x00, 0x02, 0x00, 0x00, /* handler */
2275 0x05, 0x06, 0x07, 0x08, /* data */
2278 static const struct results results_1[] =
2280 /* offset rbp handler rip frame registers */
2281 { 0x00, 0x50, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
2282 { 0x01, 0x50, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbx,0x000}, {-1,-1} }},
2283 { 0x02, 0x50, FALSE, 0x010, 0x000, { {rsp,0x018}, {rbx,0x008}, {rbp,0x000}, {-1,-1} }},
2284 { 0x03, 0x50, FALSE, 0x018, 0x000, { {rsp,0x020}, {rbx,0x010}, {rbp,0x008}, {rsi,0x000}, {-1,-1} }},
2285 { 0x04, 0x50, FALSE, 0x020, 0x000, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }},
2286 { 0x06, 0x50, FALSE, 0x028, 0x000, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }},
2287 { 0x0a, 0x50, TRUE, 0x058, 0x000, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }},
2288 { 0x0c, 0x50, FALSE, 0x058, 0x000, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }},
2289 { 0x10, 0x50, FALSE, 0x028, 0x000, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }},
2290 { 0x12, 0x50, FALSE, 0x020, 0x000, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }},
2291 { 0x13, 0x50, FALSE, 0x018, 0x000, { {rsp,0x020}, {rbx,0x010}, {rbp,0x008}, {rsi,0x000}, {-1,-1} }},
2292 { 0x14, 0x50, FALSE, 0x010, 0x000, { {rsp,0x018}, {rbx,0x008}, {rbp,0x000}, {-1,-1} }},
2293 { 0x15, 0x50, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbx,0x000}, {-1,-1} }},
2294 { 0x16, 0x50, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
2297 static const BYTE function_2[] =
2299 0x55, /* 00: push %rbp */
2300 0x90, 0x90, /* 01: nop; nop */
2301 0x5d, /* 03: pop %rbp */
2302 0xc3 /* 04: ret */
2305 static const BYTE unwind_info_2[] =
2307 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */
2308 0x0, /* prolog size */
2309 2, /* opcode count */
2310 0, /* frame reg */
2312 0x01, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */
2313 0x00, UWOP(PUSH_MACHFRAME, 0), /* 00 */
2315 0x00, 0x02, 0x00, 0x00, /* handler */
2316 0x05, 0x06, 0x07, 0x08, /* data */
2319 static const struct results results_2[] =
2321 /* offset rbp handler rip frame registers */
2322 { 0x01, 0x50, TRUE, 0x008, 0x000, { {rsp,-0x020}, {rbp,0x000}, {-1,-1} }},
2325 static const BYTE unwind_info_3[] =
2327 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */
2328 0x0, /* prolog size */
2329 2, /* opcode count */
2330 0, /* frame reg */
2332 0x01, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */
2333 0x00, UWOP(PUSH_MACHFRAME, 1), /* 00 */
2335 0x00, 0x02, 0x00, 0x00, /* handler */
2336 0x05, 0x06, 0x07, 0x08, /* data */
2339 static const struct results results_3[] =
2341 /* offset rbp handler rip frame registers */
2342 { 0x01, 0x50, TRUE, 0x010, 0x000, { {rsp,-0x028}, {rbp,0x000}, {-1,-1} }},
2345 static const BYTE function_4[] =
2347 0x55, /* 00: push %rbp */
2348 0x5d, /* 01: pop %rbp */
2349 0xc3 /* 02: ret */
2352 static const BYTE unwind_info_4[] =
2354 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */
2355 0x0, /* prolog size */
2356 0, /* opcode count */
2357 0, /* frame reg */
2359 0x00, 0x02, 0x00, 0x00, /* handler */
2360 0x05, 0x06, 0x07, 0x08, /* data */
2363 static const struct results results_4[] =
2365 /* offset rbp handler rip frame registers */
2366 { 0x01, 0x50, TRUE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }},
2369 static const struct results broken_results_4[] =
2371 /* offset rbp handler rip frame registers */
2372 { 0x01, 0x50, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbp,0x000}, {-1,-1} }},
2375 static const struct unwind_test tests[] =
2377 { function_0, sizeof(function_0), unwind_info_0, results_0, ARRAY_SIZE(results_0) },
2378 { function_1, sizeof(function_1), unwind_info_1, results_1, ARRAY_SIZE(results_1) },
2379 { function_2, sizeof(function_2), unwind_info_2, results_2, ARRAY_SIZE(results_2) },
2380 { function_2, sizeof(function_2), unwind_info_3, results_3, ARRAY_SIZE(results_3) },
2382 /* Broken before Win10 1809. */
2383 { function_4, sizeof(function_4), unwind_info_4, results_4, ARRAY_SIZE(results_4), broken_results_4 },
2385 unsigned int i;
2387 for (i = 0; i < ARRAY_SIZE(tests); i++)
2388 call_virtual_unwind( i, &tests[i] );
2391 static int consolidate_dummy_called;
2392 static PVOID CALLBACK test_consolidate_dummy(EXCEPTION_RECORD *rec)
2394 CONTEXT *ctx = (CONTEXT *)rec->ExceptionInformation[1];
2395 consolidate_dummy_called = 1;
2396 ok(ctx->Rip == 0xdeadbeef, "test_consolidate_dummy failed for Rip, expected: 0xdeadbeef, got: %lx\n", ctx->Rip);
2397 return (PVOID)rec->ExceptionInformation[2];
2400 static void test_restore_context(void)
2402 SETJMP_FLOAT128 *fltsave;
2403 EXCEPTION_RECORD rec;
2404 _JUMP_BUFFER buf;
2405 CONTEXT ctx;
2406 int i, pass;
2408 if (!pRtlUnwindEx || !pRtlRestoreContext || !pRtlCaptureContext || !p_setjmp)
2410 skip("RtlUnwindEx/RtlCaptureContext/RtlRestoreContext/_setjmp not found\n");
2411 return;
2414 /* RtlRestoreContext(NULL, NULL); crashes on Windows */
2416 /* test simple case of capture and restore context */
2417 pass = 0;
2418 InterlockedIncrement(&pass); /* interlocked to prevent compiler from moving after capture */
2419 pRtlCaptureContext(&ctx);
2420 if (InterlockedIncrement(&pass) == 2) /* interlocked to prevent compiler from moving before capture */
2422 pRtlRestoreContext(&ctx, NULL);
2423 ok(0, "shouldn't be reached\n");
2425 else
2426 ok(pass < 4, "unexpected pass %d\n", pass);
2428 /* test with jmp using RltRestoreContext */
2429 pass = 0;
2430 InterlockedIncrement(&pass);
2431 RtlCaptureContext(&ctx);
2432 InterlockedIncrement(&pass); /* only called once */
2433 p_setjmp(&buf);
2434 InterlockedIncrement(&pass);
2435 if (pass == 3)
2437 rec.ExceptionCode = STATUS_LONGJUMP;
2438 rec.NumberParameters = 1;
2439 rec.ExceptionInformation[0] = (DWORD64)&buf;
2441 /* uses buf.Rip instead of ctx.Rip */
2442 pRtlRestoreContext(&ctx, &rec);
2443 ok(0, "shouldn't be reached\n");
2445 else if (pass == 4)
2447 ok(buf.Rbx == ctx.Rbx, "longjmp failed for Rbx, expected: %lx, got: %lx\n", buf.Rbx, ctx.Rbx);
2448 ok(buf.Rsp == ctx.Rsp, "longjmp failed for Rsp, expected: %lx, got: %lx\n", buf.Rsp, ctx.Rsp);
2449 ok(buf.Rbp == ctx.Rbp, "longjmp failed for Rbp, expected: %lx, got: %lx\n", buf.Rbp, ctx.Rbp);
2450 ok(buf.Rsi == ctx.Rsi, "longjmp failed for Rsi, expected: %lx, got: %lx\n", buf.Rsi, ctx.Rsi);
2451 ok(buf.Rdi == ctx.Rdi, "longjmp failed for Rdi, expected: %lx, got: %lx\n", buf.Rdi, ctx.Rdi);
2452 ok(buf.R12 == ctx.R12, "longjmp failed for R12, expected: %lx, got: %lx\n", buf.R12, ctx.R12);
2453 ok(buf.R13 == ctx.R13, "longjmp failed for R13, expected: %lx, got: %lx\n", buf.R13, ctx.R13);
2454 ok(buf.R14 == ctx.R14, "longjmp failed for R14, expected: %lx, got: %lx\n", buf.R14, ctx.R14);
2455 ok(buf.R15 == ctx.R15, "longjmp failed for R15, expected: %lx, got: %lx\n", buf.R15, ctx.R15);
2457 fltsave = &buf.Xmm6;
2458 for (i = 0; i < 10; i++)
2460 ok(fltsave[i].Part[0] == ctx.FltSave.XmmRegisters[i + 6].Low,
2461 "longjmp failed for Xmm%d, expected %lx, got %lx\n", i + 6,
2462 fltsave[i].Part[0], ctx.FltSave.XmmRegisters[i + 6].Low);
2464 ok(fltsave[i].Part[1] == ctx.FltSave.XmmRegisters[i + 6].High,
2465 "longjmp failed for Xmm%d, expected %lx, got %lx\n", i + 6,
2466 fltsave[i].Part[1], ctx.FltSave.XmmRegisters[i + 6].High);
2469 else
2470 ok(0, "unexpected pass %d\n", pass);
2472 /* test with jmp through RtlUnwindEx */
2473 pass = 0;
2474 InterlockedIncrement(&pass);
2475 pRtlCaptureContext(&ctx);
2476 InterlockedIncrement(&pass); /* only called once */
2477 p_setjmp(&buf);
2478 InterlockedIncrement(&pass);
2479 if (pass == 3)
2481 rec.ExceptionCode = STATUS_LONGJUMP;
2482 rec.NumberParameters = 1;
2483 rec.ExceptionInformation[0] = (DWORD64)&buf;
2485 /* uses buf.Rip instead of bogus 0xdeadbeef */
2486 pRtlUnwindEx((void*)buf.Rsp, (void*)0xdeadbeef, &rec, NULL, &ctx, NULL);
2487 ok(0, "shouldn't be reached\n");
2489 else
2490 ok(pass == 4, "unexpected pass %d\n", pass);
2493 /* test with consolidate */
2494 pass = 0;
2495 InterlockedIncrement(&pass);
2496 RtlCaptureContext(&ctx);
2497 InterlockedIncrement(&pass);
2498 if (pass == 2)
2500 rec.ExceptionCode = STATUS_UNWIND_CONSOLIDATE;
2501 rec.NumberParameters = 3;
2502 rec.ExceptionInformation[0] = (DWORD64)test_consolidate_dummy;
2503 rec.ExceptionInformation[1] = (DWORD64)&ctx;
2504 rec.ExceptionInformation[2] = ctx.Rip;
2505 ctx.Rip = 0xdeadbeef;
2507 pRtlRestoreContext(&ctx, &rec);
2508 ok(0, "shouldn't be reached\n");
2510 else if (pass == 3)
2511 ok(consolidate_dummy_called, "test_consolidate_dummy not called\n");
2512 else
2513 ok(0, "unexpected pass %d\n", pass);
2516 static RUNTIME_FUNCTION* CALLBACK dynamic_unwind_callback( DWORD64 pc, PVOID context )
2518 static const int code_offset = 1024;
2519 static RUNTIME_FUNCTION runtime_func;
2520 (*(DWORD *)context)++;
2522 runtime_func.BeginAddress = code_offset + 16;
2523 runtime_func.EndAddress = code_offset + 32;
2524 runtime_func.UnwindData = 0;
2525 return &runtime_func;
2528 static void test_dynamic_unwind(void)
2530 static const int code_offset = 1024;
2531 char buf[2 * sizeof(RUNTIME_FUNCTION) + 4];
2532 RUNTIME_FUNCTION *runtime_func, *func;
2533 ULONG_PTR table, base;
2534 void *growable_table;
2535 NTSTATUS status;
2536 DWORD count;
2538 /* Test RtlAddFunctionTable with aligned RUNTIME_FUNCTION pointer */
2539 runtime_func = (RUNTIME_FUNCTION *)buf;
2540 runtime_func->BeginAddress = code_offset;
2541 runtime_func->EndAddress = code_offset + 16;
2542 runtime_func->UnwindData = 0;
2543 ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
2544 "RtlAddFunctionTable failed for runtime_func = %p (aligned)\n", runtime_func );
2546 /* Lookup function outside of any function table */
2547 base = 0xdeadbeef;
2548 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 16, &base, NULL );
2549 ok( func == NULL,
2550 "RtlLookupFunctionEntry returned unexpected function, expected: NULL, got: %p\n", func );
2551 ok( !base || broken(base == 0xdeadbeef),
2552 "RtlLookupFunctionEntry modified base address, expected: 0, got: %lx\n", base );
2554 /* Test with pointer inside of our function */
2555 base = 0xdeadbeef;
2556 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 8, &base, NULL );
2557 ok( func == runtime_func,
2558 "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func );
2559 ok( base == (ULONG_PTR)code_mem,
2560 "RtlLookupFunctionEntry returned invalid base, expected: %lx, got: %lx\n", (ULONG_PTR)code_mem, base );
2562 /* Test RtlDeleteFunctionTable */
2563 ok( pRtlDeleteFunctionTable( runtime_func ),
2564 "RtlDeleteFunctionTable failed for runtime_func = %p (aligned)\n", runtime_func );
2565 ok( !pRtlDeleteFunctionTable( runtime_func ),
2566 "RtlDeleteFunctionTable returned success for nonexistent table runtime_func = %p\n", runtime_func );
2568 /* Unaligned RUNTIME_FUNCTION pointer */
2569 runtime_func = (RUNTIME_FUNCTION *)((ULONG_PTR)buf | 0x3);
2570 runtime_func->BeginAddress = code_offset;
2571 runtime_func->EndAddress = code_offset + 16;
2572 runtime_func->UnwindData = 0;
2573 ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
2574 "RtlAddFunctionTable failed for runtime_func = %p (unaligned)\n", runtime_func );
2575 ok( pRtlDeleteFunctionTable( runtime_func ),
2576 "RtlDeleteFunctionTable failed for runtime_func = %p (unaligned)\n", runtime_func );
2578 /* Attempt to insert the same entry twice */
2579 runtime_func = (RUNTIME_FUNCTION *)buf;
2580 runtime_func->BeginAddress = code_offset;
2581 runtime_func->EndAddress = code_offset + 16;
2582 runtime_func->UnwindData = 0;
2583 ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
2584 "RtlAddFunctionTable failed for runtime_func = %p (first attempt)\n", runtime_func );
2585 ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ),
2586 "RtlAddFunctionTable failed for runtime_func = %p (second attempt)\n", runtime_func );
2587 ok( pRtlDeleteFunctionTable( runtime_func ),
2588 "RtlDeleteFunctionTable failed for runtime_func = %p (first attempt)\n", runtime_func );
2589 ok( pRtlDeleteFunctionTable( runtime_func ),
2590 "RtlDeleteFunctionTable failed for runtime_func = %p (second attempt)\n", runtime_func );
2591 ok( !pRtlDeleteFunctionTable( runtime_func ),
2592 "RtlDeleteFunctionTable returned success for nonexistent table runtime_func = %p\n", runtime_func );
2594 /* Empty table */
2595 ok( pRtlAddFunctionTable( runtime_func, 0, (ULONG_PTR)code_mem ),
2596 "RtlAddFunctionTable failed for empty table\n" );
2597 ok( pRtlDeleteFunctionTable( runtime_func ),
2598 "RtlDeleteFunctionTable failed for empty table\n" );
2599 ok( !pRtlDeleteFunctionTable( runtime_func ),
2600 "RtlDeleteFunctionTable succeeded twice for empty table\n" );
2602 /* Test RtlInstallFunctionTableCallback with both low bits unset */
2603 table = (ULONG_PTR)code_mem;
2604 ok( !pRtlInstallFunctionTableCallback( table, (ULONG_PTR)code_mem, code_offset + 32, &dynamic_unwind_callback, (PVOID*)&count, NULL ),
2605 "RtlInstallFunctionTableCallback returned success for table = %lx\n", table );
2607 /* Test RtlInstallFunctionTableCallback with both low bits set */
2608 table = (ULONG_PTR)code_mem | 0x3;
2609 ok( pRtlInstallFunctionTableCallback( table, (ULONG_PTR)code_mem, code_offset + 32, &dynamic_unwind_callback, (PVOID*)&count, NULL ),
2610 "RtlInstallFunctionTableCallback failed for table = %lx\n", table );
2612 /* Lookup function outside of any function table */
2613 count = 0;
2614 base = 0xdeadbeef;
2615 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 32, &base, NULL );
2616 ok( func == NULL,
2617 "RtlLookupFunctionEntry returned unexpected function, expected: NULL, got: %p\n", func );
2618 ok( !base || broken(base == 0xdeadbeef),
2619 "RtlLookupFunctionEntry modified base address, expected: 0, got: %lx\n", base );
2620 ok( !count,
2621 "RtlLookupFunctionEntry issued %d unexpected calls to dynamic_unwind_callback\n", count );
2623 /* Test with pointer inside of our function */
2624 count = 0;
2625 base = 0xdeadbeef;
2626 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 24, &base, NULL );
2627 ok( func != NULL && func->BeginAddress == code_offset + 16 && func->EndAddress == code_offset + 32,
2628 "RtlLookupFunctionEntry didn't return expected function, got: %p\n", func );
2629 ok( base == (ULONG_PTR)code_mem,
2630 "RtlLookupFunctionEntry returned invalid base, expected: %lx, got: %lx\n", (ULONG_PTR)code_mem, base );
2631 ok( count == 1,
2632 "RtlLookupFunctionEntry issued %d calls to dynamic_unwind_callback, expected: 1\n", count );
2634 /* Clean up again */
2635 ok( pRtlDeleteFunctionTable( (PRUNTIME_FUNCTION)table ),
2636 "RtlDeleteFunctionTable failed for table = %p\n", (PVOID)table );
2637 ok( !pRtlDeleteFunctionTable( (PRUNTIME_FUNCTION)table ),
2638 "RtlDeleteFunctionTable returned success for nonexistent table = %p\n", (PVOID)table );
2640 if (!pRtlAddGrowableFunctionTable)
2642 win_skip("Growable function tables are not supported.\n");
2643 return;
2646 runtime_func = (RUNTIME_FUNCTION *)buf;
2647 runtime_func->BeginAddress = code_offset;
2648 runtime_func->EndAddress = code_offset + 16;
2649 runtime_func->UnwindData = 0;
2650 runtime_func++;
2651 runtime_func->BeginAddress = code_offset + 16;
2652 runtime_func->EndAddress = code_offset + 32;
2653 runtime_func->UnwindData = 0;
2654 runtime_func = (RUNTIME_FUNCTION *)buf;
2656 growable_table = NULL;
2657 status = pRtlAddGrowableFunctionTable( &growable_table, runtime_func, 1, 1, (ULONG_PTR)code_mem, (ULONG_PTR)code_mem + 64 );
2658 ok(!status, "RtlAddGrowableFunctionTable failed for runtime_func = %p (aligned), %#x.\n", runtime_func, status );
2659 ok(growable_table != 0, "Unexpected table value.\n");
2660 pRtlDeleteGrowableFunctionTable( growable_table );
2662 growable_table = NULL;
2663 status = pRtlAddGrowableFunctionTable( &growable_table, runtime_func, 2, 2, (ULONG_PTR)code_mem, (ULONG_PTR)code_mem + 64 );
2664 ok(!status, "RtlAddGrowableFunctionTable failed for runtime_func = %p (aligned), %#x.\n", runtime_func, status );
2665 ok(growable_table != 0, "Unexpected table value.\n");
2666 pRtlDeleteGrowableFunctionTable( growable_table );
2668 growable_table = NULL;
2669 status = pRtlAddGrowableFunctionTable( &growable_table, runtime_func, 1, 2, (ULONG_PTR)code_mem, (ULONG_PTR)code_mem + 64 );
2670 ok(!status, "RtlAddGrowableFunctionTable failed for runtime_func = %p (aligned), %#x.\n", runtime_func, status );
2671 ok(growable_table != 0, "Unexpected table value.\n");
2672 pRtlDeleteGrowableFunctionTable( growable_table );
2674 growable_table = NULL;
2675 status = pRtlAddGrowableFunctionTable( &growable_table, runtime_func, 0, 2, (ULONG_PTR)code_mem,
2676 (ULONG_PTR)code_mem + code_offset + 64 );
2677 ok(!status, "RtlAddGrowableFunctionTable failed for runtime_func = %p (aligned), %#x.\n", runtime_func, status );
2678 ok(growable_table != 0, "Unexpected table value.\n");
2680 /* Current count is 0. */
2681 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 8, &base, NULL );
2682 ok( func == NULL,
2683 "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func );
2685 pRtlGrowFunctionTable( growable_table, 1 );
2687 base = 0xdeadbeef;
2688 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 8, &base, NULL );
2689 ok( func == runtime_func,
2690 "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func );
2691 ok( base == (ULONG_PTR)code_mem,
2692 "RtlLookupFunctionEntry returned invalid base, expected: %lx, got: %lx\n", (ULONG_PTR)code_mem, base );
2694 /* Second function is inaccessible yet. */
2695 base = 0xdeadbeef;
2696 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 16, &base, NULL );
2697 ok( func == NULL,
2698 "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func );
2700 pRtlGrowFunctionTable( growable_table, 2 );
2702 base = 0xdeadbeef;
2703 func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 16, &base, NULL );
2704 ok( func == runtime_func + 1,
2705 "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func );
2706 ok( base == (ULONG_PTR)code_mem,
2707 "RtlLookupFunctionEntry returned invalid base, expected: %lx, got: %lx\n", (ULONG_PTR)code_mem, base );
2709 pRtlDeleteGrowableFunctionTable( growable_table );
2712 static int termination_handler_called;
2713 static void WINAPI termination_handler(ULONG flags, ULONG64 frame)
2715 termination_handler_called++;
2717 ok(flags == 1 || broken(flags == 0x401), "flags = %x\n", flags);
2718 ok(frame == 0x1234, "frame = %p\n", (void*)frame);
2721 static void test___C_specific_handler(void)
2723 DISPATCHER_CONTEXT dispatch;
2724 EXCEPTION_RECORD rec;
2725 CONTEXT context;
2726 ULONG64 frame;
2727 EXCEPTION_DISPOSITION ret;
2728 SCOPE_TABLE scope_table;
2730 if (!p__C_specific_handler)
2732 win_skip("__C_specific_handler not available\n");
2733 return;
2736 memset(&rec, 0, sizeof(rec));
2737 rec.ExceptionFlags = 2; /* EH_UNWINDING */
2738 frame = 0x1234;
2739 memset(&dispatch, 0, sizeof(dispatch));
2740 dispatch.ImageBase = (ULONG_PTR)GetModuleHandleA(NULL);
2741 dispatch.ControlPc = dispatch.ImageBase + 0x200;
2742 dispatch.HandlerData = &scope_table;
2743 dispatch.ContextRecord = &context;
2744 scope_table.Count = 1;
2745 scope_table.ScopeRecord[0].BeginAddress = 0x200;
2746 scope_table.ScopeRecord[0].EndAddress = 0x400;
2747 scope_table.ScopeRecord[0].HandlerAddress = (ULONG_PTR)termination_handler-dispatch.ImageBase;
2748 scope_table.ScopeRecord[0].JumpTarget = 0;
2749 memset(&context, 0, sizeof(context));
2751 termination_handler_called = 0;
2752 ret = p__C_specific_handler(&rec, frame, &context, &dispatch);
2753 ok(ret == ExceptionContinueSearch, "__C_specific_handler returned %x\n", ret);
2754 ok(termination_handler_called == 1, "termination_handler_called = %d\n",
2755 termination_handler_called);
2756 ok(dispatch.ScopeIndex == 1, "dispatch.ScopeIndex = %d\n", dispatch.ScopeIndex);
2758 ret = p__C_specific_handler(&rec, frame, &context, &dispatch);
2759 ok(ret == ExceptionContinueSearch, "__C_specific_handler returned %x\n", ret);
2760 ok(termination_handler_called == 1, "termination_handler_called = %d\n",
2761 termination_handler_called);
2762 ok(dispatch.ScopeIndex == 1, "dispatch.ScopeIndex = %d\n", dispatch.ScopeIndex);
2765 /* This is heavily based on the i386 exception tests. */
2766 static const struct exception
2768 BYTE code[18]; /* asm code */
2769 BYTE offset; /* offset of faulting instruction */
2770 BYTE length; /* length of faulting instruction */
2771 NTSTATUS status; /* expected status code */
2772 DWORD nb_params; /* expected number of parameters */
2773 ULONG64 params[4]; /* expected parameters */
2774 NTSTATUS alt_status; /* alternative status code */
2775 DWORD alt_nb_params; /* alternative number of parameters */
2776 ULONG64 alt_params[4]; /* alternative parameters */
2777 } exceptions[] =
2779 /* 0 */
2780 /* test some privileged instructions */
2781 { { 0xfb, 0xc3 }, /* 0: sti; ret */
2782 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
2783 { { 0x6c, 0xc3 }, /* 1: insb (%dx); ret */
2784 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
2785 { { 0x6d, 0xc3 }, /* 2: insl (%dx); ret */
2786 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
2787 { { 0x6e, 0xc3 }, /* 3: outsb (%dx); ret */
2788 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
2789 { { 0x6f, 0xc3 }, /* 4: outsl (%dx); ret */
2790 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
2791 /* 5 */
2792 { { 0xe4, 0x11, 0xc3 }, /* 5: inb $0x11,%al; ret */
2793 0, 2, STATUS_PRIVILEGED_INSTRUCTION, 0 },
2794 { { 0xe5, 0x11, 0xc3 }, /* 6: inl $0x11,%eax; ret */
2795 0, 2, STATUS_PRIVILEGED_INSTRUCTION, 0 },
2796 { { 0xe6, 0x11, 0xc3 }, /* 7: outb %al,$0x11; ret */
2797 0, 2, STATUS_PRIVILEGED_INSTRUCTION, 0 },
2798 { { 0xe7, 0x11, 0xc3 }, /* 8: outl %eax,$0x11; ret */
2799 0, 2, STATUS_PRIVILEGED_INSTRUCTION, 0 },
2800 { { 0xed, 0xc3 }, /* 9: inl (%dx),%eax; ret */
2801 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
2802 /* 10 */
2803 { { 0xee, 0xc3 }, /* 10: outb %al,(%dx); ret */
2804 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
2805 { { 0xef, 0xc3 }, /* 11: outl %eax,(%dx); ret */
2806 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
2807 { { 0xf4, 0xc3 }, /* 12: hlt; ret */
2808 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
2809 { { 0xfa, 0xc3 }, /* 13: cli; ret */
2810 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
2812 /* test iret to invalid selector */
2813 { { 0x6a, 0x00, 0x6a, 0x00, 0x6a, 0x00, 0xcf, 0x83, 0xc4, 0x18, 0xc3 },
2814 /* 15: pushq $0; pushq $0; pushq $0; iret; addl $24,%esp; ret */
2815 6, 1, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffffffffffff } },
2816 /* 15 */
2817 /* test loading an invalid selector */
2818 { { 0xb8, 0xef, 0xbe, 0x00, 0x00, 0x8e, 0xe8, 0xc3 }, /* 16: mov $beef,%ax; mov %ax,%gs; ret */
2819 5, 2, STATUS_ACCESS_VIOLATION, 2, { 0, 0xbee8 } }, /* 0xbee8 or 0xffffffff */
2821 /* test overlong instruction (limit is 15 bytes) */
2822 { { 0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0xfa,0xc3 },
2823 0, 16, STATUS_ILLEGAL_INSTRUCTION, 0, { 0 },
2824 STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffffffffffff } },
2826 /* test invalid interrupt */
2827 { { 0xcd, 0xff, 0xc3 }, /* int $0xff; ret */
2828 0, 2, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffffffffffff } },
2830 /* test moves to/from Crx */
2831 { { 0x0f, 0x20, 0xc0, 0xc3 }, /* movl %cr0,%eax; ret */
2832 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
2833 { { 0x0f, 0x20, 0xe0, 0xc3 }, /* movl %cr4,%eax; ret */
2834 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
2835 /* 20 */
2836 { { 0x0f, 0x22, 0xc0, 0xc3 }, /* movl %eax,%cr0; ret */
2837 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
2838 { { 0x0f, 0x22, 0xe0, 0xc3 }, /* movl %eax,%cr4; ret */
2839 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
2841 /* test moves to/from Drx */
2842 { { 0x0f, 0x21, 0xc0, 0xc3 }, /* movl %dr0,%eax; ret */
2843 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
2844 { { 0x0f, 0x21, 0xc8, 0xc3 }, /* movl %dr1,%eax; ret */
2845 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
2846 { { 0x0f, 0x21, 0xf8, 0xc3 }, /* movl %dr7,%eax; ret */
2847 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
2848 /* 25 */
2849 { { 0x0f, 0x23, 0xc0, 0xc3 }, /* movl %eax,%dr0; ret */
2850 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
2851 { { 0x0f, 0x23, 0xc8, 0xc3 }, /* movl %eax,%dr1; ret */
2852 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
2853 { { 0x0f, 0x23, 0xf8, 0xc3 }, /* movl %eax,%dr7; ret */
2854 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
2856 /* test memory reads */
2857 { { 0xa1, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xfffffffffffffffc,%eax; ret */
2858 0, 9, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffffffffffc } },
2859 { { 0xa1, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xfffffffffffffffd,%eax; ret */
2860 0, 9, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffffffffffd } },
2861 /* 30 */
2862 { { 0xa1, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xfffffffffffffffe,%eax; ret */
2863 0, 9, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffffffffffe } },
2864 { { 0xa1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xffffffffffffffff,%eax; ret */
2865 0, 9, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffffffffffff } },
2867 /* test memory writes */
2868 { { 0xa3, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xfffffffffffffffc; ret */
2869 0, 9, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffffffffffc } },
2870 { { 0xa3, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xfffffffffffffffd; ret */
2871 0, 9, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffffffffffd } },
2872 { { 0xa3, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xfffffffffffffffe; ret */
2873 0, 9, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffffffffffe } },
2874 /* 35 */
2875 { { 0xa3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xffffffffffffffff; ret */
2876 0, 9, STATUS_ACCESS_VIOLATION, 2, { 1, 0xffffffffffffffff } },
2877 { { 0xf1, 0x90, 0xc3 }, /* icebp; nop; ret */
2878 1, 1, STATUS_SINGLE_STEP, 0 },
2879 { { 0xcd, 0x2c, 0xc3 },
2880 0, 2, STATUS_ASSERTION_FAILURE, 0 },
2883 static int got_exception;
2885 static void run_exception_test(void *handler, const void* context,
2886 const void *code, unsigned int code_size,
2887 DWORD access)
2889 unsigned char buf[8 + 6 + 8 + 8];
2890 RUNTIME_FUNCTION runtime_func;
2891 UNWIND_INFO *unwind = (UNWIND_INFO *)buf;
2892 void (*func)(void) = code_mem;
2893 DWORD oldaccess, oldaccess2;
2895 runtime_func.BeginAddress = 0;
2896 runtime_func.EndAddress = code_size;
2897 runtime_func.UnwindData = 0x1000;
2899 unwind->Version = 1;
2900 unwind->Flags = UNW_FLAG_EHANDLER;
2901 unwind->SizeOfProlog = 0;
2902 unwind->CountOfCodes = 0;
2903 unwind->FrameRegister = 0;
2904 unwind->FrameOffset = 0;
2905 *(ULONG *)&buf[4] = 0x1010;
2906 *(const void **)&buf[8] = context;
2908 /* jmp near */
2909 buf[16] = 0xff;
2910 buf[17] = 0x25;
2911 *(ULONG *)&buf[18] = 0;
2912 *(void **)&buf[22] = handler;
2914 memcpy((unsigned char *)code_mem + 0x1000, buf, sizeof(buf));
2915 memcpy(code_mem, code, code_size);
2916 if(access)
2917 VirtualProtect(code_mem, code_size, access, &oldaccess);
2919 pRtlAddFunctionTable(&runtime_func, 1, (ULONG_PTR)code_mem);
2920 func();
2921 pRtlDeleteFunctionTable(&runtime_func);
2923 if(access)
2924 VirtualProtect(code_mem, code_size, oldaccess, &oldaccess2);
2927 static DWORD WINAPI handler( EXCEPTION_RECORD *rec, ULONG64 frame,
2928 CONTEXT *context, DISPATCHER_CONTEXT *dispatcher )
2930 const struct exception *except = *(const struct exception **)(dispatcher->HandlerData);
2931 unsigned int i, parameter_count, entry = except - exceptions;
2933 got_exception++;
2934 trace( "exception %u: %x flags:%x addr:%p\n",
2935 entry, rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
2937 ok( rec->ExceptionCode == except->status ||
2938 (except->alt_status != 0 && rec->ExceptionCode == except->alt_status),
2939 "%u: Wrong exception code %x/%x\n", entry, rec->ExceptionCode, except->status );
2940 ok( context->Rip == (DWORD_PTR)code_mem + except->offset,
2941 "%u: Unexpected eip %#lx/%#lx\n", entry,
2942 context->Rip, (DWORD_PTR)code_mem + except->offset );
2943 ok( rec->ExceptionAddress == (char*)context->Rip ||
2944 (rec->ExceptionCode == STATUS_BREAKPOINT && rec->ExceptionAddress == (char*)context->Rip + 1),
2945 "%u: Unexpected exception address %p/%p\n", entry,
2946 rec->ExceptionAddress, (char*)context->Rip );
2948 if (except->status == STATUS_BREAKPOINT && is_wow64)
2949 parameter_count = 1;
2950 else if (except->alt_status == 0 || rec->ExceptionCode != except->alt_status)
2951 parameter_count = except->nb_params;
2952 else
2953 parameter_count = except->alt_nb_params;
2955 ok( rec->NumberParameters == parameter_count,
2956 "%u: Unexpected parameter count %u/%u\n", entry, rec->NumberParameters, parameter_count );
2958 /* Most CPUs (except Intel Core apparently) report a segment limit violation */
2959 /* instead of page faults for accesses beyond 0xffffffffffffffff */
2960 if (except->nb_params == 2 && except->params[1] >= 0xfffffffffffffffd)
2962 if (rec->ExceptionInformation[0] == 0 && rec->ExceptionInformation[1] == 0xffffffffffffffff)
2963 goto skip_params;
2966 /* Seems that both 0xbee8 and 0xfffffffffffffffff can be returned in windows */
2967 if (except->nb_params == 2 && rec->NumberParameters == 2
2968 && except->params[1] == 0xbee8 && rec->ExceptionInformation[1] == 0xffffffffffffffff
2969 && except->params[0] == rec->ExceptionInformation[0])
2971 goto skip_params;
2974 if (except->alt_status == 0 || rec->ExceptionCode != except->alt_status)
2976 for (i = 0; i < rec->NumberParameters; i++)
2977 ok( rec->ExceptionInformation[i] == except->params[i],
2978 "%u: Wrong parameter %d: %lx/%lx\n",
2979 entry, i, rec->ExceptionInformation[i], except->params[i] );
2981 else
2983 for (i = 0; i < rec->NumberParameters; i++)
2984 ok( rec->ExceptionInformation[i] == except->alt_params[i],
2985 "%u: Wrong parameter %d: %lx/%lx\n",
2986 entry, i, rec->ExceptionInformation[i], except->alt_params[i] );
2989 skip_params:
2990 /* don't handle exception if it's not the address we expected */
2991 if (context->Rip != (DWORD_PTR)code_mem + except->offset) return ExceptionContinueSearch;
2993 context->Rip += except->length;
2994 return ExceptionContinueExecution;
2997 static void test_prot_fault(void)
2999 unsigned int i;
3001 for (i = 0; i < ARRAY_SIZE(exceptions); i++)
3003 got_exception = 0;
3004 run_exception_test(handler, &exceptions[i], &exceptions[i].code,
3005 sizeof(exceptions[i].code), 0);
3006 ok( got_exception == (exceptions[i].status != 0),
3007 "%u: bad exception count %d\n", i, got_exception );
3011 static LONG CALLBACK dpe_handler(EXCEPTION_POINTERS *info)
3013 EXCEPTION_RECORD *rec = info->ExceptionRecord;
3014 DWORD old_prot;
3016 trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress);
3018 got_exception++;
3020 ok(rec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION,
3021 "got %#x\n", rec->ExceptionCode);
3022 ok(rec->NumberParameters == 2, "got %u params\n", rec->NumberParameters);
3023 ok(rec->ExceptionInformation[0] == EXCEPTION_EXECUTE_FAULT,
3024 "got %#lx\n", rec->ExceptionInformation[0]);
3025 ok((void *)rec->ExceptionInformation[1] == code_mem,
3026 "got %p\n", (void *)rec->ExceptionInformation[1]);
3028 VirtualProtect(code_mem, 1, PAGE_EXECUTE_READWRITE, &old_prot);
3030 return EXCEPTION_CONTINUE_EXECUTION;
3033 static void test_dpe_exceptions(void)
3035 static const BYTE ret[] = {0xc3};
3036 DWORD (CDECL *func)(void) = code_mem;
3037 DWORD old_prot, val = 0, len = 0xdeadbeef;
3038 NTSTATUS status;
3039 void *handler;
3041 status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val, &len );
3042 ok( status == STATUS_SUCCESS || status == STATUS_INVALID_PARAMETER, "got status %08x\n", status );
3043 if (!status)
3045 ok( len == sizeof(val), "wrong len %u\n", len );
3046 ok( val == (MEM_EXECUTE_OPTION_DISABLE | MEM_EXECUTE_OPTION_PERMANENT |
3047 MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION),
3048 "wrong val %08x\n", val );
3050 else ok( len == 0xdeadbeef, "wrong len %u\n", len );
3052 val = MEM_EXECUTE_OPTION_DISABLE;
3053 status = pNtSetInformationProcess( GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val );
3054 ok( status == STATUS_INVALID_PARAMETER, "got status %08x\n", status );
3056 memcpy(code_mem, ret, sizeof(ret));
3058 handler = pRtlAddVectoredExceptionHandler(TRUE, &dpe_handler);
3059 ok(!!handler, "RtlAddVectoredExceptionHandler failed\n");
3061 VirtualProtect(code_mem, 1, PAGE_NOACCESS, &old_prot);
3063 got_exception = 0;
3064 func();
3065 ok(got_exception == 1, "got %u exceptions\n", got_exception);
3067 VirtualProtect(code_mem, 1, old_prot, &old_prot);
3069 VirtualProtect(code_mem, 1, PAGE_READWRITE, &old_prot);
3071 got_exception = 0;
3072 func();
3073 ok(got_exception == 1, "got %u exceptions\n", got_exception);
3075 VirtualProtect(code_mem, 1, old_prot, &old_prot);
3077 pRtlRemoveVectoredExceptionHandler(handler);
3080 static const BYTE call_one_arg_code[] = {
3081 0x55, /* push %rbp */
3082 0x48, 0x89, 0xe5, /* mov %rsp,%rbp */
3083 0x48, 0x83, 0xec, 0x20, /* sub $0x20,%rsp */
3084 0x48, 0x89, 0xc8, /* mov %rcx,%rax */
3085 0x48, 0x89, 0xd1, /* mov %rdx,%rcx */
3086 0xff, 0xd0, /* callq *%rax */
3087 0x90, /* nop */
3088 0x90, /* nop */
3089 0x90, /* nop */
3090 0x90, /* nop */
3091 0x48, 0x83, 0xc4, 0x20, /* add $0x20,%rsp */
3092 0x5d, /* pop %rbp */
3093 0xc3, /* retq */
3096 static int rtlraiseexception_unhandled_handler_called;
3097 static int rtlraiseexception_handler_called;
3099 static void rtlraiseexception_handler_( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
3100 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher,
3101 BOOL unhandled_handler )
3103 if (unhandled_handler) rtlraiseexception_unhandled_handler_called = 1;
3104 else rtlraiseexception_handler_called = 1;
3106 trace( "exception: %08x flags:%x addr:%p context: Rip:%p\n",
3107 rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, (void *)context->Rip );
3109 ok( rec->ExceptionAddress == (char *)code_mem + 0x10
3110 || broken( rec->ExceptionAddress == code_mem || !rec->ExceptionAddress ) /* 2008 */,
3111 "ExceptionAddress at %p instead of %p\n", rec->ExceptionAddress, (char *)code_mem + 0x10 );
3113 ok( context->ContextFlags == CONTEXT_ALL || context->ContextFlags == (CONTEXT_ALL | CONTEXT_XSTATE)
3114 || context->ContextFlags == (CONTEXT_FULL | CONTEXT_SEGMENTS)
3115 || context->ContextFlags == (CONTEXT_FULL | CONTEXT_SEGMENTS | CONTEXT_XSTATE),
3116 "wrong context flags %x\n", context->ContextFlags );
3118 /* check that pc is fixed up only for EXCEPTION_BREAKPOINT
3119 * even if raised by RtlRaiseException
3121 if (rec->ExceptionCode == EXCEPTION_BREAKPOINT && test_stage)
3122 ok( context->Rip == (UINT_PTR)code_mem + 0xf,
3123 "%d: Rip at %Ix instead of %Ix\n", test_stage, context->Rip, (UINT_PTR)code_mem + 0xf );
3124 else
3125 ok( context->Rip == (UINT_PTR)code_mem + 0x10,
3126 "%d: Rip at %Ix instead of %Ix\n", test_stage, context->Rip, (UINT_PTR)code_mem + 0x10 );
3128 if (have_vectored_api) ok( context->Rax == 0xf00f00f0, "context->Rax is %Ix, should have been set to 0xf00f00f0 in vectored handler\n", context->Rax );
3130 /* give the debugger a chance to examine the state a second time */
3131 /* without the exception handler changing pc */
3132 if (test_stage == 2)
3133 return;
3135 /* pc in context is decreased by 1
3136 * Increase it again, else execution will continue in the middle of an instruction */
3137 if (rec->ExceptionCode == EXCEPTION_BREAKPOINT && (context->Rip == (UINT_PTR)code_mem + 0xf))
3138 context->Rip++;
3141 static LONG CALLBACK rtlraiseexception_unhandled_handler(EXCEPTION_POINTERS *ExceptionInfo)
3143 PCONTEXT context = ExceptionInfo->ContextRecord;
3144 PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
3145 rtlraiseexception_handler_(rec, NULL, context, NULL, TRUE);
3146 if (test_stage == 2) return EXCEPTION_CONTINUE_SEARCH;
3147 return EXCEPTION_CONTINUE_EXECUTION;
3150 static DWORD rtlraiseexception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
3151 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
3153 rtlraiseexception_handler_(rec, frame, context, dispatcher, FALSE);
3154 if (test_stage == 2) return ExceptionContinueSearch;
3155 return ExceptionContinueExecution;
3158 static LONG CALLBACK rtlraiseexception_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
3160 PCONTEXT context = ExceptionInfo->ContextRecord;
3161 PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
3163 trace( "vect. handler %08x addr:%p Rip:%p\n", rec->ExceptionCode, rec->ExceptionAddress, (void *)context->Rip );
3165 ok( rec->ExceptionAddress == (char *)code_mem + 0x10
3166 || broken(rec->ExceptionAddress == code_mem || !rec->ExceptionAddress ) /* 2008 */,
3167 "ExceptionAddress at %p instead of %p\n", rec->ExceptionAddress, (char *)code_mem + 0x10 );
3169 /* check that Rip is fixed up only for EXCEPTION_BREAKPOINT
3170 * even if raised by RtlRaiseException
3172 if (rec->ExceptionCode == EXCEPTION_BREAKPOINT && test_stage)
3173 ok( context->Rip == (UINT_PTR)code_mem + 0xf,
3174 "%d: Rip at %Ix instead of %Ix\n", test_stage, context->Rip, (UINT_PTR)code_mem + 0xf );
3175 else
3176 ok( context->Rip == (UINT_PTR)code_mem + 0x10,
3177 "%d: Rip at %Ix instead of %Ix\n", test_stage, context->Rip, (UINT_PTR)code_mem + 0x10 );
3179 /* test if context change is preserved from vectored handler to stack handlers */
3180 context->Rax = 0xf00f00f0;
3182 return EXCEPTION_CONTINUE_SEARCH;
3185 static void run_rtlraiseexception_test(DWORD exceptioncode)
3187 EXCEPTION_REGISTRATION_RECORD frame;
3188 EXCEPTION_RECORD record;
3189 PVOID vectored_handler = NULL;
3191 void (CDECL *func)(void* function, EXCEPTION_RECORD* record) = code_mem;
3193 record.ExceptionCode = exceptioncode;
3194 record.ExceptionFlags = 0;
3195 record.ExceptionRecord = NULL;
3196 record.ExceptionAddress = NULL; /* does not matter, copied return address */
3197 record.NumberParameters = 0;
3199 frame.Handler = rtlraiseexception_handler;
3200 frame.Prev = NtCurrentTeb()->Tib.ExceptionList;
3202 memcpy(code_mem, call_one_arg_code, sizeof(call_one_arg_code));
3204 NtCurrentTeb()->Tib.ExceptionList = &frame;
3205 if (have_vectored_api)
3207 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &rtlraiseexception_vectored_handler);
3208 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
3210 if (pRtlSetUnhandledExceptionFilter) pRtlSetUnhandledExceptionFilter(&rtlraiseexception_unhandled_handler);
3212 rtlraiseexception_handler_called = 0;
3213 rtlraiseexception_unhandled_handler_called = 0;
3214 func(pRtlRaiseException, &record);
3215 ok( record.ExceptionAddress == (char *)code_mem + 0x10,
3216 "address set to %p instead of %p\n", record.ExceptionAddress, (char *)code_mem + 0x10 );
3218 todo_wine
3219 ok( !rtlraiseexception_handler_called, "Frame handler called\n" );
3220 todo_wine
3221 ok( rtlraiseexception_unhandled_handler_called, "UnhandledExceptionFilter wasn't called\n" );
3223 if (have_vectored_api)
3224 pRtlRemoveVectoredExceptionHandler(vectored_handler);
3226 if (pRtlSetUnhandledExceptionFilter) pRtlSetUnhandledExceptionFilter(NULL);
3227 NtCurrentTeb()->Tib.ExceptionList = frame.Prev;
3230 static void test_rtlraiseexception(void)
3232 if (!pRtlRaiseException)
3234 skip("RtlRaiseException not found\n");
3235 return;
3238 /* test without debugger */
3239 run_rtlraiseexception_test(0x12345);
3240 run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
3241 run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
3244 static void test_debugger(void)
3246 char cmdline[MAX_PATH];
3247 PROCESS_INFORMATION pi;
3248 STARTUPINFOA si = { 0 };
3249 DEBUG_EVENT de;
3250 DWORD continuestatus;
3251 PVOID code_mem_address = NULL;
3252 NTSTATUS status;
3253 SIZE_T size_read;
3254 BOOL ret;
3255 int counter = 0;
3256 si.cb = sizeof(si);
3258 if(!pNtGetContextThread || !pNtSetContextThread || !pNtReadVirtualMemory || !pNtTerminateProcess)
3260 skip("NtGetContextThread, NtSetContextThread, NtReadVirtualMemory or NtTerminateProcess not found\n");
3261 return;
3264 sprintf(cmdline, "%s %s %s %p", my_argv[0], my_argv[1], "debuggee", &test_stage);
3265 ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi);
3266 ok(ret, "could not create child process error: %u\n", GetLastError());
3267 if (!ret)
3268 return;
3272 continuestatus = DBG_CONTINUE;
3273 ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n");
3275 ret = ContinueDebugEvent(de.dwProcessId, de.dwThreadId, 0xdeadbeef);
3276 ok(!ret, "ContinueDebugEvent unexpectedly succeeded\n");
3277 ok(GetLastError() == ERROR_INVALID_PARAMETER, "Unexpected last error: %u\n", GetLastError());
3279 if (de.dwThreadId != pi.dwThreadId)
3281 trace("event %d not coming from main thread, ignoring\n", de.dwDebugEventCode);
3282 ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE);
3283 continue;
3286 if (de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT)
3288 if(de.u.CreateProcessInfo.lpBaseOfImage != NtCurrentTeb()->Peb->ImageBaseAddress)
3290 skip("child process loaded at different address, terminating it\n");
3291 pNtTerminateProcess(pi.hProcess, 0);
3294 else if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
3296 CONTEXT ctx;
3297 int stage;
3299 counter++;
3300 status = pNtReadVirtualMemory(pi.hProcess, &code_mem, &code_mem_address,
3301 sizeof(code_mem_address), &size_read);
3302 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
3303 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
3304 sizeof(stage), &size_read);
3305 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
3307 ctx.ContextFlags = CONTEXT_FULL;
3308 status = pNtGetContextThread(pi.hThread, &ctx);
3309 ok(!status, "NtGetContextThread failed with 0x%x\n", status);
3311 trace("exception 0x%x at %p firstchance=%d Rip=%p, Rax=%p\n",
3312 de.u.Exception.ExceptionRecord.ExceptionCode,
3313 de.u.Exception.ExceptionRecord.ExceptionAddress,
3314 de.u.Exception.dwFirstChance, (char *)ctx.Rip, (char *)ctx.Rax);
3316 if (counter > 100)
3318 ok(FALSE, "got way too many exceptions, probably caught in an infinite loop, terminating child\n");
3319 pNtTerminateProcess(pi.hProcess, 1);
3321 else if (counter < 2) /* startup breakpoint */
3323 /* breakpoint is inside ntdll */
3324 void *ntdll = GetModuleHandleA( "ntdll.dll" );
3325 IMAGE_NT_HEADERS *nt = RtlImageNtHeader( ntdll );
3327 ok( (char *)ctx.Rip >= (char *)ntdll &&
3328 (char *)ctx.Rip < (char *)ntdll + nt->OptionalHeader.SizeOfImage,
3329 "wrong rip %p ntdll %p-%p\n", (void *)ctx.Rip, ntdll,
3330 (char *)ntdll + nt->OptionalHeader.SizeOfImage );
3332 else
3334 if (stage == 1)
3336 ok((char *)ctx.Rip == (char *)code_mem_address + 0x10, "Rip at %p instead of %p\n",
3337 (char *)ctx.Rip, (char *)code_mem_address + 0x10);
3338 /* setting the context from debugger does not affect the context that the
3339 * exception handler gets, except on w2008 */
3340 ctx.Rip = (UINT_PTR)code_mem_address + 0x12;
3341 ctx.Rax = 0xf00f00f1;
3342 /* let the debuggee handle the exception */
3343 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
3345 else if (stage == 2)
3347 if (de.u.Exception.dwFirstChance)
3349 ok((char *)ctx.Rip == (char *)code_mem_address + 0x10, "Rip at %p instead of %p\n",
3350 (char *)ctx.Rip, (char *)code_mem_address + 0x10);
3351 /* setting the context from debugger does not affect the context that the
3352 * exception handler gets, except on w2008 */
3353 ctx.Rip = (UINT_PTR)code_mem_address + 0x12;
3354 ctx.Rax = 0xf00f00f1;
3355 /* pass exception to debuggee
3356 * exception will not be handled and a second chance exception will be raised */
3357 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
3359 else
3361 /* debugger gets context after exception handler has played with it */
3362 if (de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT)
3364 ok((char *)ctx.Rip == (char *)code_mem_address + 0xf, "Rip at %p instead of %p\n",
3365 (char *)ctx.Rip, (char *)code_mem_address + 0xf);
3366 ctx.Rip += 1;
3368 else ok((char *)ctx.Rip == (char *)code_mem_address + 0x10, "Rip at 0x%x instead of %p\n",
3369 ctx.Rip, (char *)code_mem_address + 0x10);
3370 /* here we handle exception */
3373 else if (stage == 7 || stage == 8)
3375 ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT,
3376 "expected EXCEPTION_BREAKPOINT, got %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode);
3377 ok((char *)ctx.Rip == (char *)code_mem_address + 0x30,
3378 "expected Rip = %p, got %p\n", (char *)code_mem_address + 0x30, (char *)ctx.Rip);
3379 if (stage == 8) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
3381 else if (stage == 9 || stage == 10)
3383 ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT,
3384 "expected EXCEPTION_BREAKPOINT, got %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode);
3385 ok((char *)ctx.Rip == (char *)code_mem_address + 2,
3386 "expected Rip = %p, got %p\n", (char *)code_mem_address + 2, (char *)ctx.Rip);
3387 if (stage == 10) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
3389 else if (stage == 11 || stage == 12 || stage == 13)
3391 ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_INVALID_HANDLE,
3392 "unexpected exception code %08x, expected %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode,
3393 EXCEPTION_INVALID_HANDLE);
3394 ok(de.u.Exception.ExceptionRecord.NumberParameters == 0,
3395 "unexpected number of parameters %d, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters);
3397 if (stage == 12|| stage == 13) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
3399 else if (stage == 14 || stage == 15)
3401 test_debugger_xstate(pi.hThread, &ctx, stage);
3403 else
3404 ok(FALSE, "unexpected stage %x\n", stage);
3406 status = pNtSetContextThread(pi.hThread, &ctx);
3407 ok(!status, "NtSetContextThread failed with 0x%x\n", status);
3410 else if (de.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT)
3412 int stage;
3413 char buffer[64];
3415 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
3416 sizeof(stage), &size_read);
3417 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
3419 ok(!de.u.DebugString.fUnicode, "unexpected unicode debug string event\n");
3420 ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) - 1, "buffer not large enough to hold %d bytes\n",
3421 de.u.DebugString.nDebugStringLength);
3423 memset(buffer, 0, sizeof(buffer));
3424 status = pNtReadVirtualMemory(pi.hProcess, de.u.DebugString.lpDebugStringData, buffer,
3425 de.u.DebugString.nDebugStringLength, &size_read);
3426 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
3428 if (stage == 3 || stage == 4)
3429 ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer);
3430 else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */
3431 ok(strstr(buffer, "SHIMVIEW") != NULL, "unexpected stage %x, got debug string event '%s'\n", stage, buffer);
3433 if (stage == 4) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
3435 else if (de.dwDebugEventCode == RIP_EVENT)
3437 int stage;
3439 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
3440 sizeof(stage), &size_read);
3441 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
3443 if (stage == 5 || stage == 6)
3445 ok(de.u.RipInfo.dwError == 0x11223344, "got unexpected rip error code %08x, expected %08x\n",
3446 de.u.RipInfo.dwError, 0x11223344);
3447 ok(de.u.RipInfo.dwType == 0x55667788, "got unexpected rip type %08x, expected %08x\n",
3448 de.u.RipInfo.dwType, 0x55667788);
3450 else
3451 ok(FALSE, "unexpected stage %x\n", stage);
3453 if (stage == 6) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
3456 ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus);
3458 } while (de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT);
3460 wait_child_process( pi.hProcess );
3461 ret = CloseHandle(pi.hThread);
3462 ok(ret, "error %u\n", GetLastError());
3463 ret = CloseHandle(pi.hProcess);
3464 ok(ret, "error %u\n", GetLastError());
3467 static void test_thread_context(void)
3469 CONTEXT context;
3470 NTSTATUS status;
3471 int i;
3472 struct expected
3474 ULONG64 Rax, Rbx, Rcx, Rdx, Rsi, Rdi, R8, R9, R10, R11,
3475 R12, R13, R14, R15, Rbp, Rsp, Rip, prev_frame, EFlags;
3476 ULONG MxCsr;
3477 XMM_SAVE_AREA32 FltSave;
3478 WORD SegCs, SegDs, SegEs, SegFs, SegGs, SegSs;
3479 } expect;
3480 XMM_SAVE_AREA32 broken_fltsave;
3481 NTSTATUS (*func_ptr)( void *arg1, void *arg2, struct expected *res, void *func ) = (void *)code_mem;
3483 static const BYTE call_func[] =
3485 0x55, /* push %rbp */
3486 0x48, 0x89, 0xe5, /* mov %rsp,%rbp */
3487 0x48, 0x8d, 0x64, 0x24, 0xd0, /* lea -0x30(%rsp),%rsp */
3488 0x49, 0x89, 0x00, /* mov %rax,(%r8) */
3489 0x49, 0x89, 0x58, 0x08, /* mov %rbx,0x8(%r8) */
3490 0x49, 0x89, 0x48, 0x10, /* mov %rcx,0x10(%r8) */
3491 0x49, 0x89, 0x50, 0x18, /* mov %rdx,0x18(%r8) */
3492 0x49, 0x89, 0x70, 0x20, /* mov %rsi,0x20(%r8) */
3493 0x49, 0x89, 0x78, 0x28, /* mov %rdi,0x28(%r8) */
3494 0x4d, 0x89, 0x40, 0x30, /* mov %r8,0x30(%r8) */
3495 0x4d, 0x89, 0x48, 0x38, /* mov %r9,0x38(%r8) */
3496 0x4d, 0x89, 0x50, 0x40, /* mov %r10,0x40(%r8) */
3497 0x4d, 0x89, 0x58, 0x48, /* mov %r11,0x48(%r8) */
3498 0x4d, 0x89, 0x60, 0x50, /* mov %r12,0x50(%r8) */
3499 0x4d, 0x89, 0x68, 0x58, /* mov %r13,0x58(%r8) */
3500 0x4d, 0x89, 0x70, 0x60, /* mov %r14,0x60(%r8) */
3501 0x4d, 0x89, 0x78, 0x68, /* mov %r15,0x68(%r8) */
3502 0x49, 0x89, 0x68, 0x70, /* mov %rbp,0x70(%r8) */
3503 0x49, 0x89, 0x60, 0x78, /* mov %rsp,0x78(%r8) */
3504 0xff, 0x75, 0x08, /* pushq 0x8(%rbp) */
3505 0x41, 0x8f, 0x80, 0x80, 0x00, 0x00, 0x00, /* popq 0x80(%r8) */
3506 0xff, 0x75, 0x00, /* pushq 0x0(%rbp) */
3507 0x41, 0x8f, 0x80, 0x88, 0x00, 0x00, 0x00, /* popq 0x88(%r8) */
3508 0x9c, /* pushfq */
3509 0x41, 0x8f, 0x80, 0x90, 0x00, 0x00, 0x00, /* popq 0x90(%r8) */
3510 0x41, 0x0f, 0xae, 0x98, 0x98, 0x00, 0x00, 0x00, /* stmxcsr 0x98(%r8) */
3511 0x41, 0x0f, 0xae, 0x80, 0xa0, 0x00, 0x00, 0x00, /* fxsave 0xa0(%r8) */
3512 0x66, 0x41, 0x0f, 0x7f, 0x80, 0x40, 0x01, 0x00, 0x00, /* movdqa %xmm0,0x140(%r8) */
3513 0x66, 0x41, 0x0f, 0x7f, 0x88, 0x50, 0x01, 0x00, 0x00, /* movdqa %xmm1,0x150(%r8) */
3514 0x66, 0x41, 0x0f, 0x7f, 0x90, 0x60, 0x01, 0x00, 0x00, /* movdqa %xmm2,0x160(%r8) */
3515 0x66, 0x41, 0x0f, 0x7f, 0x98, 0x70, 0x01, 0x00, 0x00, /* movdqa %xmm3,0x170(%r8) */
3516 0x66, 0x41, 0x0f, 0x7f, 0xa0, 0x80, 0x01, 0x00, 0x00, /* movdqa %xmm4,0x180(%r8) */
3517 0x66, 0x41, 0x0f, 0x7f, 0xa8, 0x90, 0x01, 0x00, 0x00, /* movdqa %xmm5,0x190(%r8) */
3518 0x66, 0x41, 0x0f, 0x7f, 0xb0, 0xa0, 0x01, 0x00, 0x00, /* movdqa %xmm6,0x1a0(%r8) */
3519 0x66, 0x41, 0x0f, 0x7f, 0xb8, 0xb0, 0x01, 0x00, 0x00, /* movdqa %xmm7,0x1b0(%r8) */
3520 0x66, 0x45, 0x0f, 0x7f, 0x80, 0xc0, 0x01, 0x00, 0x00, /* movdqa %xmm8,0x1c0(%r8) */
3521 0x66, 0x45, 0x0f, 0x7f, 0x88, 0xd0, 0x01, 0x00, 0x00, /* movdqa %xmm9,0x1d0(%r8) */
3522 0x66, 0x45, 0x0f, 0x7f, 0x90, 0xe0, 0x01, 0x00, 0x00, /* movdqa %xmm10,0x1e0(%r8) */
3523 0x66, 0x45, 0x0f, 0x7f, 0x98, 0xf0, 0x01, 0x00, 0x00, /* movdqa %xmm11,0x1f0(%r8) */
3524 0x66, 0x45, 0x0f, 0x7f, 0xa0, 0x00, 0x02, 0x00, 0x00, /* movdqa %xmm12,0x200(%r8) */
3525 0x66, 0x45, 0x0f, 0x7f, 0xa8, 0x10, 0x02, 0x00, 0x00, /* movdqa %xmm13,0x210(%r8) */
3526 0x66, 0x45, 0x0f, 0x7f, 0xb0, 0x20, 0x02, 0x00, 0x00, /* movdqa %xmm14,0x220(%r8) */
3527 0x66, 0x45, 0x0f, 0x7f, 0xb8, 0x30, 0x02, 0x00, 0x00, /* movdqa %xmm15,0x230(%r8) */
3528 0x41, 0x8c, 0x88, 0xa0, 0x02, 0x00, 0x00, /* mov %cs,0x2a0(%r8) */
3529 0x41, 0x8c, 0x98, 0xa2, 0x02, 0x00, 0x00, /* mov %ds,0x2a2(%r8) */
3530 0x41, 0x8c, 0x80, 0xa4, 0x02, 0x00, 0x00, /* mov %es,0x2a4(%r8) */
3531 0x41, 0x8c, 0xa0, 0xa6, 0x02, 0x00, 0x00, /* mov %fs,0x2a6(%r8) */
3532 0x41, 0x8c, 0xa8, 0xa8, 0x02, 0x00, 0x00, /* mov %gs,0x2a8(%r8) */
3533 0x41, 0x8c, 0x90, 0xaa, 0x02, 0x00, 0x00, /* mov %ss,0x2aa(%r8) */
3534 0x41, 0xff, 0xd1, /* callq *%r9 */
3535 0xc9, /* leaveq */
3536 0xc3, /* retq */
3539 memcpy( func_ptr, call_func, sizeof(call_func) );
3541 #define COMPARE(reg) \
3542 ok( context.reg == expect.reg, "wrong " #reg " %p/%p\n", (void *)(ULONG64)context.reg, (void *)(ULONG64)expect.reg )
3544 memset( &context, 0xcc, sizeof(context) );
3545 memset( &expect, 0xcc, sizeof(expect) );
3546 func_ptr( &context, 0, &expect, pRtlCaptureContext );
3547 trace( "expect: rax=%p rbx=%p rcx=%p rdx=%p rsi=%p rdi=%p "
3548 "r8=%p r9=%p r10=%p r11=%p r12=%p r13=%p r14=%p r15=%p "
3549 "rbp=%p rsp=%p rip=%p cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x prev=%08x\n",
3550 (void *)expect.Rax, (void *)expect.Rbx, (void *)expect.Rcx, (void *)expect.Rdx,
3551 (void *)expect.Rsi, (void *)expect.Rdi, (void *)expect.R8, (void *)expect.R9,
3552 (void *)expect.R10, (void *)expect.R11, (void *)expect.R12, (void *)expect.R13,
3553 (void *)expect.R14, (void *)expect.R15, (void *)expect.Rbp, (void *)expect.Rsp,
3554 (void *)expect.Rip, expect.SegCs, expect.SegDs, expect.SegEs,
3555 expect.SegFs, expect.SegGs, expect.SegSs, expect.EFlags, expect.prev_frame );
3556 trace( "actual: rax=%p rbx=%p rcx=%p rdx=%p rsi=%p rdi=%p "
3557 "r8=%p r9=%p r10=%p r11=%p r12=%p r13=%p r14=%p r15=%p "
3558 "rbp=%p rsp=%p rip=%p cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x prev=%08x\n",
3559 (void *)context.Rax, (void *)context.Rbx, (void *)context.Rcx, (void *)context.Rdx,
3560 (void *)context.Rsi, (void *)context.Rdi, (void *)context.R8, (void *)context.R9,
3561 (void *)context.R10, (void *)context.R11, (void *)context.R12, (void *)context.R13,
3562 (void *)context.R14, (void *)context.R15, (void *)context.Rbp, (void *)context.Rsp,
3563 (void *)context.Rip, context.SegCs, context.SegDs, context.SegEs,
3564 context.SegFs, context.SegGs, context.SegSs, context.EFlags );
3566 ok( context.ContextFlags == (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT),
3567 "wrong flags %08x\n", context.ContextFlags );
3568 COMPARE( Rax );
3569 COMPARE( Rbx );
3570 COMPARE( Rcx );
3571 COMPARE( Rdx );
3572 COMPARE( Rsi );
3573 COMPARE( Rdi );
3574 COMPARE( R8 );
3575 COMPARE( R9 );
3576 COMPARE( R10 );
3577 COMPARE( R11 );
3578 COMPARE( R12 );
3579 COMPARE( R13 );
3580 COMPARE( R14 );
3581 COMPARE( R15 );
3582 COMPARE( Rbp );
3583 COMPARE( Rsp );
3584 COMPARE( EFlags );
3585 COMPARE( MxCsr );
3586 COMPARE( SegCs );
3587 COMPARE( SegDs );
3588 COMPARE( SegEs );
3589 COMPARE( SegFs );
3590 COMPARE( SegGs );
3591 COMPARE( SegSs );
3592 ok( !memcmp( &context.FltSave, &expect.FltSave, offsetof( XMM_SAVE_AREA32, XmmRegisters )),
3593 "wrong FltSave\n" );
3594 for (i = 0; i < 16; i++)
3595 ok( !memcmp( &context.Xmm0 + i, &expect.FltSave.XmmRegisters[i], sizeof(context.Xmm0) ),
3596 "wrong xmm%u\n", i );
3597 /* Rip is return address from RtlCaptureContext */
3598 ok( context.Rip == (ULONG64)func_ptr + sizeof(call_func) - 2,
3599 "wrong Rip %p/%p\n", (void *)context.Rip, (char *)func_ptr + sizeof(call_func) - 2 );
3601 memset( &context, 0xcc, sizeof(context) );
3602 memset( &expect, 0xcc, sizeof(expect) );
3603 context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT;
3605 status = func_ptr( GetCurrentThread(), &context, &expect, pNtGetContextThread );
3606 ok( status == STATUS_SUCCESS, "NtGetContextThread failed %08x\n", status );
3607 trace( "expect: rax=%p rbx=%p rcx=%p rdx=%p rsi=%p rdi=%p "
3608 "r8=%p r9=%p r10=%p r11=%p r12=%p r13=%p r14=%p r15=%p "
3609 "rbp=%p rsp=%p rip=%p cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x prev=%08x\n",
3610 (void *)expect.Rax, (void *)expect.Rbx, (void *)expect.Rcx, (void *)expect.Rdx,
3611 (void *)expect.Rsi, (void *)expect.Rdi, (void *)expect.R8, (void *)expect.R9,
3612 (void *)expect.R10, (void *)expect.R11, (void *)expect.R12, (void *)expect.R13,
3613 (void *)expect.R14, (void *)expect.R15, (void *)expect.Rbp, (void *)expect.Rsp,
3614 (void *)expect.Rip, expect.SegCs, expect.SegDs, expect.SegEs,
3615 expect.SegFs, expect.SegGs, expect.SegSs, expect.EFlags, expect.prev_frame );
3616 trace( "actual: rax=%p rbx=%p rcx=%p rdx=%p rsi=%p rdi=%p "
3617 "r8=%p r9=%p r10=%p r11=%p r12=%p r13=%p r14=%p r15=%p "
3618 "rbp=%p rsp=%p rip=%p cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x prev=%08x\n",
3619 (void *)context.Rax, (void *)context.Rbx, (void *)context.Rcx, (void *)context.Rdx,
3620 (void *)context.Rsi, (void *)context.Rdi, (void *)context.R8, (void *)context.R9,
3621 (void *)context.R10, (void *)context.R11, (void *)context.R12, (void *)context.R13,
3622 (void *)context.R14, (void *)context.R15, (void *)context.Rbp, (void *)context.Rsp,
3623 (void *)context.Rip, context.SegCs, context.SegDs, context.SegEs,
3624 context.SegFs, context.SegGs, context.SegSs, context.EFlags );
3625 /* other registers are not preserved */
3626 COMPARE( Rbx );
3627 COMPARE( Rsi );
3628 COMPARE( Rdi );
3629 COMPARE( R12 );
3630 COMPARE( R13 );
3631 COMPARE( R14 );
3632 COMPARE( R15 );
3633 COMPARE( Rbp );
3634 COMPARE( MxCsr );
3635 COMPARE( SegCs );
3636 COMPARE( SegDs );
3637 COMPARE( SegEs );
3638 COMPARE( SegFs );
3639 COMPARE( SegGs );
3640 COMPARE( SegSs );
3642 broken_fltsave = context.FltSave;
3643 memset( &broken_fltsave.ErrorOpcode, 0xcc, 0x12 );
3645 ok( !memcmp( &context.FltSave, &expect.FltSave, offsetof( XMM_SAVE_AREA32, XmmRegisters )) ||
3646 broken( !memcmp( &broken_fltsave, &expect.FltSave, offsetof( XMM_SAVE_AREA32, XmmRegisters )) ) /* w2008, w8 */,
3647 "wrong FltSave\n" );
3648 for (i = 6; i < 16; i++)
3649 ok( !memcmp( &context.Xmm0 + i, &expect.FltSave.XmmRegisters[i], sizeof(context.Xmm0) ),
3650 "wrong xmm%u\n", i );
3651 /* Rsp is the stack upon entry to NtGetContextThread */
3652 ok( context.Rsp == expect.Rsp - 8,
3653 "wrong Rsp %p/%p\n", (void *)context.Rsp, (void *)expect.Rsp );
3654 /* Rip is somewhere close to the NtGetContextThread implementation */
3655 ok( (char *)context.Rip >= (char *)pNtGetContextThread - 0x40000 &&
3656 (char *)context.Rip <= (char *)pNtGetContextThread + 0x40000,
3657 "wrong Rip %p/%p\n", (void *)context.Rip, (void *)pNtGetContextThread );
3658 #undef COMPARE
3661 static void test_wow64_context(void)
3663 char cmdline[] = "C:\\windows\\syswow64\\notepad.exe";
3664 PROCESS_INFORMATION pi;
3665 STARTUPINFOA si = {0};
3666 WOW64_CONTEXT ctx;
3667 NTSTATUS ret;
3669 memset(&ctx, 0x55, sizeof(ctx));
3670 ctx.ContextFlags = WOW64_CONTEXT_ALL;
3671 ret = pRtlWow64GetThreadContext( GetCurrentThread(), &ctx );
3672 ok(ret == STATUS_INVALID_PARAMETER || broken(ret == STATUS_PARTIAL_COPY), "got %#x\n", ret);
3674 CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
3676 ret = pRtlWow64GetThreadContext( pi.hThread, &ctx );
3677 ok(ret == STATUS_SUCCESS, "got %#x\n", ret);
3678 ok(ctx.ContextFlags == WOW64_CONTEXT_ALL, "got context flags %#x\n", ctx.ContextFlags);
3679 ok(!ctx.Ebp, "got ebp %08x\n", ctx.Ebp);
3680 ok(!ctx.Ecx, "got ecx %08x\n", ctx.Ecx);
3681 ok(!ctx.Edx, "got edx %08x\n", ctx.Edx);
3682 ok(!ctx.Esi, "got esi %08x\n", ctx.Esi);
3683 ok(!ctx.Edi, "got edi %08x\n", ctx.Edi);
3684 ok((ctx.EFlags & ~2) == 0x200, "got eflags %08x\n", ctx.EFlags);
3685 ok((WORD) ctx.FloatSave.ControlWord == 0x27f, "got control word %08x\n",
3686 ctx.FloatSave.ControlWord);
3687 ok(*(WORD *)ctx.ExtendedRegisters == 0x27f, "got SSE control word %04x\n",
3688 *(WORD *)ctx.ExtendedRegisters);
3690 ret = pRtlWow64SetThreadContext( pi.hThread, &ctx );
3691 ok(ret == STATUS_SUCCESS, "got %#x\n", ret);
3693 pNtTerminateProcess(pi.hProcess, 0);
3696 static BYTE saved_KiUserExceptionDispatcher_bytes[12];
3697 static void *pKiUserExceptionDispatcher;
3698 static BOOL hook_called;
3699 static void *hook_KiUserExceptionDispatcher_rip;
3700 static void *dbg_except_continue_handler_rip;
3701 static void *hook_exception_address;
3702 static struct
3704 ULONG64 old_rax;
3705 ULONG64 old_rdx;
3706 ULONG64 old_rsi;
3707 ULONG64 old_rdi;
3708 ULONG64 old_rbp;
3709 ULONG64 old_rsp;
3710 ULONG64 new_rax;
3711 ULONG64 new_rdx;
3712 ULONG64 new_rsi;
3713 ULONG64 new_rdi;
3714 ULONG64 new_rbp;
3715 ULONG64 new_rsp;
3717 test_kiuserexceptiondispatcher_regs;
3719 static DWORD dbg_except_continue_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
3720 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher)
3722 trace("handler context->Rip %#lx, codemem %p.\n", context->Rip, code_mem);
3723 got_exception = 1;
3724 dbg_except_continue_handler_rip = (void *)context->Rip;
3725 ++context->Rip;
3726 memcpy(pKiUserExceptionDispatcher, saved_KiUserExceptionDispatcher_bytes,
3727 sizeof(saved_KiUserExceptionDispatcher_bytes));
3729 RtlUnwind((void *)test_kiuserexceptiondispatcher_regs.old_rsp,
3730 (BYTE *)code_mem + 0x28, rec, (void *)0xdeadbeef);
3731 return ExceptionContinueExecution;
3734 static LONG WINAPI dbg_except_continue_vectored_handler(struct _EXCEPTION_POINTERS *e)
3736 EXCEPTION_RECORD *rec = e->ExceptionRecord;
3737 CONTEXT *context = e->ContextRecord;
3739 trace("dbg_except_continue_vectored_handler, code %#x, Rip %#lx.\n", rec->ExceptionCode, context->Rip);
3741 ok(rec->ExceptionCode == 0x80000003, "Got unexpected exception code %#x.\n", rec->ExceptionCode);
3743 got_exception = 1;
3744 dbg_except_continue_handler_rip = (void *)context->Rip;
3745 if (NtCurrentTeb()->Peb->BeingDebugged)
3746 ++context->Rip;
3748 if (context->Rip >= (ULONG64)code_mem && context->Rip < (ULONG64)code_mem + 0x100)
3749 RtlUnwind((void *)test_kiuserexceptiondispatcher_regs.old_rsp,
3750 (BYTE *)code_mem + 0x28, rec, (void *)0xdeadbeef);
3752 return EXCEPTION_CONTINUE_EXECUTION;
3755 void WINAPI hook_KiUserExceptionDispatcher(EXCEPTION_RECORD *rec, CONTEXT *context)
3757 trace("rec %p, context %p.\n", rec, context);
3758 trace("context->Rip %#lx, context->Rsp %#lx, ContextFlags %#lx.\n",
3759 context->Rip, context->Rsp, context->ContextFlags);
3761 hook_called = TRUE;
3762 /* Broken on Win2008, probably rec offset in stack is different. */
3763 ok(rec->ExceptionCode == 0x80000003 || broken(!rec->ExceptionCode),
3764 "Got unexpected ExceptionCode %#x.\n", rec->ExceptionCode);
3766 hook_KiUserExceptionDispatcher_rip = (void *)context->Rip;
3767 hook_exception_address = rec->ExceptionAddress;
3768 memcpy(pKiUserExceptionDispatcher, saved_KiUserExceptionDispatcher_bytes,
3769 sizeof(saved_KiUserExceptionDispatcher_bytes));
3772 static void test_kiuserexceptiondispatcher(void)
3774 LPVOID vectored_handler;
3775 HMODULE hntdll = GetModuleHandleA("ntdll.dll");
3776 static BYTE except_code[] =
3778 0x48, 0xb9, /* mov imm64, %rcx */
3779 /* offset: 0x2 */
3780 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3782 0x48, 0x89, 0x01, /* mov %rax, (%rcx) */
3783 0x48, 0x89, 0x51, 0x08, /* mov %rdx, 0x8(%rcx) */
3784 0x48, 0x89, 0x71, 0x10, /* mov %rsi, 0x10(%rcx) */
3785 0x48, 0x89, 0x79, 0x18, /* mov %rdi, 0x18(%rcx) */
3786 0x48, 0x89, 0x69, 0x20, /* mov %rbp, 0x20(%rcx) */
3787 0x48, 0x89, 0x61, 0x28, /* mov %rsp, 0x28(%rcx) */
3788 0x48, 0x83, 0xc1, 0x30, /* add $0x30, %rcx */
3790 /* offset: 0x25 */
3791 0xcc, /* int3 */
3793 0x0f, 0x0b, /* ud2, illegal instruction */
3795 /* offset: 0x28 */
3796 0x48, 0xb9, /* mov imm64, %rcx */
3797 /* offset: 0x2a */
3798 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3800 0x48, 0x89, 0x01, /* mov %rax, (%rcx) */
3801 0x48, 0x89, 0x51, 0x08, /* mov %rdx, 0x8(%rcx) */
3802 0x48, 0x89, 0x71, 0x10, /* mov %rsi, 0x10(%rcx) */
3803 0x48, 0x89, 0x79, 0x18, /* mov %rdi, 0x18(%rcx) */
3804 0x48, 0x89, 0x69, 0x20, /* mov %rbp, 0x20(%rcx) */
3805 0x48, 0x89, 0x61, 0x28, /* mov %rsp, 0x28(%rcx) */
3806 0xc3, /* ret */
3808 static BYTE hook_trampoline[] =
3810 0x48, 0x89, 0xe2, /* mov %rsp,%rdx */
3811 0x48, 0x8d, 0x8c, 0x24, 0xf0, 0x04, 0x00, 0x00,
3812 /* lea 0x4f0(%rsp),%rcx */
3814 0xff, 0x14, 0x25,
3815 /* offset: 14 bytes */
3816 0x00, 0x00, 0x00, 0x00, /* callq *addr */ /* call hook implementation. */
3817 0x48, 0x31, 0xc9, /* xor %rcx, %rcx */
3818 0x48, 0x31, 0xd2, /* xor %rdx, %rdx */
3820 0xff, 0x24, 0x25,
3821 /* offset: 27 bytes */
3822 0x00, 0x00, 0x00, 0x00, /* jmpq *addr */ /* jump to original function. */
3825 void *phook_KiUserExceptionDispatcher = hook_KiUserExceptionDispatcher;
3826 BYTE patched_KiUserExceptionDispatcher_bytes[12];
3827 DWORD old_protect1, old_protect2;
3828 EXCEPTION_RECORD record;
3829 void *bpt_address;
3830 BYTE *ptr;
3831 BOOL ret;
3833 pKiUserExceptionDispatcher = (void *)GetProcAddress(hntdll, "KiUserExceptionDispatcher");
3834 if (!pKiUserExceptionDispatcher)
3836 win_skip("KiUserExceptionDispatcher is not available.\n");
3837 return;
3840 *(ULONG64 *)(except_code + 2) = (ULONG64)&test_kiuserexceptiondispatcher_regs;
3841 *(ULONG64 *)(except_code + 0x2a) = (ULONG64)&test_kiuserexceptiondispatcher_regs.new_rax;
3843 ok(((ULONG64)&phook_KiUserExceptionDispatcher & 0xffffffff) == ((ULONG64)&phook_KiUserExceptionDispatcher),
3844 "Address is too long.\n");
3845 ok(((ULONG64)&pKiUserExceptionDispatcher & 0xffffffff) == ((ULONG64)&pKiUserExceptionDispatcher),
3846 "Address is too long.\n");
3848 *(unsigned int *)(hook_trampoline + 14) = (unsigned int)(ULONG_PTR)&phook_KiUserExceptionDispatcher;
3849 *(unsigned int *)(hook_trampoline + 27) = (unsigned int)(ULONG_PTR)&pKiUserExceptionDispatcher;
3851 ret = VirtualProtect(hook_trampoline, ARRAY_SIZE(hook_trampoline), PAGE_EXECUTE_READWRITE, &old_protect1);
3852 ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
3854 ret = VirtualProtect(pKiUserExceptionDispatcher, sizeof(saved_KiUserExceptionDispatcher_bytes),
3855 PAGE_EXECUTE_READWRITE, &old_protect2);
3856 ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
3858 memcpy(saved_KiUserExceptionDispatcher_bytes, pKiUserExceptionDispatcher,
3859 sizeof(saved_KiUserExceptionDispatcher_bytes));
3860 ptr = (BYTE *)patched_KiUserExceptionDispatcher_bytes;
3861 /* mov hook_trampoline, %rax */
3862 *ptr++ = 0x48;
3863 *ptr++ = 0xb8;
3864 *(void **)ptr = hook_trampoline;
3865 ptr += sizeof(ULONG64);
3866 /* jmp *rax */
3867 *ptr++ = 0xff;
3868 *ptr++ = 0xe0;
3870 memcpy(pKiUserExceptionDispatcher, patched_KiUserExceptionDispatcher_bytes,
3871 sizeof(patched_KiUserExceptionDispatcher_bytes));
3872 got_exception = 0;
3873 run_exception_test(dbg_except_continue_handler, NULL, except_code, ARRAY_SIZE(except_code), PAGE_EXECUTE_READ);
3874 ok(got_exception, "Handler was not called.\n");
3875 ok(hook_called, "Hook was not called.\n");
3877 ok(test_kiuserexceptiondispatcher_regs.new_rax == 0xdeadbeef, "Got unexpected rax %#lx.\n",
3878 test_kiuserexceptiondispatcher_regs.new_rax);
3879 ok(test_kiuserexceptiondispatcher_regs.old_rsi
3880 == test_kiuserexceptiondispatcher_regs.new_rsi, "rsi does not match.\n");
3881 ok(test_kiuserexceptiondispatcher_regs.old_rdi
3882 == test_kiuserexceptiondispatcher_regs.new_rdi, "rdi does not match.\n");
3883 ok(test_kiuserexceptiondispatcher_regs.old_rbp
3884 == test_kiuserexceptiondispatcher_regs.new_rbp, "rbp does not match.\n");
3886 bpt_address = (BYTE *)code_mem + 0x25;
3888 ok(hook_exception_address == bpt_address || broken(!hook_exception_address) /* Win2008 */,
3889 "Got unexpected exception address %p, expected %p.\n",
3890 hook_exception_address, bpt_address);
3891 ok(hook_KiUserExceptionDispatcher_rip == bpt_address, "Got unexpected exception address %p, expected %p.\n",
3892 hook_KiUserExceptionDispatcher_rip, bpt_address);
3893 ok(dbg_except_continue_handler_rip == bpt_address, "Got unexpected exception address %p, expected %p.\n",
3894 dbg_except_continue_handler_rip, bpt_address);
3896 memset(&record, 0, sizeof(record));
3897 record.ExceptionCode = 0x80000003;
3898 record.ExceptionFlags = 0;
3899 record.ExceptionRecord = NULL;
3900 record.ExceptionAddress = NULL;
3901 record.NumberParameters = 0;
3903 vectored_handler = AddVectoredExceptionHandler(TRUE, dbg_except_continue_vectored_handler);
3905 memcpy(pKiUserExceptionDispatcher, patched_KiUserExceptionDispatcher_bytes,
3906 sizeof(patched_KiUserExceptionDispatcher_bytes));
3907 got_exception = 0;
3908 hook_called = FALSE;
3910 pRtlRaiseException(&record);
3912 ok(got_exception, "Handler was not called.\n");
3913 ok(!hook_called, "Hook was called.\n");
3915 memcpy(pKiUserExceptionDispatcher, patched_KiUserExceptionDispatcher_bytes,
3916 sizeof(patched_KiUserExceptionDispatcher_bytes));
3917 got_exception = 0;
3918 hook_called = FALSE;
3919 NtCurrentTeb()->Peb->BeingDebugged = 1;
3921 pRtlRaiseException(&record);
3923 ok(got_exception, "Handler was not called.\n");
3924 ok(hook_called, "Hook was not called.\n");
3926 ok(hook_exception_address == (BYTE *)hook_KiUserExceptionDispatcher_rip + 1
3927 || broken(!hook_exception_address) /* 2008 */, "Got unexpected addresses %p, %p.\n",
3928 hook_KiUserExceptionDispatcher_rip, hook_exception_address);
3930 RemoveVectoredExceptionHandler(vectored_handler);
3932 memcpy(pKiUserExceptionDispatcher, patched_KiUserExceptionDispatcher_bytes,
3933 sizeof(patched_KiUserExceptionDispatcher_bytes));
3934 got_exception = 0;
3935 hook_called = FALSE;
3937 run_exception_test(dbg_except_continue_handler, NULL, except_code, ARRAY_SIZE(except_code), PAGE_EXECUTE_READ);
3939 ok(got_exception, "Handler was not called.\n");
3940 ok(hook_called, "Hook was not called.\n");
3941 ok(hook_KiUserExceptionDispatcher_rip == bpt_address, "Got unexpected exception address %p, expected %p.\n",
3942 hook_KiUserExceptionDispatcher_rip, bpt_address);
3943 ok(dbg_except_continue_handler_rip == bpt_address, "Got unexpected exception address %p, expected %p.\n",
3944 dbg_except_continue_handler_rip, bpt_address);
3946 ok(test_kiuserexceptiondispatcher_regs.new_rax == 0xdeadbeef, "Got unexpected rax %#lx.\n",
3947 test_kiuserexceptiondispatcher_regs.new_rax);
3948 ok(test_kiuserexceptiondispatcher_regs.old_rsi
3949 == test_kiuserexceptiondispatcher_regs.new_rsi, "rsi does not match.\n");
3950 ok(test_kiuserexceptiondispatcher_regs.old_rdi
3951 == test_kiuserexceptiondispatcher_regs.new_rdi, "rdi does not match.\n");
3952 ok(test_kiuserexceptiondispatcher_regs.old_rbp
3953 == test_kiuserexceptiondispatcher_regs.new_rbp, "rbp does not match.\n");
3955 NtCurrentTeb()->Peb->BeingDebugged = 0;
3957 ret = VirtualProtect(pKiUserExceptionDispatcher, sizeof(saved_KiUserExceptionDispatcher_bytes),
3958 old_protect2, &old_protect2);
3959 ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
3960 ret = VirtualProtect(hook_trampoline, ARRAY_SIZE(hook_trampoline), old_protect1, &old_protect1);
3961 ok(ret, "Got unexpected ret %#x, GetLastError() %u.\n", ret, GetLastError());
3964 static BOOL got_nested_exception, got_prev_frame_exception;
3965 static void *nested_exception_initial_frame;
3967 static DWORD nested_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
3968 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher)
3970 trace("nested_exception_handler Rip %p, Rsp %p, code %#x, flags %#x, ExceptionAddress %p.\n",
3971 (void *)context->Rip, (void *)context->Rsp, rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress);
3973 if (rec->ExceptionCode == 0x80000003
3974 && !(rec->ExceptionFlags & EH_NESTED_CALL))
3976 ok(rec->NumberParameters == 1, "Got unexpected rec->NumberParameters %u.\n", rec->NumberParameters);
3977 ok((void *)context->Rsp == frame, "Got unexpected frame %p.\n", frame);
3978 ok(*(void **)frame == (char *)code_mem + 5, "Got unexpected *frame %p.\n", *(void **)frame);
3979 ok(context->Rip == (ULONG_PTR)((char *)code_mem + 7), "Got unexpected Rip %#lx.\n", context->Rip);
3981 nested_exception_initial_frame = frame;
3982 RaiseException(0xdeadbeef, 0, 0, 0);
3983 ++context->Rip;
3984 return ExceptionContinueExecution;
3987 if (rec->ExceptionCode == 0xdeadbeef && rec->ExceptionFlags == EH_NESTED_CALL)
3989 ok(!rec->NumberParameters, "Got unexpected rec->NumberParameters %u.\n", rec->NumberParameters);
3990 got_nested_exception = TRUE;
3991 ok(frame == nested_exception_initial_frame, "Got unexpected frame %p.\n", frame);
3992 return ExceptionContinueSearch;
3995 ok(rec->ExceptionCode == 0xdeadbeef && !rec->ExceptionFlags,
3996 "Got unexpected exception code %#x, flags %#x.\n", rec->ExceptionCode, rec->ExceptionFlags);
3997 ok(!rec->NumberParameters, "Got unexpected rec->NumberParameters %u.\n", rec->NumberParameters);
3998 ok(frame == (void *)((BYTE *)nested_exception_initial_frame + 8),
3999 "Got unexpected frame %p.\n", frame);
4000 got_prev_frame_exception = TRUE;
4001 return ExceptionContinueExecution;
4004 static void test_nested_exception(void)
4006 static const BYTE except_code[] =
4008 0xe8, 0x02, 0x00, 0x00, 0x00, /* call nest */
4009 0x90, /* nop */
4010 0xc3, /* ret */
4011 /* nest: */
4012 0xcc, /* int3 */
4013 0x90, /* nop */
4014 0xc3, /* ret */
4017 got_nested_exception = got_prev_frame_exception = FALSE;
4018 run_exception_test(nested_exception_handler, NULL, except_code, ARRAY_SIZE(except_code), PAGE_EXECUTE_READ);
4019 ok(got_nested_exception, "Did not get nested exception.\n");
4020 ok(got_prev_frame_exception, "Did not get nested exception in the previous frame.\n");
4023 #elif defined(__arm__)
4025 static void test_thread_context(void)
4027 CONTEXT context;
4028 NTSTATUS status;
4029 struct expected
4031 DWORD R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, Sp, Lr, Pc, Cpsr;
4032 } expect;
4033 NTSTATUS (*func_ptr)( void *arg1, void *arg2, struct expected *res, void *func ) = (void *)code_mem;
4035 static const DWORD call_func[] =
4037 0xe92d4002, /* push {r1, lr} */
4038 0xe8821fff, /* stm r2, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, sl, fp, ip} */
4039 0xe582d034, /* str sp, [r2, #52] */
4040 0xe582e038, /* str lr, [r2, #56] */
4041 0xe10f1000, /* mrs r1, CPSR */
4042 0xe5821040, /* str r1, [r2, #64] */
4043 0xe59d1000, /* ldr r1, [sp] */
4044 0xe582f03c, /* str pc, [r2, #60] */
4045 0xe12fff33, /* blx r3 */
4046 0xe8bd8002, /* pop {r1, pc} */
4049 memcpy( func_ptr, call_func, sizeof(call_func) );
4051 #define COMPARE(reg) \
4052 ok( context.reg == expect.reg, "wrong " #reg " %08x/%08x\n", context.reg, expect.reg )
4054 memset( &context, 0xcc, sizeof(context) );
4055 memset( &expect, 0xcc, sizeof(expect) );
4056 func_ptr( &context, 0, &expect, pRtlCaptureContext );
4057 trace( "expect: r0=%08x r1=%08x r2=%08x r3=%08x r4=%08x r5=%08x r6=%08x r7=%08x r8=%08x r9=%08x "
4058 "r10=%08x r11=%08x r12=%08x sp=%08x lr=%08x pc=%08x cpsr=%08x\n",
4059 expect.R0, expect.R1, expect.R2, expect.R3, expect.R4, expect.R5, expect.R6, expect.R7,
4060 expect.R8, expect.R9, expect.R10, expect.R11, expect.R12, expect.Sp, expect.Lr, expect.Pc, expect.Cpsr );
4061 trace( "actual: r0=%08x r1=%08x r2=%08x r3=%08x r4=%08x r5=%08x r6=%08x r7=%08x r8=%08x r9=%08x "
4062 "r10=%08x r11=%08x r12=%08x sp=%08x lr=%08x pc=%08x cpsr=%08x\n",
4063 context.R0, context.R1, context.R2, context.R3, context.R4, context.R5, context.R6, context.R7,
4064 context.R8, context.R9, context.R10, context.R11, context.R12, context.Sp, context.Lr, context.Pc, context.Cpsr );
4066 ok( context.ContextFlags == CONTEXT_FULL,
4067 "wrong flags %08x\n", context.ContextFlags );
4068 COMPARE( R0 );
4069 COMPARE( R1 );
4070 COMPARE( R2 );
4071 COMPARE( R3 );
4072 COMPARE( R4 );
4073 COMPARE( R5 );
4074 COMPARE( R6 );
4075 COMPARE( R7 );
4076 COMPARE( R8 );
4077 COMPARE( R9 );
4078 COMPARE( R10 );
4079 COMPARE( R11 );
4080 COMPARE( R12 );
4081 COMPARE( Sp );
4082 COMPARE( Pc );
4083 COMPARE( Cpsr );
4084 ok( context.Lr == expect.Pc, "wrong Lr %08x/%08x\n", context.Lr, expect.Pc );
4086 memset( &context, 0xcc, sizeof(context) );
4087 memset( &expect, 0xcc, sizeof(expect) );
4088 context.ContextFlags = CONTEXT_FULL;
4090 status = func_ptr( GetCurrentThread(), &context, &expect, pNtGetContextThread );
4091 ok( status == STATUS_SUCCESS, "NtGetContextThread failed %08x\n", status );
4092 trace( "expect: r0=%08x r1=%08x r2=%08x r3=%08x r4=%08x r5=%08x r6=%08x r7=%08x r8=%08x r9=%08x "
4093 "r10=%08x r11=%08x r12=%08x sp=%08x lr=%08x pc=%08x cpsr=%08x\n",
4094 expect.R0, expect.R1, expect.R2, expect.R3, expect.R4, expect.R5, expect.R6, expect.R7,
4095 expect.R8, expect.R9, expect.R10, expect.R11, expect.R12, expect.Sp, expect.Lr, expect.Pc, expect.Cpsr );
4096 trace( "actual: r0=%08x r1=%08x r2=%08x r3=%08x r4=%08x r5=%08x r6=%08x r7=%08x r8=%08x r9=%08x "
4097 "r10=%08x r11=%08x r12=%08x sp=%08x lr=%08x pc=%08x cpsr=%08x\n",
4098 context.R0, context.R1, context.R2, context.R3, context.R4, context.R5, context.R6, context.R7,
4099 context.R8, context.R9, context.R10, context.R11, context.R12, context.Sp, context.Lr, context.Pc, context.Cpsr );
4100 /* other registers are not preserved */
4101 COMPARE( R4 );
4102 COMPARE( R5 );
4103 COMPARE( R6 );
4104 COMPARE( R7 );
4105 COMPARE( R8 );
4106 COMPARE( R9 );
4107 COMPARE( R10 );
4108 COMPARE( R11 );
4109 COMPARE( Cpsr );
4110 ok( context.Sp == expect.Sp - 8,
4111 "wrong Sp %08x/%08x\n", context.Sp, expect.Sp - 8 );
4112 /* Pc is somewhere close to the NtGetContextThread implementation */
4113 ok( (char *)context.Pc >= (char *)pNtGetContextThread - 0x40000 &&
4114 (char *)context.Pc <= (char *)pNtGetContextThread + 0x40000,
4115 "wrong Pc %08x/%08x\n", context.Pc, (DWORD)pNtGetContextThread );
4116 #undef COMPARE
4119 static void test_debugger(void)
4121 char cmdline[MAX_PATH];
4122 PROCESS_INFORMATION pi;
4123 STARTUPINFOA si = { 0 };
4124 DEBUG_EVENT de;
4125 DWORD continuestatus;
4126 PVOID code_mem_address = NULL;
4127 NTSTATUS status;
4128 SIZE_T size_read;
4129 BOOL ret;
4130 int counter = 0;
4131 si.cb = sizeof(si);
4133 if(!pNtGetContextThread || !pNtSetContextThread || !pNtReadVirtualMemory || !pNtTerminateProcess)
4135 skip("NtGetContextThread, NtSetContextThread, NtReadVirtualMemory or NtTerminateProcess not found\n");
4136 return;
4139 sprintf(cmdline, "%s %s %s %p", my_argv[0], my_argv[1], "debuggee", &test_stage);
4140 ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi);
4141 ok(ret, "could not create child process error: %u\n", GetLastError());
4142 if (!ret)
4143 return;
4147 continuestatus = DBG_CONTINUE;
4148 ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n");
4150 ret = ContinueDebugEvent(de.dwProcessId, de.dwThreadId, 0xdeadbeef);
4151 ok(!ret, "ContinueDebugEvent unexpectedly succeeded\n");
4152 ok(GetLastError() == ERROR_INVALID_PARAMETER, "Unexpected last error: %u\n", GetLastError());
4154 if (de.dwThreadId != pi.dwThreadId)
4156 trace("event %d not coming from main thread, ignoring\n", de.dwDebugEventCode);
4157 ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE);
4158 continue;
4161 if (de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT)
4163 if(de.u.CreateProcessInfo.lpBaseOfImage != NtCurrentTeb()->Peb->ImageBaseAddress)
4165 skip("child process loaded at different address, terminating it\n");
4166 pNtTerminateProcess(pi.hProcess, 0);
4169 else if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
4171 CONTEXT ctx;
4172 int stage;
4174 counter++;
4175 status = pNtReadVirtualMemory(pi.hProcess, &code_mem, &code_mem_address,
4176 sizeof(code_mem_address), &size_read);
4177 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
4178 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
4179 sizeof(stage), &size_read);
4180 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
4182 ctx.ContextFlags = CONTEXT_FULL;
4183 status = pNtGetContextThread(pi.hThread, &ctx);
4184 ok(!status, "NtGetContextThread failed with 0x%x\n", status);
4186 trace("exception 0x%x at %p firstchance=%d pc=%08x, r0=%08x\n",
4187 de.u.Exception.ExceptionRecord.ExceptionCode,
4188 de.u.Exception.ExceptionRecord.ExceptionAddress,
4189 de.u.Exception.dwFirstChance, ctx.Pc, ctx.R0);
4191 if (counter > 100)
4193 ok(FALSE, "got way too many exceptions, probably caught in an infinite loop, terminating child\n");
4194 pNtTerminateProcess(pi.hProcess, 1);
4196 else if (counter < 2) /* startup breakpoint */
4198 /* breakpoint is inside ntdll */
4199 void *ntdll = GetModuleHandleA( "ntdll.dll" );
4200 IMAGE_NT_HEADERS *nt = RtlImageNtHeader( ntdll );
4202 ok( (char *)ctx.Pc >= (char *)ntdll &&
4203 (char *)ctx.Pc < (char *)ntdll + nt->OptionalHeader.SizeOfImage,
4204 "wrong pc %p ntdll %p-%p\n", (void *)ctx.Pc, ntdll,
4205 (char *)ntdll + nt->OptionalHeader.SizeOfImage );
4207 else
4209 #if 0 /* RtlRaiseException test disabled for now */
4210 if (stage == 1)
4212 ok((char *)ctx.Pc == (char *)code_mem_address + 0xb, "Pc at %x instead of %p\n",
4213 ctx.Pc, (char *)code_mem_address + 0xb);
4214 /* setting the context from debugger does not affect the context that the
4215 * exception handler gets, except on w2008 */
4216 ctx.Pc = (UINT_PTR)code_mem_address + 0xd;
4217 ctx.R0 = 0xf00f00f1;
4218 /* let the debuggee handle the exception */
4219 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
4221 else if (stage == 2)
4223 if (de.u.Exception.dwFirstChance)
4225 /* debugger gets first chance exception with unmodified ctx.Pc */
4226 ok((char *)ctx.Pc == (char *)code_mem_address + 0xb, "Pc at 0x%x instead of %p\n",
4227 ctx.Pc, (char *)code_mem_address + 0xb);
4228 ctx.Pc = (UINT_PTR)code_mem_address + 0xd;
4229 ctx.R0 = 0xf00f00f1;
4230 /* pass exception to debuggee
4231 * exception will not be handled and a second chance exception will be raised */
4232 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
4234 else
4236 /* debugger gets context after exception handler has played with it */
4237 /* ctx.Pc is the same value the exception handler got */
4238 if (de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT)
4240 ok((char *)ctx.Pc == (char *)code_mem_address + 0xa,
4241 "Pc at 0x%x instead of %p\n", ctx.Pc, (char *)code_mem_address + 0xa);
4242 /* need to fixup Pc for debuggee */
4243 /*ctx.Pc += 2; */
4245 else ok((char *)ctx.Pc == (char *)code_mem_address + 0xb,
4246 "Pc at 0x%x instead of %p\n", ctx.Pc, (char *)code_mem_address + 0xb);
4247 /* here we handle exception */
4250 else
4251 #endif
4252 if (stage == 7 || stage == 8)
4254 ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT,
4255 "expected EXCEPTION_BREAKPOINT, got %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode);
4256 ok((char *)ctx.Pc == (char *)code_mem_address + 0x1d,
4257 "expected Pc = %p, got 0x%x\n", (char *)code_mem_address + 0x1d, ctx.Pc);
4258 if (stage == 8) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
4260 else if (stage == 9 || stage == 10)
4262 ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT,
4263 "expected EXCEPTION_BREAKPOINT, got %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode);
4264 ok((char *)ctx.Pc == (char *)code_mem_address + 4,
4265 "expected Pc = %p, got 0x%x\n", (char *)code_mem_address + 4, ctx.Pc);
4266 if (stage == 10) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
4268 else if (stage == 11 || stage == 12 || stage == 13)
4270 ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_INVALID_HANDLE,
4271 "unexpected exception code %08x, expected %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode,
4272 EXCEPTION_INVALID_HANDLE);
4273 ok(de.u.Exception.ExceptionRecord.NumberParameters == 0,
4274 "unexpected number of parameters %d, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters);
4276 if (stage == 12|| stage == 13) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
4278 else
4279 ok(FALSE, "unexpected stage %x\n", stage);
4281 status = pNtSetContextThread(pi.hThread, &ctx);
4282 ok(!status, "NtSetContextThread failed with 0x%x\n", status);
4285 else if (de.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT)
4287 int stage;
4288 char buffer[64];
4290 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
4291 sizeof(stage), &size_read);
4292 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
4294 ok(!de.u.DebugString.fUnicode, "unexpected unicode debug string event\n");
4295 ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) - 1, "buffer not large enough to hold %d bytes\n",
4296 de.u.DebugString.nDebugStringLength);
4298 memset(buffer, 0, sizeof(buffer));
4299 status = pNtReadVirtualMemory(pi.hProcess, de.u.DebugString.lpDebugStringData, buffer,
4300 de.u.DebugString.nDebugStringLength, &size_read);
4301 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
4303 if (stage == 3 || stage == 4)
4304 ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer);
4305 else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */
4306 ok(strstr(buffer, "SHIMVIEW") != NULL, "unexpected stage %x, got debug string event '%s'\n", stage, buffer);
4308 if (stage == 4) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
4310 else if (de.dwDebugEventCode == RIP_EVENT)
4312 int stage;
4314 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
4315 sizeof(stage), &size_read);
4316 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
4318 if (stage == 5 || stage == 6)
4320 ok(de.u.RipInfo.dwError == 0x11223344, "got unexpected rip error code %08x, expected %08x\n",
4321 de.u.RipInfo.dwError, 0x11223344);
4322 ok(de.u.RipInfo.dwType == 0x55667788, "got unexpected rip type %08x, expected %08x\n",
4323 de.u.RipInfo.dwType, 0x55667788);
4325 else
4326 ok(FALSE, "unexpected stage %x\n", stage);
4328 if (stage == 6) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
4331 ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus);
4333 } while (de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT);
4335 wait_child_process( pi.hProcess );
4336 ret = CloseHandle(pi.hThread);
4337 ok(ret, "error %u\n", GetLastError());
4338 ret = CloseHandle(pi.hProcess);
4339 ok(ret, "error %u\n", GetLastError());
4342 static void test_debug_service(DWORD numexc)
4344 /* not supported */
4347 #elif defined(__aarch64__)
4349 #define UNW_FLAG_NHANDLER 0
4350 #define UNW_FLAG_EHANDLER 1
4351 #define UNW_FLAG_UHANDLER 2
4353 #define UWOP_TWOBYTES(x) (((x) >> 8) & 0xff), ((x) & 0xff)
4355 #define UWOP_ALLOC_SMALL(size) (0x00 | (size/16))
4356 #define UWOP_SAVE_R19R20_X(offset) (0x20 | (offset/8))
4357 #define UWOP_SAVE_FPLR(offset) (0x40 | (offset/8))
4358 #define UWOP_SAVE_FPLR_X(offset) (0x80 | (offset/8 - 1))
4359 #define UWOP_ALLOC_MEDIUM(size) UWOP_TWOBYTES((0xC0 << 8) | (size/16))
4360 #define UWOP_SAVE_REGP(reg, offset) UWOP_TWOBYTES((0xC8 << 8) | ((reg - 19) << 6) | (offset/8))
4361 #define UWOP_SAVE_REGP_X(reg, offset) UWOP_TWOBYTES((0xCC << 8) | ((reg - 19) << 6) | (offset/8 - 1))
4362 #define UWOP_SAVE_REG(reg, offset) UWOP_TWOBYTES((0xD0 << 8) | ((reg - 19) << 6) | (offset/8))
4363 #define UWOP_SAVE_REG_X(reg, offset) UWOP_TWOBYTES((0xD4 << 8) | ((reg - 19) << 5) | (offset/8 - 1))
4364 #define UWOP_SAVE_LRP(reg, offset) UWOP_TWOBYTES((0xD6 << 8) | ((reg - 19)/2 << 6) | (offset/8))
4365 #define UWOP_SAVE_FREGP(reg, offset) UWOP_TWOBYTES((0xD8 << 8) | ((reg - 8) << 6) | (offset/8))
4366 #define UWOP_SAVE_FREGP_X(reg, offset) UWOP_TWOBYTES((0xDA << 8) | ((reg - 8) << 6) | (offset/8 - 1))
4367 #define UWOP_SAVE_FREG(reg, offset) UWOP_TWOBYTES((0xDC << 8) | ((reg - 8) << 6) | (offset/8))
4368 #define UWOP_SAVE_FREG_X(reg, offset) UWOP_TWOBYTES((0xDE << 8) | ((reg - 8) << 5) | (offset/8 - 1))
4369 #define UWOP_ALLOC_LARGE(size) UWOP_TWOBYTES((0xE0 << 8) | ((size/16) >> 16)), UWOP_TWOBYTES(size/16)
4370 #define UWOP_SET_FP 0xE1
4371 #define UWOP_ADD_FP(offset) UWOP_TWOBYTES((0xE2 << 8) | (offset/8))
4372 #define UWOP_NOP 0xE3
4373 #define UWOP_END 0xE4
4374 #define UWOP_END_C 0xE5
4375 #define UWOP_SAVE_NEXT 0xE6
4376 #define UWOP_TRAP_FRAME 0xE8
4377 #define UWOP_MACHINE_FRAME 0xE9
4378 #define UWOP_CONTEXT 0xEA
4379 #define UWOP_CLEAR_UNWOUND_TO_CALL 0xEC
4381 struct results
4383 int pc_offset; /* pc offset from code start */
4384 int fp_offset; /* fp offset from stack pointer */
4385 int handler; /* expect handler to be set? */
4386 ULONG_PTR pc; /* expected final pc value */
4387 int frame; /* expected frame return value */
4388 int frame_offset; /* whether the frame return value is an offset or an absolute value */
4389 int regs[48][2]; /* expected values for registers */
4392 struct unwind_test
4394 const BYTE *function;
4395 size_t function_size;
4396 const BYTE *unwind_info;
4397 size_t unwind_size;
4398 const struct results *results;
4399 unsigned int nb_results;
4402 enum regs
4404 x0, x1, x2, x3, x4, x5, x6, x7,
4405 x8, x9, x10, x11, x12, x13, x14, x15,
4406 x16, x17, x18, x19, x20, x21, x22, x23,
4407 x24, x25, x26, x27, x28, x29, lr, sp,
4408 d0, d1, d2, d3, d4, d5, d6, d7,
4409 d8, d9, d10, d11, d12, d13, d14, d15
4412 static const char * const reg_names[48] =
4414 "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
4415 "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
4416 "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
4417 "x24", "x25", "x26", "x27", "x28", "x29", "lr", "sp",
4418 "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
4419 "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15",
4422 #define ORIG_LR 0xCCCCCCCC
4424 #define UWOP(code,info) (UWOP_##code | ((info) << 4))
4426 static void call_virtual_unwind( int testnum, const struct unwind_test *test )
4428 static const int code_offset = 1024;
4429 static const int unwind_offset = 2048;
4430 void *handler, *data;
4431 CONTEXT context;
4432 RUNTIME_FUNCTION runtime_func;
4433 KNONVOLATILE_CONTEXT_POINTERS ctx_ptr;
4434 UINT i, j, k;
4435 ULONG64 fake_stack[256];
4436 ULONG64 frame, orig_pc, orig_fp, unset_reg, sp_offset = 0;
4437 static const UINT nb_regs = ARRAY_SIZE(test->results[i].regs);
4439 memcpy( (char *)code_mem + code_offset, test->function, test->function_size );
4440 memcpy( (char *)code_mem + unwind_offset, test->unwind_info, test->unwind_size );
4442 runtime_func.BeginAddress = code_offset;
4443 if (test->unwind_size)
4444 runtime_func.UnwindData = unwind_offset;
4445 else
4446 memcpy(&runtime_func.UnwindData, test->unwind_info, 4);
4448 trace( "code: %p stack: %p\n", code_mem, fake_stack );
4450 for (i = 0; i < test->nb_results; i++)
4452 memset( &ctx_ptr, 0, sizeof(ctx_ptr) );
4453 memset( &context, 0x55, sizeof(context) );
4454 memset( &unset_reg, 0x55, sizeof(unset_reg) );
4455 for (j = 0; j < 256; j++) fake_stack[j] = j * 8;
4457 context.Sp = (ULONG_PTR)fake_stack;
4458 context.Lr = (ULONG_PTR)ORIG_LR;
4459 context.Fp = (ULONG_PTR)fake_stack + test->results[i].fp_offset;
4460 orig_fp = context.Fp;
4461 orig_pc = (ULONG64)code_mem + code_offset + test->results[i].pc_offset;
4463 trace( "%u/%u: pc=%p (%02x) fp=%p sp=%p\n", testnum, i,
4464 (void *)orig_pc, *(DWORD *)orig_pc, (void *)orig_fp, (void *)context.Sp );
4466 data = (void *)0xdeadbeef;
4467 handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG64)code_mem, orig_pc,
4468 &runtime_func, &context, &data, &frame, &ctx_ptr );
4469 if (test->results[i].handler > 0)
4471 /* Yet untested */
4472 ok( (char *)handler == (char *)code_mem + 0x200,
4473 "%u/%u: wrong handler %p/%p\n", testnum, i, handler, (char *)code_mem + 0x200 );
4474 if (handler) ok( *(DWORD *)data == 0x08070605,
4475 "%u/%u: wrong handler data %p\n", testnum, i, data );
4477 else
4479 ok( handler == NULL, "%u/%u: handler %p instead of NULL\n", testnum, i, handler );
4480 ok( data == (test->results[i].handler < 0 ?
4481 (void *)0xdeadbeef : NULL),
4482 "%u/%u: handler data set to %p/%p\n", testnum, i, data,
4483 (test->results[i].handler < 0 ? (void *)0xdeadbeef : NULL) );
4486 ok( context.Pc == test->results[i].pc, "%u/%u: wrong pc %p/%p\n",
4487 testnum, i, (void *)context.Pc, (void*)test->results[i].pc );
4488 ok( frame == (test->results[i].frame_offset ? (ULONG64)fake_stack : 0) + test->results[i].frame, "%u/%u: wrong frame %p/%p\n",
4489 testnum, i, (void *)frame, (char *)(test->results[i].frame_offset ? fake_stack : NULL) + test->results[i].frame );
4491 sp_offset = 0;
4492 for (k = 0; k < nb_regs; k++)
4494 if (test->results[i].regs[k][0] == -1)
4495 break;
4496 if (test->results[i].regs[k][0] == sp) {
4497 /* If sp is part of the registers list, treat it as an offset
4498 * between the returned frame pointer and the sp register. */
4499 sp_offset = test->results[i].regs[k][1];
4500 break;
4503 ok( frame - sp_offset == context.Sp, "%u/%u: wrong sp %p/%p\n",
4504 testnum, i, (void *)(frame - sp_offset), (void *)context.Sp);
4506 for (j = 0; j < 48; j++)
4508 if (j == sp) continue; /* Handling sp separately above */
4510 for (k = 0; k < nb_regs; k++)
4512 if (test->results[i].regs[k][0] == -1)
4514 k = nb_regs;
4515 break;
4517 if (test->results[i].regs[k][0] == j) break;
4520 if (j >= 19 && j <= 30 && (&ctx_ptr.X19)[j - 19])
4522 ok( k < nb_regs, "%u/%u: register %s should not be set to %llx\n",
4523 testnum, i, reg_names[j], context.X[j] );
4524 if (k < nb_regs)
4525 ok( context.X[j] == test->results[i].regs[k][1],
4526 "%u/%u: register %s wrong %p/%x\n",
4527 testnum, i, reg_names[j], (void *)context.X[j], test->results[i].regs[k][1] );
4529 else if (j >= d8 && j <= d15 && (&ctx_ptr.D8)[j - d8])
4531 ok( k < nb_regs, "%u/%u: register %s should not be set to %llx\n",
4532 testnum, i, reg_names[j], context.V[j - d0].Low );
4533 if (k < nb_regs)
4534 ok( context.V[j - d0].Low == test->results[i].regs[k][1],
4535 "%u/%u: register %s wrong %p/%x\n",
4536 testnum, i, reg_names[j], (void *)context.V[j - d0].Low, test->results[i].regs[k][1] );
4538 else if (k < nb_regs)
4540 if (j < d0)
4541 ok( context.X[j] == test->results[i].regs[k][1],
4542 "%u/%u: register %s wrong %p/%x\n",
4543 testnum, i, reg_names[j], (void *)context.X[j], test->results[i].regs[k][1] );
4544 else
4545 ok( context.V[j - d0].Low == test->results[i].regs[k][1],
4546 "%u/%u: register %s wrong %p/%x\n",
4547 testnum, i, reg_names[j], (void *)context.V[j - d0].Low, test->results[i].regs[k][1] );
4549 else
4551 ok( k == nb_regs, "%u/%u: register %s should be set\n", testnum, i, reg_names[j] );
4552 if (j == lr)
4553 ok( context.Lr == ORIG_LR, "%u/%u: register lr wrong %p/unset\n",
4554 testnum, i, (void *)context.Lr );
4555 else if (j == x29)
4556 ok( context.Fp == orig_fp, "%u/%u: register fp wrong %p/unset\n",
4557 testnum, i, (void *)context.Fp );
4558 else if (j < d0)
4559 ok( context.X[j] == unset_reg,
4560 "%u/%u: register %s wrong %p/unset\n",
4561 testnum, i, reg_names[j], (void *)context.X[j]);
4562 else
4563 ok( context.V[j - d0].Low == unset_reg,
4564 "%u/%u: register %s wrong %p/unset\n",
4565 testnum, i, reg_names[j], (void *)context.V[j - d0].Low);
4571 #define DW(dword) ((dword >> 0) & 0xff), ((dword >> 8) & 0xff), ((dword >> 16) & 0xff), ((dword >> 24) & 0xff)
4573 static void test_virtual_unwind(void)
4575 static const BYTE function_0[] =
4577 0xff, 0x83, 0x00, 0xd1, /* 00: sub sp, sp, #32 */
4578 0xf3, 0x53, 0x01, 0xa9, /* 04: stp x19, x20, [sp, #16] */
4579 0x1f, 0x20, 0x03, 0xd5, /* 08: nop */
4580 0xf3, 0x53, 0x41, 0xa9, /* 0c: ldp x19, x20, [sp, #16] */
4581 0xff, 0x83, 0x00, 0x91, /* 10: add sp, sp, #32 */
4582 0xc0, 0x03, 0x5f, 0xd6, /* 14: ret */
4585 static const DWORD unwind_info_0_header =
4586 (sizeof(function_0)/4) | /* function length */
4587 (0 << 20) | /* X */
4588 (0 << 21) | /* E */
4589 (1 << 22) | /* epilog */
4590 (2 << 27); /* codes */
4591 static const DWORD unwind_info_0_epilog0 =
4592 (3 << 0) | /* offset */
4593 (4 << 22); /* index */
4595 static const BYTE unwind_info_0[] =
4597 DW(unwind_info_0_header),
4598 DW(unwind_info_0_epilog0),
4600 UWOP_SAVE_REGP(19, 16), /* stp x19, x20, [sp, #16] */
4601 UWOP_ALLOC_SMALL(32), /* sub sp, sp, #32 */
4602 UWOP_END,
4604 UWOP_SAVE_REGP(19, 16), /* stp x19, x20, [sp, #16] */
4605 UWOP_ALLOC_SMALL(32), /* sub sp, sp, #32 */
4606 UWOP_END,
4609 static const struct results results_0[] =
4611 /* offset fp handler pc frame offset registers */
4612 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
4613 { 0x04, 0x00, 0, ORIG_LR, 0x020, TRUE, { {-1,-1} }},
4614 { 0x08, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19,0x10}, {x20,0x18}, {-1,-1} }},
4615 { 0x0c, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19,0x10}, {x20,0x18}, {-1,-1} }},
4616 { 0x10, 0x00, 0, ORIG_LR, 0x020, TRUE, { {-1,-1} }},
4617 { 0x14, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
4621 static const BYTE function_1[] =
4623 0xf3, 0x53, 0xbe, 0xa9, /* 00: stp x19, x20, [sp, #-32]! */
4624 0xfe, 0x0b, 0x00, 0xf9, /* 04: str x30, [sp, #16] */
4625 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */
4626 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */
4627 0xff, 0x43, 0x00, 0x91, /* 10: add sp, sp, #16 */
4628 0xfe, 0x0b, 0x40, 0xf9, /* 14: ldr x30, [sp, #16] */
4629 0xf3, 0x53, 0xc2, 0xa8, /* 18: ldp x19, x20, [sp], #32 */
4630 0xc0, 0x03, 0x5f, 0xd6, /* 1c: ret */
4633 static const DWORD unwind_info_1_packed =
4634 (1 << 0) | /* Flag */
4635 (sizeof(function_1)/4 << 2) | /* FunctionLength */
4636 (0 << 13) | /* RegF */
4637 (2 << 16) | /* RegI */
4638 (0 << 20) | /* H */
4639 (1 << 21) | /* CR */
4640 (3 << 23); /* FrameSize */
4642 static const BYTE unwind_info_1[] = { DW(unwind_info_1_packed) };
4644 static const struct results results_1[] =
4646 /* offset fp handler pc frame offset registers */
4647 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
4648 { 0x04, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19,0x00}, {x20,0x08}, {-1,-1} }},
4649 { 0x08, 0x00, 0, 0x10, 0x020, TRUE, { {x19,0x00}, {x20,0x08}, {lr,0x10}, {-1,-1} }},
4650 { 0x0c, 0x00, 0, 0x20, 0x030, TRUE, { {x19,0x10}, {x20,0x18}, {lr,0x20}, {-1,-1} }},
4651 { 0x10, 0x00, 0, 0x20, 0x030, TRUE, { {x19,0x10}, {x20,0x18}, {lr,0x20}, {-1,-1} }},
4652 { 0x14, 0x00, 0, 0x10, 0x020, TRUE, { {x19,0x00}, {x20,0x08}, {lr,0x10}, {-1,-1} }},
4653 { 0x18, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19,0x00}, {x20,0x08}, {-1,-1} }},
4654 { 0x1c, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
4657 static const BYTE function_2[] =
4659 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */
4660 0x1f, 0x20, 0x03, 0xd5, /* 04: nop */
4661 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */
4662 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */
4663 0xc0, 0x03, 0x5f, 0xd6, /* 10: ret */
4666 static const DWORD unwind_info_2_header =
4667 (sizeof(function_2)/4) | /* function length */
4668 (0 << 20) | /* X */
4669 (0 << 21) | /* E */
4670 (0 << 22) | /* epilog */
4671 (1 << 27); /* codes */
4673 static const BYTE unwind_info_2[] =
4675 DW(unwind_info_2_header),
4677 UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
4678 UWOP_MACHINE_FRAME,
4679 UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
4680 UWOP_END,
4683 /* Partial prologues with the custom frame opcodes (machine frame,
4684 * context) behave like there's an off-by-one bug; unwinding from
4685 * offset 0, which normally does nothing, executes one opcode if
4686 * there's a machine frame or context in the prologue, and for other
4687 * offsets, it behaves like unwinding from one instruction further
4688 * ahead. So only test the full prologue case. */
4689 static const struct results results_2[] =
4691 /* offset fp handler pc frame offset registers */
4692 #if 0
4693 { 0x00, 0x00, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
4694 { 0x04, 0x00, 0, 0x0008, 0x010, FALSE, { {-1,-1} }},
4695 { 0x08, 0x00, 0, 0x0018, 0x020, FALSE, { {-1,-1} }},
4696 #endif
4697 { 0x0c, 0x00, 0, 0x0018, 0x020, FALSE, { {-1,-1} }},
4700 static const BYTE function_3[] =
4702 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */
4703 0x1f, 0x20, 0x03, 0xd5, /* 04: nop */
4704 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */
4705 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */
4706 0xc0, 0x03, 0x5f, 0xd6, /* 10: ret */
4709 static const DWORD unwind_info_3_header =
4710 (sizeof(function_3)/4) | /* function length */
4711 (0 << 20) | /* X */
4712 (0 << 21) | /* E */
4713 (0 << 22) | /* epilog */
4714 (1 << 27); /* codes */
4716 static const BYTE unwind_info_3[] =
4718 DW(unwind_info_3_header),
4720 UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
4721 UWOP_CONTEXT,
4722 UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */
4723 UWOP_END,
4726 static const struct results results_3[] =
4728 /* offset fp handler pc frame offset registers */
4729 #if 0
4730 { 0x00, 0x00, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }},
4731 { 0x04, 0x00, 0 , 0x0108, 0x110, FALSE, { {x0, 0x08}, {x1, 0x10}, {x2, 0x18}, {x3, 0x20}, {x4, 0x28}, {x5, 0x30}, {x6, 0x38}, {x7, 0x40}, {x8, 0x48}, {x9, 0x50}, {x10, 0x58}, {x11, 0x60}, {x12, 0x68}, {x13, 0x70}, {x14, 0x78}, {x15, 0x80}, {x16, 0x88}, {x17, 0x90}, {x18, 0x98}, {x19, 0xA0}, {x20, 0xA8}, {x21, 0xB0}, {x22, 0xB8}, {x23, 0xC0}, {x24, 0xC8}, {x25, 0xD0}, {x26, 0xD8}, {x27, 0xE0}, {x28, 0xE8}, {x29, 0xF0}, {lr, 0xF8}, {d0, 0x110}, {d1, 0x120}, {d2, 0x130}, {d3, 0x140}, {d4, 0x150}, {d5, 0x160}, {d6, 0x170}, {d7, 0x180}, {d8, 0x190}, {d9, 0x1a0}, {d10, 0x1b0}, {d11, 0x1c0}, {d12, 0x1d0}, {d13, 0x1e0}, {d14, 0x1f0}, {d15, 0x200}, {-1,-1} }},
4732 { 0x08, 0x00, 0 , 0x0118, 0x120, FALSE, { {x0, 0x18}, {x1, 0x20}, {x2, 0x28}, {x3, 0x30}, {x4, 0x38}, {x5, 0x40}, {x6, 0x48}, {x7, 0x50}, {x8, 0x58}, {x9, 0x60}, {x10, 0x68}, {x11, 0x70}, {x12, 0x78}, {x13, 0x80}, {x14, 0x88}, {x15, 0x90}, {x16, 0x98}, {x17, 0xA0}, {x18, 0xA8}, {x19, 0xB0}, {x20, 0xB8}, {x21, 0xC0}, {x22, 0xC8}, {x23, 0xD0}, {x24, 0xD8}, {x25, 0xE0}, {x26, 0xE8}, {x27, 0xF0}, {x28, 0xF8}, {x29, 0x100}, {lr, 0x108}, {d0, 0x120}, {d1, 0x130}, {d2, 0x140}, {d3, 0x150}, {d4, 0x160}, {d5, 0x170}, {d6, 0x180}, {d7, 0x190}, {d8, 0x1a0}, {d9, 0x1b0}, {d10, 0x1c0}, {d11, 0x1d0}, {d12, 0x1e0}, {d13, 0x1f0}, {d14, 0x200}, {d15, 0x210}, {-1,-1} }},
4733 #endif
4734 { 0x0c, 0x00, 0 , 0x0118, 0x120, FALSE, { {x0, 0x18}, {x1, 0x20}, {x2, 0x28}, {x3, 0x30}, {x4, 0x38}, {x5, 0x40}, {x6, 0x48}, {x7, 0x50}, {x8, 0x58}, {x9, 0x60}, {x10, 0x68}, {x11, 0x70}, {x12, 0x78}, {x13, 0x80}, {x14, 0x88}, {x15, 0x90}, {x16, 0x98}, {x17, 0xA0}, {x18, 0xA8}, {x19, 0xB0}, {x20, 0xB8}, {x21, 0xC0}, {x22, 0xC8}, {x23, 0xD0}, {x24, 0xD8}, {x25, 0xE0}, {x26, 0xE8}, {x27, 0xF0}, {x28, 0xF8}, {x29, 0x100}, {lr, 0x108}, {d0, 0x120}, {d1, 0x130}, {d2, 0x140}, {d3, 0x150}, {d4, 0x160}, {d5, 0x170}, {d6, 0x180}, {d7, 0x190}, {d8, 0x1a0}, {d9, 0x1b0}, {d10, 0x1c0}, {d11, 0x1d0}, {d12, 0x1e0}, {d13, 0x1f0}, {d14, 0x200}, {d15, 0x210}, {-1,-1} }},
4737 static const BYTE function_4[] =
4739 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */
4740 0xff, 0x03, 0x08, 0xd1, /* 04: sub sp, sp, #512 */
4741 0xff, 0x43, 0x40, 0xd1, /* 08: sub sp, sp, #65536 */
4742 0xfd, 0x03, 0x00, 0x91, /* 0c: mov x29, sp */
4743 0xf3, 0x53, 0xbe, 0xa9, /* 10: stp x19, x20, [sp, #-32]! */
4744 0xf5, 0x5b, 0x01, 0xa9, /* 14: stp x21, x22, [sp, #16] */
4745 0xf7, 0x0f, 0x1e, 0xf8, /* 18: str x23, [sp, #-32]! */
4746 0xf8, 0x07, 0x00, 0xf9, /* 1c: str x24, [sp, #8] */
4747 0xf9, 0x7b, 0x01, 0xa9, /* 20: stp x25, x30, [sp, #16] */
4748 0xfd, 0x7b, 0x03, 0xa9, /* 24: stp x29, x30, [sp, #48] */
4749 0xfd, 0x7b, 0xbe, 0xa9, /* 28: stp x29, x30, [sp, #-32]! */
4750 0xf3, 0x53, 0xbe, 0xa9, /* 2c: stp x19, x20, [sp, #-32]! */
4751 0xe8, 0x27, 0xbe, 0x6d, /* 30: stp d8, d9, [sp, #-32]! */
4752 0xea, 0x2f, 0x01, 0x6d, /* 34: stp d10, d11, [sp, #16] */
4753 0xec, 0x0f, 0x1e, 0xfc, /* 38: str d12, [sp, #-32]! */
4754 0xed, 0x07, 0x00, 0xfd, /* 3c: str d13, [sp, #8] */
4755 0xfd, 0x43, 0x00, 0x91, /* 40: add x29, sp, #16 */
4756 0xc0, 0x03, 0x5f, 0xd6, /* 44: ret */
4759 static const DWORD unwind_info_4_header =
4760 (sizeof(function_4)/4) | /* function length */
4761 (0 << 20) | /* X */
4762 (0 << 21) | /* E */
4763 (0 << 22) | /* epilog */
4764 (8 << 27); /* codes */
4766 static const BYTE unwind_info_4[] =
4768 DW(unwind_info_4_header),
4770 UWOP_ADD_FP(16), /* 40: add x29, sp, #16 */
4771 UWOP_SAVE_FREG(13, 8), /* 3c: str d13, [sp, #8] */
4772 UWOP_SAVE_FREG_X(12, 32), /* 38: str d12, [sp, #-32]! */
4773 UWOP_SAVE_FREGP(10, 16), /* 34: stp d10, d11, [sp, #16] */
4774 UWOP_SAVE_FREGP_X(8, 32), /* 30: stp d8, d9, [sp, #-32]! */
4775 UWOP_SAVE_R19R20_X(32), /* 2c: stp x19, x20, [sp, #-32]! */
4776 UWOP_SAVE_FPLR_X(32), /* 28: stp x29, x30, [sp, #-32]! */
4777 UWOP_SAVE_FPLR(16), /* 24: stp x29, x30, [sp, #16] */
4778 UWOP_SAVE_LRP(25, 16), /* 20: stp x25, x30, [sp, #16] */
4779 UWOP_SAVE_REG(24, 8), /* 1c: str x24, [sp, #8] */
4780 UWOP_SAVE_REG_X(23, 32), /* 18: str x23, [sp, #-32]! */
4781 UWOP_SAVE_REGP(21, 16), /* 14: stp x21, x22, [sp, #16] */
4782 UWOP_SAVE_REGP_X(19, 32), /* 10: stp x19, x20, [sp, #-32]! */
4783 UWOP_SET_FP, /* 0c: mov x29, sp */
4784 UWOP_ALLOC_LARGE(65536), /* 08: sub sp, sp, #65536 */
4785 UWOP_ALLOC_MEDIUM(512), /* 04: sub sp, sp, #512 */
4786 UWOP_ALLOC_SMALL(16), /* 00: sub sp, sp, #16 */
4787 UWOP_END,
4790 static const struct results results_4[] =
4792 /* offset fp handler pc frame offset registers */
4793 { 0x00, 0x10, 0, ORIG_LR, 0x00000, TRUE, { {-1,-1} }},
4794 { 0x04, 0x10, 0, ORIG_LR, 0x00010, TRUE, { {-1,-1} }},
4795 { 0x08, 0x10, 0, ORIG_LR, 0x00210, TRUE, { {-1,-1} }},
4796 { 0x0c, 0x10, 0, ORIG_LR, 0x10210, TRUE, { {-1,-1} }},
4797 { 0x14, 0x00, 0, ORIG_LR, 0x10210, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }},
4798 { 0x18, 0x00, 0, ORIG_LR, 0x10210, TRUE, { {x19, 0x00}, {x20, 0x08}, {x21, 0x10}, {x22, 0x18}, {-1,-1} }},
4799 { 0x1c, 0x00, 0, ORIG_LR, 0x10210, TRUE, { {x19, 0x20}, {x20, 0x28}, {x21, 0x30}, {x22, 0x38}, {x23, 0x00}, {-1,-1} }},
4800 { 0x20, 0x00, 0, ORIG_LR, 0x10210, TRUE, { {x19, 0x20}, {x20, 0x28}, {x21, 0x30}, {x22, 0x38}, {x23, 0x00}, {x24, 0x08}, {-1,-1} }},
4801 { 0x24, 0x00, 0, 0x0018, 0x10210, TRUE, { {x19, 0x20}, {x20, 0x28}, {x21, 0x30}, {x22, 0x38}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {lr, 0x18}, {-1,-1} }},
4802 { 0x28, 0x00, 0, 0x0018, 0x10220, FALSE, { {x19, 0x20}, {x20, 0x28}, {x21, 0x30}, {x22, 0x38}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {lr, 0x18}, {x29, 0x10}, {-1,-1} }},
4803 { 0x2c, 0x00, 0, 0x0038, 0x10240, FALSE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x20}, {x24, 0x28}, {x25, 0x30}, {lr, 0x38}, {x29, 0x30}, {-1,-1} }},
4804 { 0x30, 0x00, 0, 0x0058, 0x10260, FALSE, { {x19, 0x60}, {x20, 0x68}, {x21, 0x70}, {x22, 0x78}, {x23, 0x40}, {x24, 0x48}, {x25, 0x50}, {lr, 0x58}, {x29, 0x50}, {-1,-1} }},
4805 { 0x34, 0x00, 0, 0x0078, 0x10280, FALSE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x60}, {x24, 0x68}, {x25, 0x70}, {lr, 0x78}, {x29, 0x70}, {d8, 0x00}, {d9, 0x08}, {-1,-1} }},
4806 { 0x38, 0x00, 0, 0x0078, 0x10280, FALSE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x60}, {x24, 0x68}, {x25, 0x70}, {lr, 0x78}, {x29, 0x70}, {d8, 0x00}, {d9, 0x08}, {d10, 0x10}, {d11, 0x18}, {-1,-1} }},
4807 { 0x3c, 0x00, 0, 0x0098, 0x102a0, FALSE, { {x19, 0xa0}, {x20, 0xa8}, {x21, 0xb0}, {x22, 0xb8}, {x23, 0x80}, {x24, 0x88}, {x25, 0x90}, {lr, 0x98}, {x29, 0x90}, {d8, 0x20}, {d9, 0x28}, {d10, 0x30}, {d11, 0x38}, {d12, 0x00}, {-1,-1} }},
4808 { 0x40, 0x00, 0, 0x0098, 0x102a0, FALSE, { {x19, 0xa0}, {x20, 0xa8}, {x21, 0xb0}, {x22, 0xb8}, {x23, 0x80}, {x24, 0x88}, {x25, 0x90}, {lr, 0x98}, {x29, 0x90}, {d8, 0x20}, {d9, 0x28}, {d10, 0x30}, {d11, 0x38}, {d12, 0x00}, {d13, 0x08}, {-1,-1} }},
4809 { 0x44, 0x20, 0, 0x00a8, 0x102b0, FALSE, { {x19, 0xb0}, {x20, 0xb8}, {x21, 0xc0}, {x22, 0xc8}, {x23, 0x90}, {x24, 0x98}, {x25, 0xa0}, {lr, 0xa8}, {x29, 0xa0}, {d8, 0x30}, {d9, 0x38}, {d10, 0x40}, {d11, 0x48}, {d12, 0x10}, {d13, 0x18}, {-1,-1} }},
4812 static const BYTE function_5[] =
4814 0xf3, 0x53, 0xbe, 0xa9, /* 00: stp x19, x20, [sp, #-32]! */
4815 0xf5, 0x5b, 0x01, 0xa9, /* 04: stp x21, x22, [sp, #16] */
4816 0xf7, 0x63, 0xbc, 0xa9, /* 08: stp x23, x24, [sp, #-64]! */
4817 0xf9, 0x6b, 0x01, 0xa9, /* 0c: stp x25, x26, [sp, #16] */
4818 0xfb, 0x73, 0x02, 0xa9, /* 10: stp x27, x28, [sp, #32] */
4819 0xfd, 0x7b, 0x03, 0xa9, /* 14: stp x29, x30, [sp, #48] */
4820 0xe8, 0x27, 0xbc, 0x6d, /* 18: stp d8, d9, [sp, #-64]! */
4821 0xea, 0x2f, 0x01, 0x6d, /* 1c: stp d10, d11, [sp, #16] */
4822 0xec, 0x37, 0x02, 0x6d, /* 20: stp d12, d13, [sp, #32] */
4823 0xee, 0x3f, 0x03, 0x6d, /* 24: stp d14, d15, [sp, #48] */
4824 0xc0, 0x03, 0x5f, 0xd6, /* 28: ret */
4827 static const DWORD unwind_info_5_header =
4828 (sizeof(function_5)/4) | /* function length */
4829 (0 << 20) | /* X */
4830 (0 << 21) | /* E */
4831 (0 << 22) | /* epilog */
4832 (4 << 27); /* codes */
4834 static const BYTE unwind_info_5[] =
4836 DW(unwind_info_5_header),
4838 UWOP_SAVE_NEXT, /* 24: stp d14, d15, [sp, #48] */
4839 UWOP_SAVE_FREGP(12, 32), /* 20: stp d12, d13, [sp, #32] */
4840 UWOP_SAVE_NEXT, /* 1c: stp d10, d11, [sp, #16] */
4841 UWOP_SAVE_FREGP_X(8, 64), /* 18: stp d8, d9, [sp, #-64]! */
4842 UWOP_SAVE_NEXT, /* 14: stp x29, x30, [sp, #48] */
4843 UWOP_SAVE_REGP(27, 32), /* 10: stp x27, x28, [sp, #32] */
4844 UWOP_SAVE_NEXT, /* 0c: stp x25, x26, [sp, #16] */
4845 UWOP_SAVE_REGP_X(23, 64), /* 08: stp x23, x24, [sp, #-64]! */
4846 UWOP_SAVE_NEXT, /* 04: stp x21, x22, [sp, #16] */
4847 UWOP_SAVE_R19R20_X(32), /* 00: stp x19, x20, [sp, #-32]! */
4848 UWOP_END,
4849 UWOP_NOP /* padding */
4852 /* Windows seems to only save one register for UWOP_SAVE_NEXT for
4853 * float registers, contrary to what the documentation says. The tests
4854 * for those cases are commented out; they succeed in wine but fail
4855 * on native windows. */
4856 static const struct results results_5[] =
4858 /* offset fp handler pc frame offset registers */
4859 { 0x00, 0x00, 0, ORIG_LR, 0x00000, TRUE, { {-1,-1} }},
4860 { 0x04, 0x00, 0, ORIG_LR, 0x00020, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }},
4861 { 0x08, 0x00, 0, ORIG_LR, 0x00020, TRUE, { {x19, 0x00}, {x20, 0x08}, {x21, 0x10}, {x22, 0x18}, {-1,-1} }},
4862 { 0x0c, 0x00, 0, ORIG_LR, 0x00060, TRUE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x00}, {x24, 0x08}, {-1,-1} }},
4863 { 0x10, 0x00, 0, ORIG_LR, 0x00060, TRUE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {x26, 0x18}, {-1,-1} }},
4864 { 0x14, 0x00, 0, ORIG_LR, 0x00060, TRUE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {x26, 0x18}, {x27, 0x20}, {x28, 0x28}, {-1,-1} }},
4865 { 0x18, 0x00, 0, 0x38, 0x00060, TRUE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {x26, 0x18}, {x27, 0x20}, {x28, 0x28}, {x29, 0x30}, {lr, 0x38}, {-1,-1} }},
4866 { 0x1c, 0x00, 0, 0x78, 0x000a0, TRUE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x40}, {x24, 0x48}, {x25, 0x50}, {x26, 0x58}, {x27, 0x60}, {x28, 0x68}, {x29, 0x70}, {lr, 0x78}, {d8, 0x00}, {d9, 0x08}, {-1,-1} }},
4867 #if 0
4868 { 0x20, 0x00, 0, 0x78, 0x000a0, TRUE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x40}, {x24, 0x48}, {x25, 0x50}, {x26, 0x58}, {x27, 0x60}, {x28, 0x68}, {x29, 0x70}, {lr, 0x78}, {d8, 0x00}, {d9, 0x08}, {d10, 0x10}, {d11, 0x18}, {-1,-1} }},
4869 { 0x24, 0x00, 0, 0x78, 0x000a0, TRUE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x40}, {x24, 0x48}, {x25, 0x50}, {x26, 0x58}, {x27, 0x60}, {x28, 0x68}, {x29, 0x70}, {lr, 0x78}, {d8, 0x00}, {d9, 0x08}, {d10, 0x10}, {d11, 0x18}, {d12, 0x20}, {d13, 0x28}, {-1,-1} }},
4870 { 0x28, 0x00, 0, 0x78, 0x000a0, TRUE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x40}, {x24, 0x48}, {x25, 0x50}, {x26, 0x58}, {x27, 0x60}, {x28, 0x68}, {x29, 0x70}, {lr, 0x78}, {d8, 0x00}, {d9, 0x08}, {d10, 0x10}, {d11, 0x18}, {d12, 0x20}, {d13, 0x28}, {d14, 0x30}, {d15, 0x38}, {-1,-1} }},
4871 #endif
4874 static const BYTE function_6[] =
4876 0xf3, 0x53, 0xbd, 0xa9, /* 00: stp x19, x20, [sp, #-48]! */
4877 0xf5, 0x0b, 0x00, 0xf9, /* 04: str x21, [sp, #16] */
4878 0xe8, 0xa7, 0x01, 0x6d, /* 08: stp d8, d9, [sp, #24] */
4879 0xea, 0x17, 0x00, 0xfd, /* 0c: str d10, [sp, #40] */
4880 0xff, 0x03, 0x00, 0xd1, /* 10: sub sp, sp, #0 */
4881 0x1f, 0x20, 0x03, 0xd5, /* 14: nop */
4882 0xff, 0x03, 0x00, 0x91, /* 18: add sp, sp, #0 */
4883 0xea, 0x17, 0x40, 0xfd, /* 1c: ldr d10, [sp, #40] */
4884 0xe8, 0xa7, 0x41, 0x6d, /* 20: ldp d8, d9, [sp, #24] */
4885 0xf5, 0x0b, 0x40, 0xf9, /* 24: ldr x21, [sp, #16] */
4886 0xf3, 0x53, 0xc3, 0xa8, /* 28: ldp x19, x20, [sp], #48 */
4887 0xc0, 0x03, 0x5f, 0xd6, /* 2c: ret */
4890 static const DWORD unwind_info_6_packed =
4891 (1 << 0) | /* Flag */
4892 (sizeof(function_6)/4 << 2) | /* FunctionLength */
4893 (2 << 13) | /* RegF */
4894 (3 << 16) | /* RegI */
4895 (0 << 20) | /* H */
4896 (0 << 21) | /* CR */
4897 (3 << 23); /* FrameSize */
4899 static const BYTE unwind_info_6[] = { DW(unwind_info_6_packed) };
4901 static const struct results results_6[] =
4903 /* offset fp handler pc frame offset registers */
4904 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
4905 { 0x04, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {-1,-1} }},
4906 { 0x08, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {-1,-1} }},
4907 { 0x0c, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {-1,-1} }},
4908 { 0x10, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {d10, 0x28}, {-1,-1} }},
4909 { 0x14, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {d10, 0x28}, {-1,-1} }},
4910 { 0x18, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {d10, 0x28}, {-1,-1} }},
4911 { 0x1c, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {d10, 0x28}, {-1,-1} }},
4912 { 0x20, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {-1,-1} }},
4913 { 0x24, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {-1,-1} }},
4914 { 0x28, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {-1,-1} }},
4915 { 0x2c, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
4918 static const BYTE function_7[] =
4920 0xf3, 0x0f, 0x1d, 0xf8, /* 00: str x19, [sp, #-48]! */
4921 0xe8, 0xa7, 0x00, 0x6d, /* 04: stp d8, d9, [sp, #8] */
4922 0xea, 0xaf, 0x01, 0x6d, /* 08: stp d10, d11, [sp, #24] */
4923 0xff, 0x03, 0x00, 0xd1, /* 0c: sub sp, sp, #0 */
4924 0x1f, 0x20, 0x03, 0xd5, /* 10: nop */
4925 0xff, 0x03, 0x00, 0x91, /* 14: add sp, sp, #0 */
4926 0xea, 0xaf, 0x41, 0x6d, /* 18: ldp d10, d11, [sp, #24] */
4927 0xe8, 0xa7, 0x40, 0x6d, /* 1c: ldp d8, d9, [sp, #8] */
4928 0xf3, 0x07, 0x43, 0xf8, /* 20: ldr x19, [sp], #48 */
4929 0xc0, 0x03, 0x5f, 0xd6, /* 24: ret */
4932 static const DWORD unwind_info_7_packed =
4933 (1 << 0) | /* Flag */
4934 (sizeof(function_7)/4 << 2) | /* FunctionLength */
4935 (3 << 13) | /* RegF */
4936 (1 << 16) | /* RegI */
4937 (0 << 20) | /* H */
4938 (0 << 21) | /* CR */
4939 (3 << 23); /* FrameSize */
4941 static const BYTE unwind_info_7[] = { DW(unwind_info_7_packed) };
4943 static const struct results results_7[] =
4945 /* offset fp handler pc frame offset registers */
4946 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
4947 { 0x04, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {-1,-1} }},
4948 { 0x08, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {-1,-1} }},
4949 { 0x0c, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {d10, 0x18}, {d11, 0x20}, {-1,-1} }},
4950 { 0x10, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {d10, 0x18}, {d11, 0x20}, {-1,-1} }},
4951 { 0x14, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {d10, 0x18}, {d11, 0x20}, {-1,-1} }},
4952 { 0x18, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {d10, 0x18}, {d11, 0x20}, {-1,-1} }},
4953 { 0x1c, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {-1,-1} }},
4954 { 0x20, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {-1,-1} }},
4955 { 0x24, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
4958 static const BYTE function_8[] =
4960 0xe8, 0x27, 0xbf, 0x6d, /* 00: stp d8, d9, [sp, #-16]! */
4961 0xff, 0x83, 0x00, 0xd1, /* 04: sub sp, sp, #32 */
4962 0x1f, 0x20, 0x03, 0xd5, /* 08: nop */
4963 0xff, 0x83, 0x00, 0x91, /* 0c: add sp, sp, #32 */
4964 0xe8, 0x27, 0xc1, 0x6c, /* 10: ldp d8, d9, [sp], #16 */
4965 0xc0, 0x03, 0x5f, 0xd6, /* 14: ret */
4968 static const DWORD unwind_info_8_packed =
4969 (1 << 0) | /* Flag */
4970 (sizeof(function_8)/4 << 2) | /* FunctionLength */
4971 (1 << 13) | /* RegF */
4972 (0 << 16) | /* RegI */
4973 (0 << 20) | /* H */
4974 (0 << 21) | /* CR */
4975 (3 << 23); /* FrameSize */
4977 static const BYTE unwind_info_8[] = { DW(unwind_info_8_packed) };
4979 static const struct results results_8[] =
4981 /* offset fp handler pc frame offset registers */
4982 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
4983 { 0x04, 0x00, 0, ORIG_LR, 0x010, TRUE, { {d8, 0x00}, {d9, 0x08}, {-1,-1} }},
4984 { 0x08, 0x00, 0, ORIG_LR, 0x030, TRUE, { {d8, 0x20}, {d9, 0x28}, {-1,-1} }},
4985 { 0x0c, 0x00, 0, ORIG_LR, 0x030, TRUE, { {d8, 0x20}, {d9, 0x28}, {-1,-1} }},
4986 { 0x10, 0x00, 0, ORIG_LR, 0x010, TRUE, { {d8, 0x00}, {d9, 0x08}, {-1,-1} }},
4987 { 0x14, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
4990 static const BYTE function_9[] =
4992 0xf3, 0x0f, 0x1b, 0xf8, /* 00: str x19, [sp, #-80]! */
4993 0xe0, 0x87, 0x00, 0xa9, /* 04: stp x0, x1, [sp, #8] */
4994 0xe2, 0x8f, 0x01, 0xa9, /* 08: stp x2, x3, [sp, #24] */
4995 0xe4, 0x97, 0x02, 0xa9, /* 0c: stp x4, x5, [sp, #40] */
4996 0xe6, 0x9f, 0x03, 0xa9, /* 10: stp x6, x7, [sp, #56] */
4997 0xff, 0x83, 0x00, 0xd1, /* 14: sub sp, sp, #32 */
4998 0x1f, 0x20, 0x03, 0xd5, /* 18: nop */
4999 0xff, 0x83, 0x00, 0x91, /* 1c: add sp, sp, #32 */
5000 0x1f, 0x20, 0x03, 0xd5, /* 20: nop */
5001 0x1f, 0x20, 0x03, 0xd5, /* 24: nop */
5002 0x1f, 0x20, 0x03, 0xd5, /* 28: nop */
5003 0x1f, 0x20, 0x03, 0xd5, /* 2c: nop */
5004 0xf3, 0x0f, 0x1b, 0xf8, /* 30: ldr x19, [sp], #80 */
5005 0xc0, 0x03, 0x5f, 0xd6, /* 34: ret */
5008 static const DWORD unwind_info_9_packed =
5009 (1 << 0) | /* Flag */
5010 (sizeof(function_9)/4 << 2) | /* FunctionLength */
5011 (0 << 13) | /* RegF */
5012 (1 << 16) | /* RegI */
5013 (1 << 20) | /* H */
5014 (0 << 21) | /* CR */
5015 (7 << 23); /* FrameSize */
5017 static const BYTE unwind_info_9[] = { DW(unwind_info_9_packed) };
5019 static const struct results results_9[] =
5021 /* offset fp handler pc frame offset registers */
5022 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
5023 { 0x04, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }},
5024 { 0x08, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }},
5025 { 0x0c, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }},
5026 { 0x10, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }},
5027 { 0x14, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }},
5028 { 0x18, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }},
5029 { 0x1c, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }},
5030 { 0x20, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }},
5031 { 0x24, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }},
5032 { 0x28, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }},
5033 { 0x2c, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }},
5034 { 0x30, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }},
5035 { 0x34, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
5038 static const BYTE function_10[] =
5040 0xfe, 0x0f, 0x1f, 0xf8, /* 00: str lr, [sp, #-16]! */
5041 0xff, 0x43, 0x00, 0xd1, /* 04: sub sp, sp, #16 */
5042 0x1f, 0x20, 0x03, 0xd5, /* 08: nop */
5043 0xff, 0x43, 0x00, 0x91, /* 0c: add sp, sp, #16 */
5044 0xfe, 0x07, 0x41, 0xf8, /* 10: ldr lr, [sp], #16 */
5045 0xc0, 0x03, 0x5f, 0xd6, /* 14: ret */
5048 static const DWORD unwind_info_10_packed =
5049 (1 << 0) | /* Flag */
5050 (sizeof(function_10)/4 << 2) | /* FunctionLength */
5051 (0 << 13) | /* RegF */
5052 (0 << 16) | /* RegI */
5053 (0 << 20) | /* H */
5054 (1 << 21) | /* CR */
5055 (2 << 23); /* FrameSize */
5057 static const BYTE unwind_info_10[] = { DW(unwind_info_10_packed) };
5059 static const struct results results_10[] =
5061 /* offset fp handler pc frame offset registers */
5062 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
5063 { 0x04, 0x00, 0, 0x00, 0x010, TRUE, { {lr, 0x00}, {-1,-1} }},
5064 { 0x08, 0x00, 0, 0x10, 0x020, TRUE, { {lr, 0x10}, {-1,-1} }},
5065 { 0x0c, 0x00, 0, 0x10, 0x020, TRUE, { {lr, 0x10}, {-1,-1} }},
5066 { 0x10, 0x00, 0, 0x00, 0x010, TRUE, { {lr, 0x00}, {-1,-1} }},
5067 { 0x14, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
5070 static const BYTE function_11[] =
5072 0xf3, 0x53, 0xbe, 0xa9, /* 00: stp x19, x20, [sp, #-32]! */
5073 0xf5, 0x7b, 0x01, 0xa9, /* 04: stp x21, lr, [sp, #16] */
5074 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */
5075 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */
5076 0xff, 0x43, 0x00, 0x91, /* 10: add sp, sp, #16 */
5077 0xf5, 0x7b, 0x41, 0xa9, /* 14: ldp x21, lr, [sp, #16] */
5078 0xf3, 0x53, 0xc2, 0xa8, /* 18: ldp x19, x20, [sp], #32 */
5079 0xc0, 0x03, 0x5f, 0xd6, /* 1c: ret */
5082 static const DWORD unwind_info_11_packed =
5083 (1 << 0) | /* Flag */
5084 (sizeof(function_11)/4 << 2) | /* FunctionLength */
5085 (0 << 13) | /* RegF */
5086 (3 << 16) | /* RegI */
5087 (0 << 20) | /* H */
5088 (1 << 21) | /* CR */
5089 (3 << 23); /* FrameSize */
5091 static const BYTE unwind_info_11[] = { DW(unwind_info_11_packed) };
5093 static const struct results results_11[] =
5095 /* offset fp handler pc frame offset registers */
5096 { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
5097 { 0x04, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }},
5098 { 0x08, 0x00, 0, 0x18, 0x020, TRUE, { {x19, 0x00}, {x20, 0x08}, {x21, 0x10}, {lr, 0x18}, {-1,-1} }},
5099 { 0x0c, 0x00, 0, 0x28, 0x030, TRUE, { {x19, 0x10}, {x20, 0x18}, {x21, 0x20}, {lr, 0x28}, {-1,-1} }},
5100 { 0x10, 0x00, 0, 0x28, 0x030, TRUE, { {x19, 0x10}, {x20, 0x18}, {x21, 0x20}, {lr, 0x28}, {-1,-1} }},
5101 { 0x14, 0x00, 0, 0x18, 0x020, TRUE, { {x19, 0x00}, {x20, 0x08}, {x21, 0x10}, {lr, 0x18}, {-1,-1} }},
5102 { 0x18, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }},
5103 { 0x1c, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
5106 static const BYTE function_12[] =
5108 0xf3, 0x53, 0xbf, 0xa9, /* 00: stp x19, x20, [sp, #-16]! */
5109 0xfd, 0x7b, 0xbe, 0xa9, /* 04: stp x29, lr, [sp, #-32]! */
5110 0xfd, 0x03, 0x00, 0x91, /* 08: mov x29, sp */
5111 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */
5112 0xbf, 0x03, 0x00, 0x91, /* 10: mov sp, x29 */
5113 0xfd, 0x7b, 0xc2, 0xa8, /* 14: ldp x29, lr, [sp], #32 */
5114 0xf3, 0x53, 0xc1, 0xa8, /* 18: ldp x19, x20, [sp], #16 */
5115 0xc0, 0x03, 0x5f, 0xd6, /* 1c: ret */
5118 static const DWORD unwind_info_12_packed =
5119 (1 << 0) | /* Flag */
5120 (sizeof(function_12)/4 << 2) | /* FunctionLength */
5121 (0 << 13) | /* RegF */
5122 (2 << 16) | /* RegI */
5123 (0 << 20) | /* H */
5124 (3 << 21) | /* CR */
5125 (3 << 23); /* FrameSize */
5127 static const BYTE unwind_info_12[] = { DW(unwind_info_12_packed) };
5129 static const struct results results_12[] =
5131 /* offset fp handler pc frame offset registers */
5132 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
5133 { 0x04, 0x10, 0, ORIG_LR, 0x010, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }},
5134 { 0x08, 0x10, 0, 0x08, 0x030, TRUE, { {x19, 0x20}, {x20, 0x28}, {x29, 0x00}, {lr, 0x08}, {-1,-1} }},
5135 { 0x0c, 0x10, 0, 0x18, 0x040, TRUE, { {x19, 0x30}, {x20, 0x38}, {x29, 0x10}, {lr, 0x18}, {-1,-1} }},
5136 { 0x10, 0x10, 0, 0x18, 0x040, TRUE, { {x19, 0x30}, {x20, 0x38}, {x29, 0x10}, {lr, 0x18}, {-1,-1} }},
5137 { 0x14, 0x10, 0, 0x08, 0x030, TRUE, { {x19, 0x20}, {x20, 0x28}, {x29, 0x00}, {lr, 0x08}, {-1,-1} }},
5138 { 0x18, 0x10, 0, ORIG_LR, 0x010, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }},
5139 { 0x1c, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
5142 static const BYTE function_13[] =
5144 0xf3, 0x53, 0xbf, 0xa9, /* 00: stp x19, x20, [sp, #-16]! */
5145 0xff, 0x43, 0x08, 0xd1, /* 04: sub sp, sp, #528 */
5146 0xfd, 0x7b, 0x00, 0xd1, /* 08: stp x29, lr, [sp] */
5147 0xfd, 0x03, 0x00, 0x91, /* 0c: mov x29, sp */
5148 0x1f, 0x20, 0x03, 0xd5, /* 10: nop */
5149 0xbf, 0x03, 0x00, 0x91, /* 14: mov sp, x29 */
5150 0xfd, 0x7b, 0x40, 0xa9, /* 18: ldp x29, lr, [sp] */
5151 0xff, 0x43, 0x08, 0x91, /* 1c: add sp, sp, #528 */
5152 0xf3, 0x53, 0xc1, 0xa8, /* 20: ldp x19, x20, [sp], #16 */
5153 0xc0, 0x03, 0x5f, 0xd6, /* 24: ret */
5156 static const DWORD unwind_info_13_packed =
5157 (1 << 0) | /* Flag */
5158 (sizeof(function_13)/4 << 2) | /* FunctionLength */
5159 (0 << 13) | /* RegF */
5160 (2 << 16) | /* RegI */
5161 (0 << 20) | /* H */
5162 (3 << 21) | /* CR */
5163 (34 << 23); /* FrameSize */
5165 static const BYTE unwind_info_13[] = { DW(unwind_info_13_packed) };
5167 static const struct results results_13[] =
5169 /* offset fp handler pc frame offset registers */
5170 { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
5171 { 0x04, 0x10, 0, ORIG_LR, 0x010, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }},
5172 { 0x08, 0x10, 0, ORIG_LR, 0x220, TRUE, { {x19, 0x210}, {x20, 0x218}, {-1,-1} }},
5173 { 0x0c, 0x10, 0, 0x08, 0x220, TRUE, { {x19, 0x210}, {x20, 0x218}, {x29, 0x00}, {lr, 0x08}, {-1,-1} }},
5174 { 0x10, 0x10, 0, 0x18, 0x230, TRUE, { {x19, 0x220}, {x20, 0x228}, {x29, 0x10}, {lr, 0x18}, {-1,-1} }},
5175 { 0x14, 0x10, 0, 0x18, 0x230, TRUE, { {x19, 0x220}, {x20, 0x228}, {x29, 0x10}, {lr, 0x18}, {-1,-1} }},
5176 { 0x18, 0x10, 0, 0x08, 0x220, TRUE, { {x19, 0x210}, {x20, 0x218}, {x29, 0x00}, {lr, 0x08}, {-1,-1} }},
5177 { 0x1c, 0x10, 0, ORIG_LR, 0x220, TRUE, { {x19, 0x210}, {x20, 0x218}, {-1,-1} }},
5178 { 0x20, 0x10, 0, ORIG_LR, 0x010, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }},
5179 { 0x24, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }},
5182 static const struct unwind_test tests[] =
5184 #define TEST(func, unwind, unwind_packed, results) \
5185 { func, sizeof(func), unwind, unwind_packed ? 0 : sizeof(unwind), results, ARRAY_SIZE(results) }
5186 TEST(function_0, unwind_info_0, 0, results_0),
5187 TEST(function_1, unwind_info_1, 1, results_1),
5188 TEST(function_2, unwind_info_2, 0, results_2),
5189 TEST(function_3, unwind_info_3, 0, results_3),
5190 TEST(function_4, unwind_info_4, 0, results_4),
5191 TEST(function_5, unwind_info_5, 0, results_5),
5192 TEST(function_6, unwind_info_6, 1, results_6),
5193 TEST(function_7, unwind_info_7, 1, results_7),
5194 TEST(function_8, unwind_info_8, 1, results_8),
5195 TEST(function_9, unwind_info_9, 1, results_9),
5196 TEST(function_10, unwind_info_10, 1, results_10),
5197 TEST(function_11, unwind_info_11, 1, results_11),
5198 TEST(function_12, unwind_info_12, 1, results_12),
5199 TEST(function_13, unwind_info_13, 1, results_13),
5200 #undef TEST
5202 unsigned int i;
5204 for (i = 0; i < ARRAY_SIZE(tests); i++)
5205 call_virtual_unwind( i, &tests[i] );
5208 static void test_thread_context(void)
5210 CONTEXT context;
5211 NTSTATUS status;
5212 struct expected
5214 ULONG64 X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16,
5215 X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, Fp, Lr, Sp, Pc;
5216 ULONG Cpsr;
5217 } expect;
5218 NTSTATUS (*func_ptr)( void *arg1, void *arg2, struct expected *res, void *func ) = (void *)code_mem;
5220 static const DWORD call_func[] =
5222 0xa9bf7bfd, /* stp x29, x30, [sp, #-16]! */
5223 0xa9000440, /* stp x0, x1, [x2] */
5224 0xa9010c42, /* stp x2, x3, [x2, #16] */
5225 0xa9021444, /* stp x4, x5, [x2, #32] */
5226 0xa9031c46, /* stp x6, x7, [x2, #48] */
5227 0xa9042448, /* stp x8, x9, [x2, #64] */
5228 0xa9052c4a, /* stp x10, x11, [x2, #80] */
5229 0xa906344c, /* stp x12, x13, [x2, #96] */
5230 0xa9073c4e, /* stp x14, x15, [x2, #112] */
5231 0xa9084450, /* stp x16, x17, [x2, #128] */
5232 0xa9094c52, /* stp x18, x19, [x2, #144] */
5233 0xa90a5454, /* stp x20, x21, [x2, #160] */
5234 0xa90b5c56, /* stp x22, x23, [x2, #176] */
5235 0xa90c6458, /* stp x24, x25, [x2, #192] */
5236 0xa90d6c5a, /* stp x26, x27, [x2, #208] */
5237 0xa90e745c, /* stp x28, x29, [x2, #224] */
5238 0xf900785e, /* str x30, [x2, #240] */
5239 0x910003e1, /* mov x1, sp */
5240 0xf9007c41, /* str x1, [x2, #248] */
5241 0x90000001, /* adrp x1, 1f */
5242 0x9101a021, /* add x1, x1, #:lo12:1f */
5243 0xf9008041, /* str x1, [x2, #256] */
5244 0xd53b4201, /* mrs x1, nzcv */
5245 0xb9010841, /* str w1, [x2, #264] */
5246 0xf9400441, /* ldr x1, [x2, #8] */
5247 0xd63f0060, /* blr x3 */
5248 0xa8c17bfd, /* 1: ldp x29, x30, [sp], #16 */
5249 0xd65f03c0, /* ret */
5252 memcpy( func_ptr, call_func, sizeof(call_func) );
5254 #define COMPARE(reg) \
5255 ok( context.reg == expect.reg, "wrong " #reg " %p/%p\n", (void *)(ULONG64)context.reg, (void *)(ULONG64)expect.reg )
5257 memset( &context, 0xcc, sizeof(context) );
5258 memset( &expect, 0xcc, sizeof(expect) );
5259 func_ptr( &context, 0, &expect, pRtlCaptureContext );
5260 trace( "expect: x0=%p x1=%p x2=%p x3=%p x4=%p x5=%p x6=%p x7=%p x8=%p x9=%p x10=%p x11=%p x12=%p x13=%p x14=%p x15=%p x16=%p x17=%p x18=%p x19=%p x20=%p x21=%p x22=%p x23=%p x24=%p x25=%p x26=%p x27=%p x28=%p fp=%p lr=%p sp=%p pc=%p cpsr=%08x\n",
5261 (void *)expect.X0, (void *)expect.X1, (void *)expect.X2, (void *)expect.X3,
5262 (void *)expect.X4, (void *)expect.X5, (void *)expect.X6, (void *)expect.X7,
5263 (void *)expect.X8, (void *)expect.X9, (void *)expect.X10, (void *)expect.X11,
5264 (void *)expect.X12, (void *)expect.X13, (void *)expect.X14, (void *)expect.X15,
5265 (void *)expect.X16, (void *)expect.X17, (void *)expect.X18, (void *)expect.X19,
5266 (void *)expect.X20, (void *)expect.X21, (void *)expect.X22, (void *)expect.X23,
5267 (void *)expect.X24, (void *)expect.X25, (void *)expect.X26, (void *)expect.X27,
5268 (void *)expect.X28, (void *)expect.Fp, (void *)expect.Lr, (void *)expect.Sp,
5269 (void *)expect.Pc, expect.Cpsr );
5270 trace( "actual: x0=%p x1=%p x2=%p x3=%p x4=%p x5=%p x6=%p x7=%p x8=%p x9=%p x10=%p x11=%p x12=%p x13=%p x14=%p x15=%p x16=%p x17=%p x18=%p x19=%p x20=%p x21=%p x22=%p x23=%p x24=%p x25=%p x26=%p x27=%p x28=%p fp=%p lr=%p sp=%p pc=%p cpsr=%08x\n",
5271 (void *)context.X0, (void *)context.X1, (void *)context.X2, (void *)context.X3,
5272 (void *)context.X4, (void *)context.X5, (void *)context.X6, (void *)context.X7,
5273 (void *)context.X8, (void *)context.X9, (void *)context.X10, (void *)context.X11,
5274 (void *)context.X12, (void *)context.X13, (void *)context.X14, (void *)context.X15,
5275 (void *)context.X16, (void *)context.X17, (void *)context.X18, (void *)context.X19,
5276 (void *)context.X20, (void *)context.X21, (void *)context.X22, (void *)context.X23,
5277 (void *)context.X24, (void *)context.X25, (void *)context.X26, (void *)context.X27,
5278 (void *)context.X28, (void *)context.Fp, (void *)context.Lr, (void *)context.Sp,
5279 (void *)context.Pc, context.Cpsr );
5281 ok( context.ContextFlags == CONTEXT_FULL,
5282 "wrong flags %08x\n", context.ContextFlags );
5283 COMPARE( X0 );
5284 COMPARE( X1 );
5285 COMPARE( X2 );
5286 COMPARE( X3 );
5287 COMPARE( X4 );
5288 COMPARE( X5 );
5289 COMPARE( X6 );
5290 COMPARE( X7 );
5291 COMPARE( X8 );
5292 COMPARE( X9 );
5293 COMPARE( X10 );
5294 COMPARE( X11 );
5295 COMPARE( X12 );
5296 COMPARE( X13 );
5297 COMPARE( X14 );
5298 COMPARE( X15 );
5299 COMPARE( X16 );
5300 COMPARE( X17 );
5301 COMPARE( X18 );
5302 COMPARE( X19 );
5303 COMPARE( X20 );
5304 COMPARE( X21 );
5305 COMPARE( X22 );
5306 COMPARE( X23 );
5307 COMPARE( X24 );
5308 COMPARE( X25 );
5309 COMPARE( X26 );
5310 COMPARE( X27 );
5311 COMPARE( X28 );
5312 COMPARE( Fp );
5313 COMPARE( Sp );
5314 COMPARE( Pc );
5315 COMPARE( Cpsr );
5316 ok( context.Lr == expect.Pc, "wrong Lr %p/%p\n", (void *)context.Lr, (void *)expect.Pc );
5318 memset( &context, 0xcc, sizeof(context) );
5319 memset( &expect, 0xcc, sizeof(expect) );
5320 context.ContextFlags = CONTEXT_FULL;
5322 status = func_ptr( GetCurrentThread(), &context, &expect, pNtGetContextThread );
5323 ok( status == STATUS_SUCCESS, "NtGetContextThread failed %08x\n", status );
5324 trace( "expect: x0=%p x1=%p x2=%p x3=%p x4=%p x5=%p x6=%p x7=%p x8=%p x9=%p x10=%p x11=%p x12=%p x13=%p x14=%p x15=%p x16=%p x17=%p x18=%p x19=%p x20=%p x21=%p x22=%p x23=%p x24=%p x25=%p x26=%p x27=%p x28=%p fp=%p lr=%p sp=%p pc=%p cpsr=%08x\n",
5325 (void *)expect.X0, (void *)expect.X1, (void *)expect.X2, (void *)expect.X3,
5326 (void *)expect.X4, (void *)expect.X5, (void *)expect.X6, (void *)expect.X7,
5327 (void *)expect.X8, (void *)expect.X9, (void *)expect.X10, (void *)expect.X11,
5328 (void *)expect.X12, (void *)expect.X13, (void *)expect.X14, (void *)expect.X15,
5329 (void *)expect.X16, (void *)expect.X17, (void *)expect.X18, (void *)expect.X19,
5330 (void *)expect.X20, (void *)expect.X21, (void *)expect.X22, (void *)expect.X23,
5331 (void *)expect.X24, (void *)expect.X25, (void *)expect.X26, (void *)expect.X27,
5332 (void *)expect.X28, (void *)expect.Fp, (void *)expect.Lr, (void *)expect.Sp,
5333 (void *)expect.Pc, expect.Cpsr );
5334 trace( "actual: x0=%p x1=%p x2=%p x3=%p x4=%p x5=%p x6=%p x7=%p x8=%p x9=%p x10=%p x11=%p x12=%p x13=%p x14=%p x15=%p x16=%p x17=%p x18=%p x19=%p x20=%p x21=%p x22=%p x23=%p x24=%p x25=%p x26=%p x27=%p x28=%p fp=%p lr=%p sp=%p pc=%p cpsr=%08x\n",
5335 (void *)context.X0, (void *)context.X1, (void *)context.X2, (void *)context.X3,
5336 (void *)context.X4, (void *)context.X5, (void *)context.X6, (void *)context.X7,
5337 (void *)context.X8, (void *)context.X9, (void *)context.X10, (void *)context.X11,
5338 (void *)context.X12, (void *)context.X13, (void *)context.X14, (void *)context.X15,
5339 (void *)context.X16, (void *)context.X17, (void *)context.X18, (void *)context.X19,
5340 (void *)context.X20, (void *)context.X21, (void *)context.X22, (void *)context.X23,
5341 (void *)context.X24, (void *)context.X25, (void *)context.X26, (void *)context.X27,
5342 (void *)context.X28, (void *)context.Fp, (void *)context.Lr, (void *)context.Sp,
5343 (void *)context.Pc, context.Cpsr );
5344 /* other registers are not preserved */
5345 todo_wine COMPARE( X18 );
5346 COMPARE( X19 );
5347 COMPARE( X20 );
5348 COMPARE( X21 );
5349 COMPARE( X22 );
5350 COMPARE( X23 );
5351 COMPARE( X24 );
5352 COMPARE( X25 );
5353 COMPARE( X26 );
5354 COMPARE( X27 );
5355 COMPARE( X28 );
5356 COMPARE( Fp );
5357 ok( context.Lr == expect.Pc, "wrong Lr %p/%p\n", (void *)context.Lr, (void *)expect.Pc );
5358 ok( context.Sp == expect.Sp - 16,
5359 "wrong Sp %p/%p\n", (void *)context.Sp, (void *)(expect.Sp - 16) );
5360 /* Pc is somewhere close to the NtGetContextThread implementation */
5361 ok( (char *)context.Pc >= (char *)pNtGetContextThread - 0x40000 &&
5362 (char *)context.Pc <= (char *)pNtGetContextThread + 0x40000,
5363 "wrong Pc %p/%p\n", (void *)context.Pc, pNtGetContextThread );
5364 #undef COMPARE
5367 static void test_debugger(void)
5369 char cmdline[MAX_PATH];
5370 PROCESS_INFORMATION pi;
5371 STARTUPINFOA si = { 0 };
5372 DEBUG_EVENT de;
5373 DWORD continuestatus;
5374 PVOID code_mem_address = NULL;
5375 NTSTATUS status;
5376 SIZE_T size_read;
5377 BOOL ret;
5378 int counter = 0;
5379 si.cb = sizeof(si);
5381 if(!pNtGetContextThread || !pNtSetContextThread || !pNtReadVirtualMemory || !pNtTerminateProcess)
5383 skip("NtGetContextThread, NtSetContextThread, NtReadVirtualMemory or NtTerminateProcess not found\n");
5384 return;
5387 sprintf(cmdline, "%s %s %s %p", my_argv[0], my_argv[1], "debuggee", &test_stage);
5388 ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi);
5389 ok(ret, "could not create child process error: %u\n", GetLastError());
5390 if (!ret)
5391 return;
5395 continuestatus = DBG_CONTINUE;
5396 ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n");
5398 ret = ContinueDebugEvent(de.dwProcessId, de.dwThreadId, 0xdeadbeef);
5399 ok(!ret, "ContinueDebugEvent unexpectedly succeeded\n");
5400 ok(GetLastError() == ERROR_INVALID_PARAMETER, "Unexpected last error: %u\n", GetLastError());
5402 if (de.dwThreadId != pi.dwThreadId)
5404 trace("event %d not coming from main thread, ignoring\n", de.dwDebugEventCode);
5405 ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE);
5406 continue;
5409 if (de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT)
5411 if(de.u.CreateProcessInfo.lpBaseOfImage != NtCurrentTeb()->Peb->ImageBaseAddress)
5413 skip("child process loaded at different address, terminating it\n");
5414 pNtTerminateProcess(pi.hProcess, 0);
5417 else if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT)
5419 CONTEXT ctx;
5420 int stage;
5422 counter++;
5423 status = pNtReadVirtualMemory(pi.hProcess, &code_mem, &code_mem_address,
5424 sizeof(code_mem_address), &size_read);
5425 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
5426 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
5427 sizeof(stage), &size_read);
5428 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
5430 ctx.ContextFlags = CONTEXT_FULL;
5431 status = pNtGetContextThread(pi.hThread, &ctx);
5432 ok(!status, "NtGetContextThread failed with 0x%x\n", status);
5434 trace("exception 0x%x at %p firstchance=%d pc=%p, x0=%p\n",
5435 de.u.Exception.ExceptionRecord.ExceptionCode,
5436 de.u.Exception.ExceptionRecord.ExceptionAddress,
5437 de.u.Exception.dwFirstChance, (char *)ctx.Pc, (char *)ctx.X0);
5439 if (counter > 100)
5441 ok(FALSE, "got way too many exceptions, probably caught in an infinite loop, terminating child\n");
5442 pNtTerminateProcess(pi.hProcess, 1);
5444 else if (counter < 2) /* startup breakpoint */
5446 /* breakpoint is inside ntdll */
5447 void *ntdll = GetModuleHandleA( "ntdll.dll" );
5448 IMAGE_NT_HEADERS *nt = RtlImageNtHeader( ntdll );
5450 ok( (char *)ctx.Pc >= (char *)ntdll &&
5451 (char *)ctx.Pc < (char *)ntdll + nt->OptionalHeader.SizeOfImage,
5452 "wrong pc %p ntdll %p-%p\n", (void *)ctx.Pc, ntdll,
5453 (char *)ntdll + nt->OptionalHeader.SizeOfImage );
5455 else
5457 #if 0 /* RtlRaiseException test disabled for now */
5458 if (stage == 1)
5460 ok((char *)ctx.Pc == (char *)code_mem_address + 0xb, "Pc at %p instead of %p\n",
5461 (char *)ctx.Pc, (char *)code_mem_address + 0xb);
5462 /* setting the context from debugger does not affect the context that the
5463 * exception handler gets, except on w2008 */
5464 ctx.Pc = (UINT_PTR)code_mem_address + 0xd;
5465 ctx.X0 = 0xf00f00f1;
5466 /* let the debuggee handle the exception */
5467 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
5469 else if (stage == 2)
5471 if (de.u.Exception.dwFirstChance)
5473 /* debugger gets first chance exception with unmodified ctx.Pc */
5474 ok((char *)ctx.Pc == (char *)code_mem_address + 0xb, "Pc at %p instead of %p\n",
5475 (char *)ctx.Pc, (char *)code_mem_address + 0xb);
5476 ctx.Pc = (UINT_PTR)code_mem_address + 0xd;
5477 ctx.X0 = 0xf00f00f1;
5478 /* pass exception to debuggee
5479 * exception will not be handled and a second chance exception will be raised */
5480 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
5482 else
5484 /* debugger gets context after exception handler has played with it */
5485 /* ctx.Pc is the same value the exception handler got */
5486 if (de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT)
5488 ok((char *)ctx.Pc == (char *)code_mem_address + 0xa,
5489 "Pc at %p instead of %p\n", (char *)ctx.Pc, (char *)code_mem_address + 0xa);
5490 /* need to fixup Pc for debuggee */
5491 ctx.Pc += 4;
5493 else ok((char *)ctx.Pc == (char *)code_mem_address + 0xb,
5494 "Pc at 0x%x instead of %p\n", ctx.Pc, (char *)code_mem_address + 0xb);
5495 /* here we handle exception */
5498 else
5499 #endif
5500 if (stage == 7 || stage == 8)
5502 ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT,
5503 "expected EXCEPTION_BREAKPOINT, got %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode);
5504 ok((char *)ctx.Pc == (char *)code_mem_address + 0x1d,
5505 "expected Pc = %p, got %p\n", (char *)code_mem_address + 0x1d, (char *)ctx.Pc);
5506 if (stage == 8) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
5508 else if (stage == 9 || stage == 10)
5510 ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT,
5511 "expected EXCEPTION_BREAKPOINT, got %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode);
5512 ok((char *)ctx.Pc == (char *)code_mem_address + 4,
5513 "expected Pc = %p, got %p\n", (char *)code_mem_address + 4, (char *)ctx.Pc);
5514 if (stage == 10) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
5516 else if (stage == 11 || stage == 12 || stage == 13)
5518 ok(de.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_INVALID_HANDLE,
5519 "unexpected exception code %08x, expected %08x\n", de.u.Exception.ExceptionRecord.ExceptionCode,
5520 EXCEPTION_INVALID_HANDLE);
5521 ok(de.u.Exception.ExceptionRecord.NumberParameters == 0,
5522 "unexpected number of parameters %d, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters);
5524 if (stage == 12|| stage == 13) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
5526 else
5527 ok(FALSE, "unexpected stage %x\n", stage);
5529 status = pNtSetContextThread(pi.hThread, &ctx);
5530 ok(!status, "NtSetContextThread failed with 0x%x\n", status);
5533 else if (de.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT)
5535 int stage;
5536 char buffer[64];
5538 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
5539 sizeof(stage), &size_read);
5540 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
5542 ok(!de.u.DebugString.fUnicode, "unexpected unicode debug string event\n");
5543 ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) - 1, "buffer not large enough to hold %d bytes\n",
5544 de.u.DebugString.nDebugStringLength);
5546 memset(buffer, 0, sizeof(buffer));
5547 status = pNtReadVirtualMemory(pi.hProcess, de.u.DebugString.lpDebugStringData, buffer,
5548 de.u.DebugString.nDebugStringLength, &size_read);
5549 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
5551 if (stage == 3 || stage == 4)
5552 ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer);
5553 else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */
5554 ok(strstr(buffer, "SHIMVIEW") != NULL, "unexpected stage %x, got debug string event '%s'\n", stage, buffer);
5556 if (stage == 4) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
5558 else if (de.dwDebugEventCode == RIP_EVENT)
5560 int stage;
5562 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
5563 sizeof(stage), &size_read);
5564 ok(!status,"NtReadVirtualMemory failed with 0x%x\n", status);
5566 if (stage == 5 || stage == 6)
5568 ok(de.u.RipInfo.dwError == 0x11223344, "got unexpected rip error code %08x, expected %08x\n",
5569 de.u.RipInfo.dwError, 0x11223344);
5570 ok(de.u.RipInfo.dwType == 0x55667788, "got unexpected rip type %08x, expected %08x\n",
5571 de.u.RipInfo.dwType, 0x55667788);
5573 else
5574 ok(FALSE, "unexpected stage %x\n", stage);
5576 if (stage == 6) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
5579 ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus);
5581 } while (de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT);
5583 wait_child_process( pi.hProcess );
5584 ret = CloseHandle(pi.hThread);
5585 ok(ret, "error %u\n", GetLastError());
5586 ret = CloseHandle(pi.hProcess);
5587 ok(ret, "error %u\n", GetLastError());
5590 static void test_debug_service(DWORD numexc)
5592 /* not supported */
5595 #endif /* __aarch64__ */
5597 #if defined(__i386__) || defined(__x86_64__)
5599 static DWORD WINAPI register_check_thread(void *arg)
5601 NTSTATUS status;
5602 CONTEXT ctx;
5604 memset(&ctx, 0, sizeof(ctx));
5605 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
5607 status = pNtGetContextThread(GetCurrentThread(), &ctx);
5608 ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %x\n", status);
5609 ok(!ctx.Dr0, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr0);
5610 ok(!ctx.Dr1, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr1);
5611 ok(!ctx.Dr2, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr2);
5612 ok(!ctx.Dr3, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr3);
5613 ok(!ctx.Dr6, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr6);
5614 ok(!ctx.Dr7, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr7);
5616 return 0;
5619 static void test_debug_registers(void)
5621 static const struct
5623 ULONG_PTR dr0, dr1, dr2, dr3, dr6, dr7;
5625 tests[] =
5627 { 0x42424240, 0, 0x126bb070, 0x0badbad0, 0, 0xffff0115 },
5628 { 0x42424242, 0, 0x100f0fe7, 0x0abebabe, 0, 0x115 },
5630 NTSTATUS status;
5631 CONTEXT ctx;
5632 HANDLE thread;
5633 int i;
5635 for (i = 0; i < ARRAY_SIZE(tests); i++)
5637 memset(&ctx, 0, sizeof(ctx));
5638 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
5639 ctx.Dr0 = tests[i].dr0;
5640 ctx.Dr1 = tests[i].dr1;
5641 ctx.Dr2 = tests[i].dr2;
5642 ctx.Dr3 = tests[i].dr3;
5643 ctx.Dr6 = tests[i].dr6;
5644 ctx.Dr7 = tests[i].dr7;
5646 status = pNtSetContextThread(GetCurrentThread(), &ctx);
5647 ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %08x\n", status);
5649 memset(&ctx, 0, sizeof(ctx));
5650 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
5652 status = pNtGetContextThread(GetCurrentThread(), &ctx);
5653 ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %08x\n", status);
5654 ok(ctx.Dr0 == tests[i].dr0, "test %d: expected %lx, got %lx\n", i, tests[i].dr0, (DWORD_PTR)ctx.Dr0);
5655 ok(ctx.Dr1 == tests[i].dr1, "test %d: expected %lx, got %lx\n", i, tests[i].dr1, (DWORD_PTR)ctx.Dr1);
5656 ok(ctx.Dr2 == tests[i].dr2, "test %d: expected %lx, got %lx\n", i, tests[i].dr2, (DWORD_PTR)ctx.Dr2);
5657 ok(ctx.Dr3 == tests[i].dr3, "test %d: expected %lx, got %lx\n", i, tests[i].dr3, (DWORD_PTR)ctx.Dr3);
5658 ok((ctx.Dr6 & 0xf00f) == tests[i].dr6, "test %d: expected %lx, got %lx\n", i, tests[i].dr6, (DWORD_PTR)ctx.Dr6);
5659 ok((ctx.Dr7 & ~0xdc00) == tests[i].dr7, "test %d: expected %lx, got %lx\n", i, tests[i].dr7, (DWORD_PTR)ctx.Dr7);
5662 memset(&ctx, 0, sizeof(ctx));
5663 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
5664 ctx.Dr0 = 0xffffffff;
5665 ctx.Dr1 = 0xffffffff;
5666 ctx.Dr2 = 0xffffffff;
5667 ctx.Dr3 = 0xffffffff;
5668 ctx.Dr6 = 0xffffffff;
5669 ctx.Dr7 = 0x00000400;
5670 status = pNtSetContextThread(GetCurrentThread(), &ctx);
5671 ok(status == STATUS_SUCCESS, "NtSetContextThread failed with %x\n", status);
5673 thread = CreateThread(NULL, 0, register_check_thread, NULL, CREATE_SUSPENDED, NULL);
5674 ok(thread != INVALID_HANDLE_VALUE, "CreateThread failed with %d\n", GetLastError());
5676 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
5677 status = pNtGetContextThread(thread, &ctx);
5678 ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %x\n", status);
5679 ok(!ctx.Dr0, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr0);
5680 ok(!ctx.Dr1, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr1);
5681 ok(!ctx.Dr2, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr2);
5682 ok(!ctx.Dr3, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr3);
5683 ok(!ctx.Dr6, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr6);
5684 ok(!ctx.Dr7, "expected 0, got %lx\n", (DWORD_PTR)ctx.Dr7);
5686 ResumeThread(thread);
5687 WaitForSingleObject(thread, 10000);
5688 CloseHandle(thread);
5691 static DWORD debug_service_exceptions;
5693 static LONG CALLBACK debug_service_handler(EXCEPTION_POINTERS *ExceptionInfo)
5695 EXCEPTION_RECORD *rec = ExceptionInfo->ExceptionRecord;
5696 trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress);
5698 ok(rec->ExceptionCode == EXCEPTION_BREAKPOINT, "ExceptionCode is %08x instead of %08x\n",
5699 rec->ExceptionCode, EXCEPTION_BREAKPOINT);
5701 #ifdef __i386__
5702 ok(ExceptionInfo->ContextRecord->Eip == (DWORD)code_mem + 0x1c,
5703 "expected Eip = %x, got %x\n", (DWORD)code_mem + 0x1c, ExceptionInfo->ContextRecord->Eip);
5704 ok(rec->NumberParameters == (is_wow64 ? 1 : 3),
5705 "ExceptionParameters is %d instead of %d\n", rec->NumberParameters, is_wow64 ? 1 : 3);
5706 ok(rec->ExceptionInformation[0] == ExceptionInfo->ContextRecord->Eax,
5707 "expected ExceptionInformation[0] = %x, got %lx\n",
5708 ExceptionInfo->ContextRecord->Eax, rec->ExceptionInformation[0]);
5709 if (!is_wow64)
5711 ok(rec->ExceptionInformation[1] == 0x11111111,
5712 "got ExceptionInformation[1] = %lx\n", rec->ExceptionInformation[1]);
5713 ok(rec->ExceptionInformation[2] == 0x22222222,
5714 "got ExceptionInformation[2] = %lx\n", rec->ExceptionInformation[2]);
5716 #else
5717 ok(ExceptionInfo->ContextRecord->Rip == (DWORD_PTR)code_mem + 0x2f,
5718 "expected Rip = %lx, got %lx\n", (DWORD_PTR)code_mem + 0x2f, ExceptionInfo->ContextRecord->Rip);
5719 ok(rec->NumberParameters == 1,
5720 "ExceptionParameters is %d instead of 1\n", rec->NumberParameters);
5721 ok(rec->ExceptionInformation[0] == ExceptionInfo->ContextRecord->Rax,
5722 "expected ExceptionInformation[0] = %lx, got %lx\n",
5723 ExceptionInfo->ContextRecord->Rax, rec->ExceptionInformation[0]);
5724 #endif
5726 debug_service_exceptions++;
5727 return (rec->ExceptionCode == EXCEPTION_BREAKPOINT) ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH;
5730 #ifdef __i386__
5732 static const BYTE call_debug_service_code[] = {
5733 0x53, /* pushl %ebx */
5734 0x57, /* pushl %edi */
5735 0x8b, 0x44, 0x24, 0x0c, /* movl 12(%esp),%eax */
5736 0xb9, 0x11, 0x11, 0x11, 0x11, /* movl $0x11111111,%ecx */
5737 0xba, 0x22, 0x22, 0x22, 0x22, /* movl $0x22222222,%edx */
5738 0xbb, 0x33, 0x33, 0x33, 0x33, /* movl $0x33333333,%ebx */
5739 0xbf, 0x44, 0x44, 0x44, 0x44, /* movl $0x44444444,%edi */
5740 0xcd, 0x2d, /* int $0x2d */
5741 0xeb, /* jmp $+17 */
5742 0x0f, 0x1f, 0x00, /* nop */
5743 0x31, 0xc0, /* xorl %eax,%eax */
5744 0xeb, 0x0c, /* jmp $+14 */
5745 0x90, 0x90, 0x90, 0x90, /* nop */
5746 0x90, 0x90, 0x90, 0x90,
5747 0x90,
5748 0x31, 0xc0, /* xorl %eax,%eax */
5749 0x40, /* incl %eax */
5750 0x5f, /* popl %edi */
5751 0x5b, /* popl %ebx */
5752 0xc3, /* ret */
5755 #else
5757 static const BYTE call_debug_service_code[] = {
5758 0x53, /* push %rbx */
5759 0x57, /* push %rdi */
5760 0x48, 0x89, 0xc8, /* movl %rcx,%rax */
5761 0x48, 0xb9, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, /* movabs $0x1111111111111111,%rcx */
5762 0x48, 0xba, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* movabs $0x2222222222222222,%rdx */
5763 0x48, 0xbb, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, /* movabs $0x3333333333333333,%rbx */
5764 0x48, 0xbf, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, /* movabs $0x4444444444444444,%rdi */
5765 0xcd, 0x2d, /* int $0x2d */
5766 0xeb, /* jmp $+17 */
5767 0x0f, 0x1f, 0x00, /* nop */
5768 0x48, 0x31, 0xc0, /* xor %rax,%rax */
5769 0xeb, 0x0e, /* jmp $+16 */
5770 0x90, 0x90, 0x90, 0x90, /* nop */
5771 0x90, 0x90, 0x90, 0x90,
5772 0x48, 0x31, 0xc0, /* xor %rax,%rax */
5773 0x48, 0xff, 0xc0, /* inc %rax */
5774 0x5f, /* pop %rdi */
5775 0x5b, /* pop %rbx */
5776 0xc3, /* ret */
5779 #endif
5781 static void test_debug_service(DWORD numexc)
5783 DWORD (CDECL *func)(DWORD_PTR) = code_mem;
5784 DWORD expected_exc, expected_ret;
5785 void *vectored_handler;
5786 DWORD ret;
5788 /* code will return 0 if execution resumes immediately after "int $0x2d", otherwise 1 */
5789 memcpy(code_mem, call_debug_service_code, sizeof(call_debug_service_code));
5791 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &debug_service_handler);
5792 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
5794 expected_exc = numexc;
5795 expected_ret = (numexc != 0);
5797 /* BREAKPOINT_BREAK */
5798 debug_service_exceptions = 0;
5799 ret = func(0);
5800 ok(debug_service_exceptions == expected_exc,
5801 "BREAKPOINT_BREAK generated %u exceptions, expected %u\n",
5802 debug_service_exceptions, expected_exc);
5803 ok(ret == expected_ret,
5804 "BREAKPOINT_BREAK returned %u, expected %u\n", ret, expected_ret);
5806 /* BREAKPOINT_PROMPT */
5807 debug_service_exceptions = 0;
5808 ret = func(2);
5809 ok(debug_service_exceptions == expected_exc,
5810 "BREAKPOINT_PROMPT generated %u exceptions, expected %u\n",
5811 debug_service_exceptions, expected_exc);
5812 ok(ret == expected_ret,
5813 "BREAKPOINT_PROMPT returned %u, expected %u\n", ret, expected_ret);
5815 /* invalid debug service */
5816 debug_service_exceptions = 0;
5817 ret = func(6);
5818 ok(debug_service_exceptions == expected_exc,
5819 "invalid debug service generated %u exceptions, expected %u\n",
5820 debug_service_exceptions, expected_exc);
5821 ok(ret == expected_ret,
5822 "invalid debug service returned %u, expected %u\n", ret, expected_ret);
5824 expected_exc = (is_wow64 ? numexc : 0);
5825 expected_ret = (is_wow64 && numexc);
5827 /* BREAKPOINT_PRINT */
5828 debug_service_exceptions = 0;
5829 ret = func(1);
5830 ok(debug_service_exceptions == expected_exc,
5831 "BREAKPOINT_PRINT generated %u exceptions, expected %u\n",
5832 debug_service_exceptions, expected_exc);
5833 ok(ret == expected_ret,
5834 "BREAKPOINT_PRINT returned %u, expected %u\n", ret, expected_ret);
5836 /* BREAKPOINT_LOAD_SYMBOLS */
5837 debug_service_exceptions = 0;
5838 ret = func(3);
5839 ok(debug_service_exceptions == expected_exc,
5840 "BREAKPOINT_LOAD_SYMBOLS generated %u exceptions, expected %u\n",
5841 debug_service_exceptions, expected_exc);
5842 ok(ret == expected_ret,
5843 "BREAKPOINT_LOAD_SYMBOLS returned %u, expected %u\n", ret, expected_ret);
5845 /* BREAKPOINT_UNLOAD_SYMBOLS */
5846 debug_service_exceptions = 0;
5847 ret = func(4);
5848 ok(debug_service_exceptions == expected_exc,
5849 "BREAKPOINT_UNLOAD_SYMBOLS generated %u exceptions, expected %u\n",
5850 debug_service_exceptions, expected_exc);
5851 ok(ret == expected_ret,
5852 "BREAKPOINT_UNLOAD_SYMBOLS returned %u, expected %u\n", ret, expected_ret);
5854 /* BREAKPOINT_COMMAND_STRING */
5855 debug_service_exceptions = 0;
5856 ret = func(5);
5857 ok(debug_service_exceptions == expected_exc || broken(debug_service_exceptions == numexc),
5858 "BREAKPOINT_COMMAND_STRING generated %u exceptions, expected %u\n",
5859 debug_service_exceptions, expected_exc);
5860 ok(ret == expected_ret || broken(ret == (numexc != 0)),
5861 "BREAKPOINT_COMMAND_STRING returned %u, expected %u\n", ret, expected_ret);
5863 pRtlRemoveVectoredExceptionHandler(vectored_handler);
5865 #endif /* defined(__i386__) || defined(__x86_64__) */
5867 static DWORD outputdebugstring_exceptions;
5869 static LONG CALLBACK outputdebugstring_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
5871 PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
5872 trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress);
5874 ok(rec->ExceptionCode == DBG_PRINTEXCEPTION_C, "ExceptionCode is %08x instead of %08x\n",
5875 rec->ExceptionCode, DBG_PRINTEXCEPTION_C);
5876 ok(rec->NumberParameters == 2, "ExceptionParameters is %d instead of 2\n", rec->NumberParameters);
5877 ok(rec->ExceptionInformation[0] == 12, "ExceptionInformation[0] = %d instead of 12\n", (DWORD)rec->ExceptionInformation[0]);
5878 ok(!strcmp((char *)rec->ExceptionInformation[1], "Hello World"),
5879 "ExceptionInformation[1] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[1]);
5881 outputdebugstring_exceptions++;
5882 return EXCEPTION_CONTINUE_SEARCH;
5885 static void test_outputdebugstring(DWORD numexc, BOOL todo)
5887 PVOID vectored_handler;
5889 if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler)
5891 skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n");
5892 return;
5895 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &outputdebugstring_vectored_handler);
5896 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
5898 outputdebugstring_exceptions = 0;
5899 OutputDebugStringA("Hello World");
5901 todo_wine_if(todo)
5902 ok(outputdebugstring_exceptions == numexc, "OutputDebugStringA generated %d exceptions, expected %d\n",
5903 outputdebugstring_exceptions, numexc);
5905 pRtlRemoveVectoredExceptionHandler(vectored_handler);
5908 static DWORD ripevent_exceptions;
5910 static LONG CALLBACK ripevent_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
5912 PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
5913 trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress);
5915 ok(rec->ExceptionCode == DBG_RIPEXCEPTION, "ExceptionCode is %08x instead of %08x\n",
5916 rec->ExceptionCode, DBG_RIPEXCEPTION);
5917 ok(rec->NumberParameters == 2, "ExceptionParameters is %d instead of 2\n", rec->NumberParameters);
5918 ok(rec->ExceptionInformation[0] == 0x11223344, "ExceptionInformation[0] = %08x instead of %08x\n",
5919 (NTSTATUS)rec->ExceptionInformation[0], 0x11223344);
5920 ok(rec->ExceptionInformation[1] == 0x55667788, "ExceptionInformation[1] = %08x instead of %08x\n",
5921 (NTSTATUS)rec->ExceptionInformation[1], 0x55667788);
5923 ripevent_exceptions++;
5924 return (rec->ExceptionCode == DBG_RIPEXCEPTION) ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH;
5927 static void test_ripevent(DWORD numexc)
5929 EXCEPTION_RECORD record;
5930 PVOID vectored_handler;
5932 if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler || !pRtlRaiseException)
5934 skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler or RtlRaiseException not found\n");
5935 return;
5938 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &ripevent_vectored_handler);
5939 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
5941 record.ExceptionCode = DBG_RIPEXCEPTION;
5942 record.ExceptionFlags = 0;
5943 record.ExceptionRecord = NULL;
5944 record.ExceptionAddress = NULL;
5945 record.NumberParameters = 2;
5946 record.ExceptionInformation[0] = 0x11223344;
5947 record.ExceptionInformation[1] = 0x55667788;
5949 ripevent_exceptions = 0;
5950 pRtlRaiseException(&record);
5951 ok(ripevent_exceptions == numexc, "RtlRaiseException generated %d exceptions, expected %d\n",
5952 ripevent_exceptions, numexc);
5954 pRtlRemoveVectoredExceptionHandler(vectored_handler);
5957 static DWORD breakpoint_exceptions;
5959 static LONG CALLBACK breakpoint_handler(EXCEPTION_POINTERS *ExceptionInfo)
5961 EXCEPTION_RECORD *rec = ExceptionInfo->ExceptionRecord;
5962 trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress);
5964 ok(rec->ExceptionCode == EXCEPTION_BREAKPOINT, "ExceptionCode is %08x instead of %08x\n",
5965 rec->ExceptionCode, EXCEPTION_BREAKPOINT);
5967 #ifdef __i386__
5968 ok(ExceptionInfo->ContextRecord->Eip == (DWORD)code_mem + 1,
5969 "expected Eip = %x, got %x\n", (DWORD)code_mem + 1, ExceptionInfo->ContextRecord->Eip);
5970 ok(rec->NumberParameters == (is_wow64 ? 1 : 3),
5971 "ExceptionParameters is %d instead of %d\n", rec->NumberParameters, is_wow64 ? 1 : 3);
5972 ok(rec->ExceptionInformation[0] == 0,
5973 "got ExceptionInformation[0] = %lx\n", rec->ExceptionInformation[0]);
5974 ExceptionInfo->ContextRecord->Eip = (DWORD)code_mem + 2;
5975 #elif defined(__x86_64__)
5976 ok(ExceptionInfo->ContextRecord->Rip == (DWORD_PTR)code_mem + 1,
5977 "expected Rip = %lx, got %lx\n", (DWORD_PTR)code_mem + 1, ExceptionInfo->ContextRecord->Rip);
5978 ok(rec->NumberParameters == 1,
5979 "ExceptionParameters is %d instead of 1\n", rec->NumberParameters);
5980 ok(rec->ExceptionInformation[0] == 0,
5981 "got ExceptionInformation[0] = %lx\n", rec->ExceptionInformation[0]);
5982 ExceptionInfo->ContextRecord->Rip = (DWORD_PTR)code_mem + 2;
5983 #elif defined(__arm__)
5984 ok(ExceptionInfo->ContextRecord->Pc == (DWORD)code_mem + 4,
5985 "expected pc = %lx, got %lx\n", (DWORD)code_mem + 4, ExceptionInfo->ContextRecord->Pc);
5986 ok(rec->NumberParameters == 1,
5987 "ExceptionParameters is %d instead of 1\n", rec->NumberParameters);
5988 ok(rec->ExceptionInformation[0] == 0,
5989 "got ExceptionInformation[0] = %lx\n", rec->ExceptionInformation[0]);
5990 #elif defined(__aarch64__)
5991 ok(ExceptionInfo->ContextRecord->Pc == (DWORD_PTR)code_mem + 4,
5992 "expected pc = %lx, got %lx\n", (DWORD_PTR)code_mem + 4, ExceptionInfo->ContextRecord->Pc);
5993 ok(rec->NumberParameters == 1,
5994 "ExceptionParameters is %d instead of 1\n", rec->NumberParameters);
5995 ok(rec->ExceptionInformation[0] == 0,
5996 "got ExceptionInformation[0] = %lx\n", rec->ExceptionInformation[0]);
5997 #endif
5999 breakpoint_exceptions++;
6000 return (rec->ExceptionCode == EXCEPTION_BREAKPOINT) ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH;
6003 #if defined(__i386__) || defined(__x86_64__)
6004 static const BYTE breakpoint_code[] = { 0xcd, 0x03, 0xc3 }; /* int $0x3; ret */
6005 #elif defined(__arm__)
6006 static const DWORD breakpoint_code[] = { 0xe1200070, 0xe12fff1e }; /* bkpt #0; bx lr */
6007 #elif defined(__aarch64__)
6008 static const DWORD breakpoint_code[] = { 0xd4200000, 0xd65f03c0 }; /* brk #0; ret */
6009 #endif
6011 static void test_breakpoint(DWORD numexc)
6013 DWORD (CDECL *func)(void) = code_mem;
6014 void *vectored_handler;
6016 memcpy(code_mem, breakpoint_code, sizeof(breakpoint_code));
6018 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &breakpoint_handler);
6019 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
6021 breakpoint_exceptions = 0;
6022 func();
6023 ok(breakpoint_exceptions == numexc, "int $0x3 generated %u exceptions, expected %u\n",
6024 breakpoint_exceptions, numexc);
6026 pRtlRemoveVectoredExceptionHandler(vectored_handler);
6029 #if defined(__i386__) || defined(__x86_64__)
6030 static BYTE except_code_set_ymm0[] =
6032 #ifdef __x86_64__
6033 0x48,
6034 #endif
6035 0xb8, /* mov imm,%ax */
6036 0x00, 0x00, 0x00, 0x00,
6037 #ifdef __x86_64__
6038 0x00, 0x00, 0x00, 0x00,
6039 #endif
6041 0xc5, 0xfc, 0x10, 0x00, /* vmovups (%ax),%ymm0 */
6042 0xcc, /* int3 */
6043 0xc5, 0xfc, 0x11, 0x00, /* vmovups %ymm0,(%ax) */
6044 0xc3, /* ret */
6047 static void test_debuggee_xstate(void)
6049 void (CDECL *func)(void) = code_mem;
6050 unsigned int address_offset, i;
6051 unsigned int data[8];
6053 if (!pRtlGetEnabledExtendedFeatures || !pRtlGetEnabledExtendedFeatures(1 << XSTATE_AVX))
6055 memcpy(code_mem, breakpoint_code, sizeof(breakpoint_code));
6056 func();
6057 return;
6060 memcpy(code_mem, except_code_set_ymm0, sizeof(except_code_set_ymm0));
6061 address_offset = sizeof(void *) == 8 ? 2 : 1;
6062 *(void **)((BYTE *)code_mem + address_offset) = data;
6064 for (i = 0; i < ARRAY_SIZE(data); ++i)
6065 data[i] = i + 1;
6067 func();
6069 for (i = 0; i < 4; ++i)
6070 ok(data[i] == (test_stage == 14 ? i + 1 : 0x28282828),
6071 "Got unexpected data %#x, test_stage %u, i %u.\n", data[i], test_stage, i);
6073 for ( ; i < ARRAY_SIZE(data); ++i)
6074 ok(data[i] == (test_stage == 14 ? i + 1 : 0x48484848)
6075 || broken(test_stage == 15 && data[i] == i + 1) /* Win7 */,
6076 "Got unexpected data %#x, test_stage %u, i %u.\n", data[i], test_stage, i);
6078 #endif
6080 static DWORD invalid_handle_exceptions;
6082 static LONG CALLBACK invalid_handle_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
6084 PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
6085 trace("vect. handler %08x addr:%p\n", rec->ExceptionCode, rec->ExceptionAddress);
6087 ok(rec->ExceptionCode == EXCEPTION_INVALID_HANDLE, "ExceptionCode is %08x instead of %08x\n",
6088 rec->ExceptionCode, EXCEPTION_INVALID_HANDLE);
6089 ok(rec->NumberParameters == 0, "ExceptionParameters is %d instead of 0\n", rec->NumberParameters);
6091 invalid_handle_exceptions++;
6092 return (rec->ExceptionCode == EXCEPTION_INVALID_HANDLE) ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH;
6095 static void test_closehandle(DWORD numexc, HANDLE handle)
6097 PVOID vectored_handler;
6098 NTSTATUS status;
6099 DWORD res;
6101 if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler || !pRtlRaiseException)
6103 skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler or RtlRaiseException not found\n");
6104 return;
6107 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &invalid_handle_vectored_handler);
6108 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
6110 invalid_handle_exceptions = 0;
6111 res = CloseHandle(handle);
6113 ok(!res || (is_wow64 && res), "CloseHandle(%p) unexpectedly succeeded\n", handle);
6114 ok(GetLastError() == ERROR_INVALID_HANDLE, "wrong error code %d instead of %d\n",
6115 GetLastError(), ERROR_INVALID_HANDLE);
6116 ok(invalid_handle_exceptions == numexc, "CloseHandle generated %d exceptions, expected %d\n",
6117 invalid_handle_exceptions, numexc);
6119 invalid_handle_exceptions = 0;
6120 status = pNtClose(handle);
6121 ok(status == STATUS_INVALID_HANDLE || (is_wow64 && status == 0), "NtClose(%p) returned status %08x\n", handle, status);
6122 ok(invalid_handle_exceptions == numexc, "NtClose generated %d exceptions, expected %d\n",
6123 invalid_handle_exceptions, numexc);
6125 pRtlRemoveVectoredExceptionHandler(vectored_handler);
6128 static void test_vectored_continue_handler(void)
6130 PVOID handler1, handler2;
6131 ULONG ret;
6133 if (!pRtlAddVectoredContinueHandler || !pRtlRemoveVectoredContinueHandler)
6135 skip("RtlAddVectoredContinueHandler or RtlRemoveVectoredContinueHandler not found\n");
6136 return;
6139 handler1 = pRtlAddVectoredContinueHandler(TRUE, (void *)0xdeadbeef);
6140 ok(handler1 != 0, "RtlAddVectoredContinueHandler failed\n");
6142 handler2 = pRtlAddVectoredContinueHandler(TRUE, (void *)0xdeadbeef);
6143 ok(handler2 != 0, "RtlAddVectoredContinueHandler failed\n");
6144 ok(handler1 != handler2, "RtlAddVectoredContinueHandler returned same handler\n");
6146 if (pRtlRemoveVectoredExceptionHandler)
6148 ret = pRtlRemoveVectoredExceptionHandler(handler1);
6149 ok(!ret, "RtlRemoveVectoredExceptionHandler succeeded\n");
6152 ret = pRtlRemoveVectoredContinueHandler(handler1);
6153 ok(ret, "RtlRemoveVectoredContinueHandler failed\n");
6155 ret = pRtlRemoveVectoredContinueHandler(handler2);
6156 ok(ret, "RtlRemoveVectoredContinueHandler failed\n");
6158 ret = pRtlRemoveVectoredContinueHandler(handler1);
6159 ok(!ret, "RtlRemoveVectoredContinueHandler succeeded\n");
6161 ret = pRtlRemoveVectoredContinueHandler((void *)0x11223344);
6162 ok(!ret, "RtlRemoveVectoredContinueHandler succeeded\n");
6165 static DWORD WINAPI suspend_thread_test( void *arg )
6167 HANDLE event = arg;
6168 WaitForSingleObject(event, INFINITE);
6169 return 0;
6172 static void test_suspend_count(HANDLE hthread, ULONG expected_count, int line)
6174 static BOOL supported = TRUE;
6175 NTSTATUS status;
6176 ULONG count;
6178 if (!supported)
6179 return;
6181 count = ~0u;
6182 status = pNtQueryInformationThread(hthread, ThreadSuspendCount, &count, sizeof(count), NULL);
6183 if (status)
6185 win_skip("ThreadSuspendCount is not supported.\n");
6186 supported = FALSE;
6187 return;
6190 ok_(__FILE__, line)(!status, "Failed to get suspend count, status %#x.\n", status);
6191 ok_(__FILE__, line)(count == expected_count, "Unexpected suspend count %u.\n", count);
6194 static void test_suspend_thread(void)
6196 #define TEST_SUSPEND_COUNT(thread, count) test_suspend_count((thread), (count), __LINE__)
6197 HANDLE thread, event;
6198 ULONG count, len;
6199 NTSTATUS status;
6200 DWORD ret;
6202 status = NtSuspendThread(0, NULL);
6203 ok(status == STATUS_INVALID_HANDLE, "Unexpected return value %#x.\n", status);
6205 status = NtResumeThread(0, NULL);
6206 ok(status == STATUS_INVALID_HANDLE, "Unexpected return value %#x.\n", status);
6208 event = CreateEventW(NULL, FALSE, FALSE, NULL);
6210 thread = CreateThread(NULL, 0, suspend_thread_test, event, 0, NULL);
6211 ok(thread != NULL, "Failed to create a thread.\n");
6213 ret = WaitForSingleObject(thread, 0);
6214 ok(ret == WAIT_TIMEOUT, "Unexpected status %d.\n", ret);
6216 status = pNtQueryInformationThread(thread, ThreadSuspendCount, &count, sizeof(count), NULL);
6217 if (!status)
6219 status = pNtQueryInformationThread(thread, ThreadSuspendCount, NULL, sizeof(count), NULL);
6220 ok(status == STATUS_ACCESS_VIOLATION, "Unexpected status %#x.\n", status);
6222 status = pNtQueryInformationThread(thread, ThreadSuspendCount, &count, sizeof(count) / 2, NULL);
6223 ok(status == STATUS_INFO_LENGTH_MISMATCH, "Unexpected status %#x.\n", status);
6225 len = 123;
6226 status = pNtQueryInformationThread(thread, ThreadSuspendCount, &count, sizeof(count) / 2, &len);
6227 ok(status == STATUS_INFO_LENGTH_MISMATCH, "Unexpected status %#x.\n", status);
6228 ok(len == 123, "Unexpected info length %u.\n", len);
6230 len = 123;
6231 status = pNtQueryInformationThread(thread, ThreadSuspendCount, NULL, 0, &len);
6232 ok(status == STATUS_INFO_LENGTH_MISMATCH, "Unexpected status %#x.\n", status);
6233 ok(len == 123, "Unexpected info length %u.\n", len);
6235 count = 10;
6236 status = pNtQueryInformationThread(0, ThreadSuspendCount, &count, sizeof(count), NULL);
6237 ok(status, "Unexpected status %#x.\n", status);
6238 ok(count == 10, "Unexpected suspend count %u.\n", count);
6241 status = NtResumeThread(thread, NULL);
6242 ok(!status, "Unexpected status %#x.\n", status);
6244 status = NtResumeThread(thread, &count);
6245 ok(!status, "Unexpected status %#x.\n", status);
6246 ok(count == 0, "Unexpected suspended count %u.\n", count);
6248 TEST_SUSPEND_COUNT(thread, 0);
6250 status = NtSuspendThread(thread, NULL);
6251 ok(!status, "Failed to suspend a thread, status %#x.\n", status);
6253 TEST_SUSPEND_COUNT(thread, 1);
6255 status = NtSuspendThread(thread, &count);
6256 ok(!status, "Failed to suspend a thread, status %#x.\n", status);
6257 ok(count == 1, "Unexpected suspended count %u.\n", count);
6259 TEST_SUSPEND_COUNT(thread, 2);
6261 status = NtResumeThread(thread, &count);
6262 ok(!status, "Failed to resume a thread, status %#x.\n", status);
6263 ok(count == 2, "Unexpected suspended count %u.\n", count);
6265 TEST_SUSPEND_COUNT(thread, 1);
6267 status = NtResumeThread(thread, NULL);
6268 ok(!status, "Failed to resume a thread, status %#x.\n", status);
6270 TEST_SUSPEND_COUNT(thread, 0);
6272 SetEvent(event);
6273 WaitForSingleObject(thread, INFINITE);
6275 CloseHandle(thread);
6276 #undef TEST_SUSPEND_COUNT
6279 static const char *suspend_process_event_name = "suspend_process_event";
6280 static const char *suspend_process_event2_name = "suspend_process_event2";
6282 static DWORD WINAPI dummy_thread_proc( void *arg )
6284 return 0;
6287 static void suspend_process_proc(void)
6289 HANDLE event = OpenEventA(SYNCHRONIZE, FALSE, suspend_process_event_name);
6290 HANDLE event2 = OpenEventA(SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, suspend_process_event2_name);
6291 unsigned int count;
6292 NTSTATUS status;
6293 HANDLE thread;
6295 ok(event != NULL, "Failed to open event handle.\n");
6296 ok(event2 != NULL, "Failed to open event handle.\n");
6298 thread = CreateThread(NULL, 0, dummy_thread_proc, 0, CREATE_SUSPENDED, NULL);
6299 ok(thread != NULL, "Failed to create auxiliary thread.\n");
6301 /* Suspend up to limit. */
6302 while (!(status = NtSuspendThread(thread, NULL)))
6304 ok(status == STATUS_SUSPEND_COUNT_EXCEEDED, "Unexpected status %#x.\n", status);
6306 for (;;)
6308 SetEvent(event2);
6309 if (WaitForSingleObject(event, 100) == WAIT_OBJECT_0)
6310 break;
6313 status = NtSuspendThread(thread, &count);
6314 ok(!status, "Failed to suspend a thread, status %#x.\n", status);
6315 ok(count == 125, "Unexpected suspend count %u.\n", count);
6317 status = NtResumeThread(thread, NULL);
6318 ok(!status, "Failed to resume a thread, status %#x.\n", status);
6320 CloseHandle(event);
6321 CloseHandle(event2);
6324 static void test_suspend_process(void)
6326 PROCESS_INFORMATION info;
6327 char path_name[MAX_PATH];
6328 STARTUPINFOA startup;
6329 HANDLE event, event2;
6330 NTSTATUS status;
6331 char **argv;
6332 DWORD ret;
6334 event = CreateEventA(NULL, FALSE, FALSE, suspend_process_event_name);
6335 ok(event != NULL, "Failed to create event.\n");
6337 event2 = CreateEventA(NULL, FALSE, FALSE, suspend_process_event2_name);
6338 ok(event2 != NULL, "Failed to create event.\n");
6340 winetest_get_mainargs(&argv);
6341 memset(&startup, 0, sizeof(startup));
6342 startup.cb = sizeof(startup);
6343 sprintf(path_name, "%s exception suspend_process", argv[0]);
6345 ret = CreateProcessA(NULL, path_name, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info);
6346 ok(ret, "Failed to create target process.\n");
6348 /* New process signals this event. */
6349 ResetEvent(event2);
6350 ret = WaitForSingleObject(event2, INFINITE);
6351 ok(ret == WAIT_OBJECT_0, "Wait failed, %#x.\n", ret);
6353 /* Suspend main thread */
6354 status = NtSuspendThread(info.hThread, &ret);
6355 ok(!status && !ret, "Failed to suspend main thread, status %#x.\n", status);
6357 /* Process wasn't suspended yet. */
6358 status = pNtResumeProcess(info.hProcess);
6359 ok(!status, "Failed to resume a process, status %#x.\n", status);
6361 status = pNtSuspendProcess(0);
6362 ok(status == STATUS_INVALID_HANDLE, "Unexpected status %#x.\n", status);
6364 status = pNtResumeProcess(info.hProcess);
6365 ok(!status, "Failed to resume a process, status %#x.\n", status);
6367 ResetEvent(event2);
6368 ret = WaitForSingleObject(event2, 200);
6369 ok(ret == WAIT_OBJECT_0, "Wait failed.\n");
6371 status = pNtSuspendProcess(info.hProcess);
6372 ok(!status, "Failed to suspend a process, status %#x.\n", status);
6374 status = NtSuspendThread(info.hThread, &ret);
6375 ok(!status && ret == 1, "Failed to suspend main thread, status %#x.\n", status);
6376 status = NtResumeThread(info.hThread, &ret);
6377 ok(!status && ret == 2, "Failed to resume main thread, status %#x.\n", status);
6379 ResetEvent(event2);
6380 ret = WaitForSingleObject(event2, 200);
6381 ok(ret == WAIT_TIMEOUT, "Wait failed.\n");
6383 status = pNtSuspendProcess(info.hProcess);
6384 ok(!status, "Failed to suspend a process, status %#x.\n", status);
6386 status = pNtResumeProcess(info.hProcess);
6387 ok(!status, "Failed to resume a process, status %#x.\n", status);
6389 ResetEvent(event2);
6390 ret = WaitForSingleObject(event2, 200);
6391 ok(ret == WAIT_TIMEOUT, "Wait failed.\n");
6393 status = pNtResumeProcess(info.hProcess);
6394 ok(!status, "Failed to resume a process, status %#x.\n", status);
6396 ResetEvent(event2);
6397 ret = WaitForSingleObject(event2, 200);
6398 ok(ret == WAIT_OBJECT_0, "Wait failed.\n");
6400 SetEvent(event);
6402 wait_child_process(info.hProcess);
6404 CloseHandle(info.hProcess);
6405 CloseHandle(info.hThread);
6407 CloseHandle(event);
6408 CloseHandle(event2);
6411 static void test_unload_trace(void)
6413 static const WCHAR imageW[] = {'m','s','x','m','l','3','.','d','l','l',0};
6414 RTL_UNLOAD_EVENT_TRACE *unload_trace, **unload_trace_ex = NULL, *ptr;
6415 ULONG *element_size, *element_count, size;
6416 HMODULE hmod;
6417 BOOL found;
6419 unload_trace = pRtlGetUnloadEventTrace();
6420 ok(unload_trace != NULL, "Failed to get unload events pointer.\n");
6422 if (pRtlGetUnloadEventTraceEx)
6424 pRtlGetUnloadEventTraceEx(&element_size, &element_count, (void **)&unload_trace_ex);
6425 ok(*element_size >= sizeof(*ptr), "Unexpected element size.\n");
6426 ok(*element_count == RTL_UNLOAD_EVENT_TRACE_NUMBER, "Unexpected trace element count %u.\n", *element_count);
6427 ok(unload_trace_ex != NULL, "Unexpected pointer %p.\n", unload_trace_ex);
6428 size = *element_size;
6430 else
6431 size = sizeof(*unload_trace);
6433 hmod = LoadLibraryA("msxml3.dll");
6434 ok(hmod != NULL, "Failed to load library.\n");
6435 FreeLibrary(hmod);
6437 found = FALSE;
6438 ptr = unload_trace;
6439 while (ptr->BaseAddress != NULL)
6441 if (!lstrcmpW(imageW, ptr->ImageName))
6443 found = TRUE;
6444 break;
6446 ptr = (RTL_UNLOAD_EVENT_TRACE *)((char *)ptr + size);
6448 ok(found, "Unloaded module wasn't found.\n");
6450 if (unload_trace_ex)
6452 found = FALSE;
6453 ptr = *unload_trace_ex;
6454 while (ptr->BaseAddress != NULL)
6456 if (!lstrcmpW(imageW, ptr->ImageName))
6458 found = TRUE;
6459 break;
6461 ptr = (RTL_UNLOAD_EVENT_TRACE *)((char *)ptr + size);
6463 ok(found, "Unloaded module wasn't found.\n");
6467 #if defined(__i386__) || defined(__x86_64__)
6469 static const unsigned int test_extended_context_data[8] = {1, 2, 3, 4, 5, 6, 7, 8};
6470 static const unsigned test_extended_context_spoil_data1[8] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80};
6471 static const unsigned test_extended_context_spoil_data2[8] = {0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, 0x85};
6473 static BOOL test_extended_context_modified_state;
6474 static BOOL compaction_enabled;
6476 static DWORD test_extended_context_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
6477 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher)
6479 static const ULONG64 expected_compaction_mask = 0x8000000000000004;
6480 CONTEXT_EX *xctx = (CONTEXT_EX *)(context + 1);
6481 unsigned int *context_ymm_data;
6482 DWORD expected_min_offset;
6483 XSTATE *xs;
6485 ok((context->ContextFlags & (CONTEXT_FULL | CONTEXT_XSTATE)) == (CONTEXT_FULL | CONTEXT_XSTATE),
6486 "Got unexpected ContextFlags %#x.\n", context->ContextFlags);
6488 if ((context->ContextFlags & (CONTEXT_FULL | CONTEXT_XSTATE)) != (CONTEXT_FULL | CONTEXT_XSTATE))
6489 goto done;
6491 #ifdef __x86_64__
6493 /* Unwind contexts do not inherit xstate information. */
6494 DISPATCHER_CONTEXT *dispatch = (DISPATCHER_CONTEXT *)dispatcher;
6496 ok(!(dispatch->ContextRecord->ContextFlags & 0x40), "Got unexpected ContextRecord->ContextFlags %#x.\n",
6497 dispatch->ContextRecord->ContextFlags);
6499 #endif
6501 ok(xctx->Legacy.Offset == -(int)(sizeof(CONTEXT)), "Got unexpected Legacy.Offset %d.\n", xctx->Legacy.Offset);
6502 ok(xctx->Legacy.Length == sizeof(CONTEXT), "Got unexpected Legacy.Length %d.\n", xctx->Legacy.Length);
6503 ok(xctx->All.Offset == -(int)sizeof(CONTEXT), "Got unexpected All.Offset %d.\n", xctx->All.Offset);
6504 ok(xctx->All.Length == sizeof(CONTEXT) + xctx->XState.Offset + xctx->XState.Length,
6505 "Got unexpected All.Offset %d.\n", xctx->All.Offset);
6506 expected_min_offset = sizeof(void *) == 8 ? sizeof(CONTEXT_EX) + sizeof(EXCEPTION_RECORD) : sizeof(CONTEXT_EX);
6507 ok(xctx->XState.Offset >= expected_min_offset,
6508 "Got unexpected XState.Offset %d.\n", xctx->XState.Offset);
6509 ok(xctx->XState.Length >= sizeof(XSTATE), "Got unexpected XState.Length %d.\n", xctx->XState.Length);
6511 xs = (XSTATE *)((char *)xctx + xctx->XState.Offset);
6512 context_ymm_data = (unsigned int *)&xs->YmmContext;
6513 ok(!((ULONG_PTR)xs % 64), "Got unexpected xs %p.\n", xs);
6515 ok((compaction_enabled && (xs->CompactionMask & (expected_compaction_mask | 3)) == expected_compaction_mask)
6516 || (!compaction_enabled && !xs->CompactionMask), "Got unexpected CompactionMask %s, compaction %#x.\n",
6517 wine_dbgstr_longlong(xs->CompactionMask), compaction_enabled);
6519 if (test_extended_context_modified_state)
6521 ok((xs->Mask & 7) == 4, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
6522 ok(!memcmp(context_ymm_data, test_extended_context_data + 4, sizeof(M128A)),
6523 "Got unexpected context data.\n");
6525 else
6527 ok(!xs->Mask, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
6528 /* The save area has garbage in this case, the state should be restored to INIT_STATE
6529 * without using these data. */
6530 memcpy(context_ymm_data, test_extended_context_spoil_data1 + 4, sizeof(M128A));
6533 done:
6534 #ifdef __GNUC__
6535 __asm__ volatile("vmovups %0,%%ymm0" : : "m"(test_extended_context_spoil_data2));
6536 #endif
6537 #ifdef __x86_64__
6538 ++context->Rip;
6539 #else
6540 if (*(BYTE *)context->Eip == 0xcc)
6541 ++context->Eip;
6542 #endif
6543 return ExceptionContinueExecution;
6546 struct call_func_offsets
6548 unsigned int func_addr;
6549 unsigned int func_param1;
6550 unsigned int func_param2;
6551 unsigned int ymm0_save;
6553 #ifdef __x86_64__
6554 static BYTE call_func_code_set_ymm0[] =
6556 0x55, /* pushq %rbp */
6557 0x48, 0xb8, /* mov imm,%rax */
6558 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6560 0x48, 0xb9, /* mov imm,%rcx */
6561 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6563 0x48, 0xba, /* mov imm,%rdx */
6564 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6566 0x48, 0xbd, /* mov imm,%rbp */
6567 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6569 0xc5, 0xfc, 0x10, 0x45, 0x00, /* vmovups (%rbp),%ymm0 */
6570 0x48, 0x83, 0xec, 0x20, /* sub $0x20,%rsp */
6571 0xff, 0xd0, /* call *rax */
6572 0x48, 0x83, 0xc4, 0x20, /* add $0x20,%rsp */
6573 0xc5, 0xfc, 0x11, 0x45, 0x00, /* vmovups %ymm0,(%rbp) */
6574 0x5d, /* popq %rbp */
6575 0xc3, /* ret */
6577 static BYTE call_func_code_reset_ymm_state[] =
6579 0x55, /* pushq %rbp */
6580 0x48, 0xb8, /* mov imm,%rax */
6581 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6583 0x48, 0xb9, /* mov imm,%rcx */
6584 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6586 0x48, 0xba, /* mov imm,%rdx */
6587 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6589 0x48, 0xbd, /* mov imm,%rbp */
6590 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6592 0xc5, 0xf8, 0x77, /* vzeroupper */
6593 0x0f, 0x57, 0xc0, /* xorps %xmm0,%xmm0 */
6594 0x48, 0x83, 0xec, 0x20, /* sub $0x20,%rsp */
6595 0xff, 0xd0, /* call *rax */
6596 0x48, 0x83, 0xc4, 0x20, /* add $0x20,%rsp */
6597 0xc5, 0xfc, 0x11, 0x45, 0x00, /* vmovups %ymm0,(%rbp) */
6598 0x5d, /* popq %rbp */
6599 0xc3, /* ret */
6601 static const struct call_func_offsets call_func_offsets = {3, 13, 23, 33};
6602 #else
6603 static BYTE call_func_code_set_ymm0[] =
6605 0x55, /* pushl %ebp */
6606 0xb8, /* mov imm,%eax */
6607 0x00, 0x00, 0x00, 0x00,
6609 0xb9, /* mov imm,%ecx */
6610 0x00, 0x00, 0x00, 0x00,
6612 0xba, /* mov imm,%edx */
6613 0x00, 0x00, 0x00, 0x00,
6615 0xbd, /* mov imm,%ebp */
6616 0x00, 0x00, 0x00, 0x00,
6618 0x81, 0xfa, 0xef, 0xbe, 0xad, 0xde,
6619 /* cmpl $0xdeadbeef, %edx */
6620 0x74, 0x01, /* je 1f */
6621 0x52, /* pushl %edx */
6622 0x51, /* 1: pushl %ecx */
6623 0xc5, 0xfc, 0x10, 0x45, 0x00, /* vmovups (%ebp),%ymm0 */
6624 0xff, 0xd0, /* call *eax */
6625 0xc5, 0xfc, 0x11, 0x45, 0x00, /* vmovups %ymm0,(%ebp) */
6626 0x5d, /* popl %ebp */
6627 0xc3, /* ret */
6629 static BYTE call_func_code_reset_ymm_state[] =
6631 0x55, /* pushl %ebp */
6632 0xb8, /* mov imm,%eax */
6633 0x00, 0x00, 0x00, 0x00,
6635 0xb9, /* mov imm,%ecx */
6636 0x00, 0x00, 0x00, 0x00,
6638 0xba, /* mov imm,%edx */
6639 0x00, 0x00, 0x00, 0x00,
6641 0xbd, /* mov imm,%ebp */
6642 0x00, 0x00, 0x00, 0x00,
6644 0x81, 0xfa, 0xef, 0xbe, 0xad, 0xde,
6645 /* cmpl $0xdeadbeef, %edx */
6646 0x74, 0x01, /* je 1f */
6647 0x52, /* pushl %edx */
6648 0x51, /* 1: pushl %ecx */
6649 0xc5, 0xf8, 0x77, /* vzeroupper */
6650 0x0f, 0x57, 0xc0, /* xorps %xmm0,%xmm0 */
6651 0xff, 0xd0, /* call *eax */
6652 0xc5, 0xfc, 0x11, 0x45, 0x00, /* vmovups %ymm0,(%ebp) */
6653 0x5d, /* popl %ebp */
6654 0xc3, /* ret */
6656 static const struct call_func_offsets call_func_offsets = {2, 7, 12, 17};
6657 #endif
6659 static DWORD WINAPI test_extended_context_thread(void *arg)
6661 ULONG (WINAPI* func)(void) = code_mem;
6662 static unsigned int data[8];
6663 unsigned int i;
6665 memcpy(code_mem, call_func_code_reset_ymm_state, sizeof(call_func_code_reset_ymm_state));
6666 *(void **)((BYTE *)code_mem + call_func_offsets.func_addr) = SuspendThread;
6667 *(void **)((BYTE *)code_mem + call_func_offsets.func_param1) = (void *)GetCurrentThread();
6668 *(void **)((BYTE *)code_mem + call_func_offsets.func_param2) = (void *)0xdeadbeef;
6669 *(void **)((BYTE *)code_mem + call_func_offsets.ymm0_save) = data;
6670 func();
6672 for (i = 0; i < 4; ++i)
6673 ok(!data[i], "Got unexpected data %#x, i %u.\n", data[i], i);
6674 for (; i < 8; ++i)
6675 ok(data[i] == 0x48484848, "Got unexpected data %#x, i %u.\n", data[i], i);
6676 memset(data, 0x68, sizeof(data));
6678 memcpy(code_mem, call_func_code_set_ymm0, sizeof(call_func_code_set_ymm0));
6679 *(void **)((BYTE *)code_mem + call_func_offsets.func_addr) = SuspendThread;
6680 *(void **)((BYTE *)code_mem + call_func_offsets.func_param1) = (void *)GetCurrentThread();
6681 *(void **)((BYTE *)code_mem + call_func_offsets.func_param2) = (void *)0xdeadbeef;
6682 *(void **)((BYTE *)code_mem + call_func_offsets.ymm0_save) = data;
6683 func();
6685 memcpy(code_mem, call_func_code_reset_ymm_state, sizeof(call_func_code_reset_ymm_state));
6686 *(void **)((BYTE *)code_mem + call_func_offsets.func_addr) = SuspendThread;
6687 *(void **)((BYTE *)code_mem + call_func_offsets.func_param1) = (void *)GetCurrentThread();
6688 *(void **)((BYTE *)code_mem + call_func_offsets.func_param2) = (void *)0xdeadbeef;
6689 *(void **)((BYTE *)code_mem + call_func_offsets.ymm0_save) = data;
6690 func();
6691 return 0;
6694 static void wait_for_thread_next_suspend(HANDLE thread)
6696 DWORD result;
6698 result = ResumeThread(thread);
6699 ok(result == 1, "Got unexpexted suspend count %u.\n", result);
6701 /* NtQueryInformationThread(ThreadSuspendCount, ...) is not supported on older Windows. */
6702 while (!(result = SuspendThread(thread)))
6704 ResumeThread(thread);
6705 Sleep(1);
6707 ok(result == 1, "Got unexpexted suspend count %u.\n", result);
6708 result = ResumeThread(thread);
6709 ok(result == 2, "Got unexpexted suspend count %u.\n", result);
6712 #define CONTEXT_NATIVE (CONTEXT_XSTATE & CONTEXT_CONTROL)
6714 static void test_extended_context(void)
6716 static BYTE except_code_reset_ymm_state[] =
6718 #ifdef __x86_64__
6719 0x48,
6720 #endif
6721 0xb8, /* mov imm,%ax */
6722 0x00, 0x00, 0x00, 0x00,
6723 #ifdef __x86_64__
6724 0x00, 0x00, 0x00, 0x00,
6725 #endif
6726 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6728 0xc5, 0xf8, 0x77, /* vzeroupper */
6729 0x0f, 0x57, 0xc0, /* xorps %xmm0,%xmm0 */
6730 0xcc, /* int3 */
6731 0xc5, 0xfc, 0x11, 0x00, /* vmovups %ymm0,(%ax) */
6732 0xc3, /* ret */
6735 static const struct
6737 ULONG flag;
6738 ULONG supported_flags;
6739 ULONG broken_flags;
6740 ULONG context_length;
6741 ULONG legacy_length;
6742 ULONG context_ex_length;
6743 ULONG align;
6744 ULONG flags_offset;
6745 ULONG xsavearea_offset;
6746 ULONG vector_reg_count;
6748 context_arch[] =
6751 0x00100000, /* CONTEXT_AMD64 */
6752 0xd800005f,
6753 0xd8000000,
6754 0x4d0, /* sizeof(CONTEXT) */
6755 0x4d0, /* sizeof(CONTEXT) */
6756 0x20, /* sizeof(CONTEXT_EX) */
6758 0x30,
6759 0x100, /* offsetof(CONTEXT, FltSave) */
6763 0x00010000, /* CONTEXT_X86 */
6764 0xd800007f,
6765 0xd8000000,
6766 0x2cc, /* sizeof(CONTEXT) */
6767 0xcc, /* offsetof(CONTEXT, ExtendedRegisters) */
6768 0x18, /* sizeof(CONTEXT_EX) */
6771 0xcc, /* offsetof(CONTEXT, ExtendedRegisters) */
6775 ULONG expected_length, expected_length_xstate, context_flags, expected_offset;
6776 ULONG64 enabled_features, expected_compaction;
6777 DECLSPEC_ALIGN(64) BYTE context_buffer2[2048];
6778 DECLSPEC_ALIGN(64) BYTE context_buffer[2048];
6779 unsigned int i, j, address_offset, test;
6780 ULONG ret, ret2, length, length2, align;
6781 ULONG flags, flags_fpx, expected_flags;
6782 ULONG (WINAPI* func)(void) = code_mem;
6783 CONTEXT_EX *context_ex;
6784 CONTEXT *context;
6785 unsigned data[8];
6786 HANDLE thread;
6787 ULONG64 mask;
6788 XSTATE *xs;
6789 BOOL bret;
6790 void *p;
6792 address_offset = sizeof(void *) == 8 ? 2 : 1;
6793 *(void **)(except_code_set_ymm0 + address_offset) = data;
6794 *(void **)(except_code_reset_ymm_state + address_offset) = data;
6796 if (!pRtlGetEnabledExtendedFeatures)
6798 skip("RtlGetEnabledExtendedFeatures is not available.\n");
6799 return;
6802 enabled_features = pRtlGetEnabledExtendedFeatures(~(ULONG64)0);
6804 if (enabled_features)
6806 int regs[4];
6808 __cpuidex(regs, 0xd, 1);
6809 compaction_enabled = regs[0] & 2;
6812 /* Test context manipulation functions. */
6813 length = 0xdeadbeef;
6814 ret = pRtlGetExtendedContextLength(0, &length);
6815 ok(ret == STATUS_INVALID_PARAMETER && length == 0xdeadbeef, "Got unexpected result ret %#x, length %#x.\n",
6816 ret, length);
6818 for (test = 0; test < ARRAY_SIZE(context_arch); ++test)
6820 expected_length = context_arch[test].context_length + context_arch[test].context_ex_length
6821 + context_arch[test].align;
6822 expected_length_xstate = context_arch[test].context_length + context_arch[test].context_ex_length
6823 + sizeof(XSTATE) + 63;
6825 length = 0xdeadbeef;
6826 ret = pRtlGetExtendedContextLength(context_arch[test].flag, &length);
6827 ok(!ret && length == expected_length, "Got unexpected result ret %#x, length %#x.\n",
6828 ret, length);
6830 for (i = 0; i < 32; ++i)
6832 if (i == 6) /* CONTEXT_XSTATE */
6833 continue;
6835 flags = context_arch[test].flag | (1 << i);
6836 length = length2 = 0xdeadbeef;
6837 ret = pRtlGetExtendedContextLength(flags, &length);
6839 if ((context_arch[test].supported_flags & flags) || flags == context_arch[test].flag)
6841 ok((!ret && length == expected_length)
6842 || broken((context_arch[test].broken_flags & (1 << i))
6843 && ret == STATUS_INVALID_PARAMETER && length == 0xdeadbeef),
6844 "Got unexpected result ret %#x, length %#x, flags 0x%08x.\n",
6845 ret, length, flags);
6847 else
6849 ok(ret == STATUS_INVALID_PARAMETER && length == 0xdeadbeef,
6850 "Got unexpected result ret %#x, length %#x, flags 0x%08x.\n", ret, length, flags);
6853 SetLastError(0xdeadbeef);
6854 bret = pInitializeContext(NULL, flags, NULL, &length2);
6855 ok(!bret && length2 == length && GetLastError()
6856 == (!ret ? ERROR_INSUFFICIENT_BUFFER : ERROR_INVALID_PARAMETER),
6857 "Got unexpected bret %#x, length2 %#x, GetLastError() %u, flags %#x.\n",
6858 bret, length2, GetLastError(), flags);
6860 if (GetLastError() == ERROR_INVALID_PARAMETER)
6861 continue;
6863 SetLastError(0xdeadbeef);
6864 context = (void *)0xdeadbeef;
6865 length2 = expected_length - 1;
6866 bret = pInitializeContext(context_buffer, flags, &context, &length2);
6867 ok(!bret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
6868 "Got unexpected bret %#x, GetLastError() %u, flags %#x.\n", bret, GetLastError(), flags);
6869 ok(context == (void *)0xdeadbeef, "Got unexpected context %p.\n", context);
6871 SetLastError(0xdeadbeef);
6872 memset(context_buffer, 0xcc, sizeof(context_buffer));
6873 length2 = expected_length;
6874 bret = pInitializeContext(context_buffer, flags, &context, &length2);
6875 ok(bret && GetLastError() == 0xdeadbeef,
6876 "Got unexpected bret %#x, GetLastError() %u, flags %#x.\n", bret, GetLastError(), flags);
6877 ok(length2 == expected_length, "Got unexpexted length %#x.\n", length);
6878 ok((BYTE *)context == context_buffer, "Got unexpected context %p, flags %#x.\n", context, flags);
6880 context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
6881 ok(context_flags == flags, "Got unexpected ContextFlags %#x, flags %#x.\n", context_flags, flags);
6883 context_ex = (CONTEXT_EX *)(context_buffer + context_arch[test].context_length);
6884 ok(context_ex->Legacy.Offset == -(int)context_arch[test].context_length,
6885 "Got unexpected Offset %d, flags %#x.\n", context_ex->Legacy.Offset, flags);
6886 ok(context_ex->Legacy.Length == ((flags & 0x20) ? context_arch[test].context_length
6887 : context_arch[test].legacy_length),
6888 "Got unexpected Length %#x, flags %#x.\n", context_ex->Legacy.Length, flags);
6889 ok(context_ex->All.Offset == -(int)context_arch[test].context_length,
6890 "Got unexpected Offset %d, flags %#x.\n", context_ex->All.Offset, flags);
6892 /* No extra 8 bytes in x64 CONTEXT_EX here. */
6893 ok(context_ex->All.Length == context_arch[test].context_length + context_arch[1].context_ex_length,
6894 "Got unexpected Length %#x, flags %#x.\n", context_ex->All.Length, flags);
6896 ok(context_ex->XState.Offset == 25,
6897 "Got unexpected Offset %d, flags %#x.\n", context_ex->XState.Offset, flags);
6898 ok(!context_ex->XState.Length,
6899 "Got unexpected Length %#x, flags %#x.\n", context_ex->XState.Length, flags);
6901 if (0)
6903 /* Crashes on Windows. */
6904 pRtlLocateLegacyContext(NULL, NULL);
6906 p = pRtlLocateLegacyContext(context_ex, NULL);
6907 ok(p == context, "Got unexpected p %p, flags %#x.\n", p, flags);
6908 length2 = 0xdeadbeef;
6909 p = pRtlLocateLegacyContext(context_ex, &length2);
6910 ok(p == context && length2 == context_ex->Legacy.Length,
6911 "Got unexpected p %p, length %#x, flags %#x.\n", p, length2, flags);
6912 length2 = expected_length;
6914 if (0)
6916 /* Crashes on Windows. */
6917 pGetXStateFeaturesMask(context, NULL);
6918 pRtlGetExtendedFeaturesMask(context_ex);
6919 pRtlSetExtendedFeaturesMask(context_ex, 0);
6922 flags_fpx = flags & 0x10000 ? flags | 0x20 : flags | 0x8;
6924 mask = 0xdeadbeef;
6925 bret = pGetXStateFeaturesMask(context, &mask);
6926 SetLastError(0xdeadbeef);
6927 if (flags & CONTEXT_NATIVE)
6928 ok(bret && mask == ((flags & flags_fpx) == flags_fpx ? 0x3 : 0),
6929 "Got unexpected bret %#x, mask %s, flags %#x.\n", bret, wine_dbgstr_longlong(mask), flags);
6930 else
6931 ok(!bret && mask == 0xdeadbeef && GetLastError() == 0xdeadbeef,
6932 "Got unexpected bret %#x, mask %s, GetLastError() %#x, flags %#x.\n",
6933 bret, wine_dbgstr_longlong(mask), GetLastError(), flags);
6935 bret = pSetXStateFeaturesMask(context, 0);
6936 ok(bret == !!(flags & CONTEXT_NATIVE), "Got unexpected bret %#x, flags %#x.\n", bret, flags);
6937 context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
6938 ok(context_flags == flags, "Got unexpected ContextFlags %#x, flags %#x.\n", context_flags, flags);
6940 bret = pSetXStateFeaturesMask(context, 1);
6941 ok(bret == !!(flags & CONTEXT_NATIVE), "Got unexpected bret %#x, flags %#x.\n", bret, flags);
6942 context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
6943 ok(context_flags == (bret ? flags_fpx : flags),
6944 "Got unexpected ContextFlags %#x, flags %#x.\n", context_flags, flags);
6946 bret = pSetXStateFeaturesMask(context, 2);
6947 ok(bret == !!(flags & CONTEXT_NATIVE), "Got unexpected bret %#x, flags %#x.\n", bret, flags);
6948 context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
6949 ok(context_flags == (bret ? flags_fpx : flags),
6950 "Got unexpected ContextFlags %#x, flags %#x.\n", context_flags, flags);
6952 bret = pSetXStateFeaturesMask(context, 4);
6953 ok(!bret, "Got unexpected bret %#x.\n", bret);
6954 context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
6955 ok(context_flags == (flags & CONTEXT_NATIVE ? flags_fpx : flags),
6956 "Got unexpected ContextFlags %#x, flags %#x.\n", context_flags, flags);
6957 *(DWORD *)(context_buffer + context_arch[test].flags_offset) = flags;
6959 for (j = 0; j < context_arch[test].flags_offset; ++j)
6961 if (context_buffer[j] != 0xcc)
6963 ok(0, "Buffer data changed at offset %#x.\n", j);
6964 break;
6967 for (j = context_arch[test].flags_offset + sizeof(context_flags);
6968 j < context_arch[test].context_length; ++j)
6970 if (context_buffer[j] != 0xcc)
6972 ok(0, "Buffer data changed at offset %#x.\n", j);
6973 break;
6976 for (j = context_arch[test].context_length + context_arch[test].context_ex_length;
6977 j < sizeof(context_buffer); ++j)
6979 if (context_buffer[j] != 0xcc)
6981 ok(0, "Buffer data changed at offset %#x.\n", j);
6982 break;
6986 memset(context_buffer2, 0xcc, sizeof(context_buffer2));
6987 ret2 = pRtlInitializeExtendedContext(context_buffer2, flags, &context_ex);
6988 ok(!ret2, "Got unexpected ret2 %#x, flags %#x.\n", ret2, flags);
6989 ok(!memcmp(context_buffer2, context_buffer, sizeof(context_buffer2)),
6990 "Context data do not match, flags %#x.\n", flags);
6992 memset(context_buffer2, 0xcc, sizeof(context_buffer2));
6993 ret2 = pRtlInitializeExtendedContext(context_buffer2 + 2, flags, &context_ex);
6994 ok(!ret2, "Got unexpected ret2 %#x, flags %#x.\n", ret2, flags);
6996 /* Buffer gets aligned to 16 bytes on x64, while returned context length suggests it should be 8. */
6997 align = test ? 4 : 16;
6998 ok(!memcmp(context_buffer2 + align, context_buffer,
6999 sizeof(context_buffer2) - align),
7000 "Context data do not match, flags %#x.\n", flags);
7002 SetLastError(0xdeadbeef);
7003 memset(context_buffer2, 0xcc, sizeof(context_buffer2));
7004 bret = pInitializeContext(context_buffer2 + 2, flags, &context, &length2);
7005 ok(bret && GetLastError() == 0xdeadbeef,
7006 "Got unexpected bret %#x, GetLastError() %u, flags %#x.\n", bret, GetLastError(), flags);
7007 ok(length2 == expected_length, "Got unexpexted length %#x.\n", length);
7008 ok(!memcmp(context_buffer2 + align, context_buffer,
7009 sizeof(context_buffer2) - align),
7010 "Context data do not match, flags %#x.\n", flags);
7012 length2 = 0xdeadbeef;
7013 p = pLocateXStateFeature(context, 0, &length2);
7014 if (flags & CONTEXT_NATIVE)
7015 ok(p == (BYTE *)context + context_arch[test].xsavearea_offset
7016 && length2 == offsetof(XSAVE_FORMAT, XmmRegisters),
7017 "Got unexpected p %p, length %#x, flags %#x.\n", p, length2, flags);
7018 else
7019 ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#x, flags %#x.\n", p, length2, flags);
7020 length2 = 0xdeadbeef;
7021 p = pLocateXStateFeature(context, 1, &length2);
7022 if (flags & CONTEXT_NATIVE)
7023 ok(p == (BYTE *)context + context_arch[test].xsavearea_offset + offsetof(XSAVE_FORMAT, XmmRegisters)
7024 && length2 == sizeof(M128A) * context_arch[test].vector_reg_count,
7025 "Got unexpected p %p, length %#x, flags %#x.\n", p, length2, flags);
7026 else
7027 ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#x, flags %#x.\n", p, length2, flags);
7028 length2 = 0xdeadbeef;
7029 p = pLocateXStateFeature(context, 2, &length2);
7030 ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#x, flags %#x.\n", p, length2, flags);
7032 if (!pRtlInitializeExtendedContext2 || !pInitializeContext2)
7034 static int once;
7036 if (!once++)
7037 win_skip("InitializeContext2 is not available.\n");
7038 continue;
7041 length2 = expected_length;
7042 memset(context_buffer2, 0xcc, sizeof(context_buffer2));
7043 ret2 = pRtlInitializeExtendedContext2(context_buffer2 + 2, flags, &context_ex, ~(ULONG64)0);
7044 ok(!ret2, "Got unexpected ret2 %#x, flags %#x.\n", ret2, flags);
7045 ok(!memcmp(context_buffer2 + align, context_buffer,
7046 sizeof(context_buffer2) - align),
7047 "Context data do not match, flags %#x.\n", flags);
7049 memset(context_buffer2, 0xcc, sizeof(context_buffer2));
7050 bret = pInitializeContext2(context_buffer2 + 2, flags, &context, &length2, 0);
7051 ok(bret && GetLastError() == 0xdeadbeef,
7052 "Got unexpected bret %#x, GetLastError() %u, flags %#x.\n", bret, GetLastError(), flags);
7053 ok(length2 == expected_length, "Got unexpexted length %#x.\n", length);
7054 ok(!memcmp(context_buffer2 + align, context_buffer,
7055 sizeof(context_buffer2) - align),
7056 "Context data do not match, flags %#x.\n", flags);
7058 length2 = 0xdeadbeef;
7059 p = pLocateXStateFeature(context, 0, &length2);
7060 if (flags & CONTEXT_NATIVE)
7061 ok(p == (BYTE *)context + context_arch[test].xsavearea_offset
7062 && length2 == offsetof(XSAVE_FORMAT, XmmRegisters),
7063 "Got unexpected p %p, length %#x, flags %#x.\n", p, length2, flags);
7064 else
7065 ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#x, flags %#x.\n", p, length2, flags);
7068 flags = context_arch[test].flag | 0x40;
7069 flags_fpx = flags & 0x10000 ? flags | 0x20 : flags | 0x8;
7071 length = 0xdeadbeef;
7072 ret = pRtlGetExtendedContextLength(flags, &length);
7074 if (!enabled_features)
7076 ok(ret == STATUS_NOT_SUPPORTED && length == 0xdeadbeef,
7077 "Got unexpected result ret %#x, length %#x.\n", ret, length);
7079 context_ex = (void *)0xdeadbeef;
7080 ret2 = pRtlInitializeExtendedContext(context_buffer, flags, &context_ex);
7081 ok(ret2 == STATUS_NOT_SUPPORTED, "Got unexpected result ret %#x, test %u.\n", ret2, test);
7083 SetLastError(0xdeadbeef);
7084 length2 = sizeof(context_buffer);
7085 bret = pInitializeContext(context_buffer, flags, &context, &length2);
7086 ok(bret && GetLastError() == 0xdeadbeef,
7087 "Got unexpected bret %#x, GetLastError() %u, flags %#x.\n", bret, GetLastError(), flags);
7088 context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
7089 ok(context_flags == (flags & ~0x40), "Got unexpected ContextFlags %#x, flags %#x.\n",
7090 context_flags, flags);
7092 if (pInitializeContext2)
7094 SetLastError(0xdeadbeef);
7095 length2 = sizeof(context_buffer);
7096 bret = pInitializeContext2(context_buffer, flags, &context, &length2, ~(ULONG64)0);
7097 ok(bret && GetLastError() == 0xdeadbeef,
7098 "Got unexpected bret %#x, GetLastError() %u, flags %#x.\n", bret, GetLastError(), flags);
7099 context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
7100 ok(context_flags == (flags & ~0x40), "Got unexpected ContextFlags %#x, flags %#x.\n",
7101 context_flags, flags);
7103 continue;
7106 ok(!ret && length >= expected_length_xstate,
7107 "Got unexpected result ret %#x, length %#x, test %u.\n", ret, length, test);
7109 if (!pRtlGetExtendedContextLength2)
7111 win_skip("RtlGetExtendedContextLength2 is not available.\n");
7113 else
7115 length = 0xdeadbeef;
7116 ret = pRtlGetExtendedContextLength2(flags, &length, 7);
7117 ok(!ret && length == expected_length_xstate,
7118 "Got unexpected result ret %#x, length %#x, test %u.\n", ret, length, test);
7120 length = 0xdeadbeef;
7121 ret = pRtlGetExtendedContextLength2(flags, &length, ~0);
7122 ok(!ret && length >= expected_length_xstate,
7123 "Got unexpected result ret %#x, length %#x, test %u.\n", ret, length, test);
7125 length = 0xdeadbeef;
7126 ret = pRtlGetExtendedContextLength2(flags, &length, 0);
7127 ok((!ret && length == expected_length_xstate - sizeof(YMMCONTEXT))
7128 || broken(!ret && length == expected_length_xstate) /* win10pro */,
7129 "Got unexpected result ret %#x, length %#x, test %u.\n", ret, length, test);
7131 length = 0xdeadbeef;
7132 ret = pRtlGetExtendedContextLength2(flags, &length, 3);
7133 ok((!ret && length == expected_length_xstate - sizeof(YMMCONTEXT))
7134 || broken(!ret && length == expected_length_xstate) /* win10pro */,
7135 "Got unexpected result ret %#x, length %#x, test %u.\n", ret, length, test);
7137 length = 0xdeadbeef;
7138 ret = pRtlGetExtendedContextLength2(flags, &length, 4);
7139 ok(!ret && length == expected_length_xstate,
7140 "Got unexpected result ret %#x, length %#x, test %u.\n", ret, length, test);
7143 pRtlGetExtendedContextLength(flags, &length);
7144 SetLastError(0xdeadbeef);
7145 bret = pInitializeContext(NULL, flags, NULL, &length2);
7146 ok(!bret && length2 == length && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
7147 "Got unexpected bret %#x, length2 %#x, GetLastError() %u, flags %#x.\n",
7148 bret, length2, GetLastError(), flags);
7150 SetLastError(0xdeadbeef);
7151 context = (void *)0xdeadbeef;
7152 length2 = length - 1;
7153 bret = pInitializeContext(context_buffer, flags, &context, &length2);
7154 ok(!bret && GetLastError() == ERROR_INSUFFICIENT_BUFFER && length2 == length && context == (void *)0xdeadbeef,
7155 "Got unexpected bret %#x, GetLastError() %u, length2 %#x, flags %#x.\n",
7156 bret, GetLastError(), length2, flags);
7158 SetLastError(0xdeadbeef);
7159 memset(context_buffer, 0xcc, sizeof(context_buffer));
7160 length2 = length + 1;
7161 bret = pInitializeContext(context_buffer, flags, &context, &length2);
7162 ok(bret && GetLastError() == 0xdeadbeef,
7163 "Got unexpected bret %#x, GetLastError() %u, flags %#x.\n", bret, GetLastError(), flags);
7164 ok(length2 == length, "Got unexpexted length %#x.\n", length);
7165 ok((BYTE *)context == context_buffer, "Got unexpected context %p.\n", context);
7167 context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
7168 ok(context_flags == flags, "Got unexpected ContextFlags %#x, flags %#x.\n", context_flags, flags);
7170 context_ex = (CONTEXT_EX *)(context_buffer + context_arch[test].context_length);
7171 ok(context_ex->Legacy.Offset == -(int)context_arch[test].context_length,
7172 "Got unexpected Offset %d, flags %#x.\n", context_ex->Legacy.Offset, flags);
7173 ok(context_ex->Legacy.Length == ((flags & 0x20) ? context_arch[test].context_length
7174 : context_arch[test].legacy_length),
7175 "Got unexpected Length %#x, flags %#x.\n", context_ex->Legacy.Length, flags);
7177 expected_offset = (((ULONG_PTR)context + context_arch[test].context_length
7178 + context_arch[test].context_ex_length + 63) & ~(ULONG64)63) - (ULONG_PTR)context
7179 - context_arch[test].context_length;
7180 ok(context_ex->XState.Offset == expected_offset,
7181 "Got unexpected Offset %d, flags %#x.\n", context_ex->XState.Offset, flags);
7182 ok(context_ex->XState.Length >= sizeof(XSTATE),
7183 "Got unexpected Length %#x, flags %#x.\n", context_ex->XState.Length, flags);
7185 ok(context_ex->All.Offset == -(int)context_arch[test].context_length,
7186 "Got unexpected Offset %d, flags %#x.\n", context_ex->All.Offset, flags);
7187 /* No extra 8 bytes in x64 CONTEXT_EX here. */
7188 ok(context_ex->All.Length == context_arch[test].context_length
7189 + context_ex->XState.Offset + context_ex->XState.Length,
7190 "Got unexpected Length %#x, flags %#x.\n", context_ex->All.Length, flags);
7192 xs = (XSTATE *)((BYTE *)context_ex + context_ex->XState.Offset);
7193 length2 = 0xdeadbeef;
7194 for (i = 0; i < 2; ++i)
7196 p = pRtlLocateExtendedFeature(context_ex, i, &length2);
7197 ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#x.\n", p, length2);
7200 p = pRtlLocateExtendedFeature(context_ex, XSTATE_AVX, &length2);
7201 ok(length2 == sizeof(YMMCONTEXT), "Got unexpected length %#x.\n", length2);
7202 ok(p == &xs->YmmContext, "Got unexpected p %p.\n", p);
7203 p = pRtlLocateExtendedFeature(context_ex, XSTATE_AVX, NULL);
7204 ok(p == &xs->YmmContext, "Got unexpected p %p.\n", p);
7206 length2 = 0xdeadbeef;
7207 p = pLocateXStateFeature(context, 0, &length2);
7208 if (flags & CONTEXT_NATIVE)
7209 ok(p == (BYTE *)context + context_arch[test].xsavearea_offset
7210 && length2 == offsetof(XSAVE_FORMAT, XmmRegisters),
7211 "Got unexpected p %p, length %#x, flags %#x.\n", p, length2, flags);
7212 else
7213 ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#x, flags %#x.\n", p, length2, flags);
7215 length2 = 0xdeadbeef;
7216 p = pLocateXStateFeature(context, 1, &length2);
7217 if (flags & CONTEXT_NATIVE)
7218 ok(p == (BYTE *)context + context_arch[test].xsavearea_offset + offsetof(XSAVE_FORMAT, XmmRegisters)
7219 && length2 == sizeof(M128A) * context_arch[test].vector_reg_count,
7220 "Got unexpected p %p, length %#x, flags %#x.\n", p, length2, flags);
7221 else
7222 ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#x, flags %#x.\n", p, length2, flags);
7224 length2 = 0xdeadbeef;
7225 p = pLocateXStateFeature(context, 2, &length2);
7226 if (flags & CONTEXT_NATIVE)
7227 ok(p == &xs->YmmContext && length2 == sizeof(YMMCONTEXT),
7228 "Got unexpected p %p, length %#x, flags %#x.\n", p, length2, flags);
7229 else
7230 ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#x, flags %#x.\n", p, length2, flags);
7232 mask = 0xdeadbeef;
7233 bret = pGetXStateFeaturesMask(context, &mask);
7234 if (flags & CONTEXT_NATIVE)
7235 ok(bret && !mask,
7236 "Got unexpected bret %#x, mask %s, flags %#x.\n", bret, wine_dbgstr_longlong(mask), flags);
7237 else
7238 ok(!bret && mask == 0xdeadbeef,
7239 "Got unexpected bret %#x, mask %s, flags %#x.\n", bret, wine_dbgstr_longlong(mask), flags);
7241 expected_compaction = compaction_enabled ? ((ULONG64)1 << 63) | enabled_features : 0;
7242 ok(!xs->Mask, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
7243 mask = pRtlGetExtendedFeaturesMask(context_ex);
7244 ok(mask == (xs->Mask & ~(ULONG64)3), "Got unexpected mask %s.\n", wine_dbgstr_longlong(mask));
7245 ok(xs->CompactionMask == expected_compaction,
7246 "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(xs->CompactionMask));
7247 ok(!xs->Reserved[0], "Got unexpected Reserved[0] %s.\n", wine_dbgstr_longlong(xs->Reserved[0]));
7249 xs->Mask = 0xdeadbeef;
7250 xs->CompactionMask = 0xdeadbeef;
7251 pRtlSetExtendedFeaturesMask(context_ex, 0);
7252 ok(!xs->Mask, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
7253 mask = pRtlGetExtendedFeaturesMask(context_ex);
7254 ok(mask == (xs->Mask & ~(ULONG64)3), "Got unexpected mask %s.\n", wine_dbgstr_longlong(mask));
7255 ok(xs->CompactionMask == 0xdeadbeef, "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(xs->CompactionMask));
7256 context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
7257 ok(context_flags == flags, "Got unexpected ContextFlags %#x, flags %#x.\n", context->ContextFlags, flags);
7259 xs->Mask = 0xdeadbeef;
7260 xs->CompactionMask = 0;
7261 pRtlSetExtendedFeaturesMask(context_ex, ~(ULONG64)0);
7262 ok(xs->Mask == (enabled_features & ~(ULONG64)3), "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
7263 mask = pRtlGetExtendedFeaturesMask(context_ex);
7264 ok(mask == (xs->Mask & ~(ULONG64)3), "Got unexpected mask %s.\n", wine_dbgstr_longlong(mask));
7265 ok(!xs->CompactionMask, "Got unexpected CompactionMask %s.\n",
7266 wine_dbgstr_longlong(xs->CompactionMask));
7267 context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
7268 ok(context_flags == flags, "Got unexpected ContextFlags %#x, flags %#x.\n", context->ContextFlags, flags);
7270 xs->Mask = 0xdeadbeef;
7271 xs->CompactionMask = 0xdeadbeef;
7272 bret = pSetXStateFeaturesMask(context, 7);
7273 ok(bret == !!(flags & CONTEXT_NATIVE), "Got unexpected bret %#x.\n", bret);
7274 context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
7275 ok(context_flags == (bret ? flags_fpx : flags),
7276 "Got unexpected ContextFlags %#x, flags %#x.\n", context_flags, flags);
7277 ok(xs->Mask == bret ? 4 : 0xdeadbeef, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
7278 mask = pRtlGetExtendedFeaturesMask(context_ex);
7279 ok(mask == (xs->Mask & ~(ULONG64)3), "Got unexpected mask %s.\n", wine_dbgstr_longlong(mask));
7280 ok(xs->CompactionMask == bret ? expected_compaction : 0xdeadbeef, "Got unexpected CompactionMask %s.\n",
7281 wine_dbgstr_longlong(xs->CompactionMask));
7283 mask = 0xdeadbeef;
7284 bret = pGetXStateFeaturesMask(context, &mask);
7285 if (flags & CONTEXT_NATIVE)
7286 ok(bret && mask == enabled_features,
7287 "Got unexpected bret %#x, mask %s, flags %#x.\n", bret, wine_dbgstr_longlong(mask), flags);
7288 else
7289 ok(!bret && mask == 0xdeadbeef,
7290 "Got unexpected bret %#x, mask %s, flags %#x.\n", bret, wine_dbgstr_longlong(mask), flags);
7292 if (pRtlGetExtendedContextLength2)
7294 memset(context_buffer, 0xcc, sizeof(context_buffer));
7295 pRtlGetExtendedContextLength2(flags, &length, 0);
7296 SetLastError(0xdeadbeef);
7297 memset(context_buffer, 0xcc, sizeof(context_buffer));
7298 length2 = length;
7299 bret = pInitializeContext2(context_buffer, flags, &context, &length2, 0);
7300 ok(bret && GetLastError() == 0xdeadbeef,
7301 "Got unexpected bret %#x, GetLastError() %u, flags %#x.\n", bret, GetLastError(), flags);
7302 ok(length2 == length, "Got unexpexted length %#x.\n", length);
7303 ok((BYTE *)context == context_buffer, "Got unexpected context %p.\n", context);
7305 length2 = 0xdeadbeef;
7306 p = pLocateXStateFeature(context, 0, &length2);
7307 if (flags & CONTEXT_NATIVE)
7308 ok(p == (BYTE *)context + context_arch[test].xsavearea_offset
7309 && length2 == offsetof(XSAVE_FORMAT, XmmRegisters),
7310 "Got unexpected p %p, length %#x, flags %#x.\n", p, length2, flags);
7311 else
7312 ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#x, flags %#x.\n", p, length2, flags);
7314 length2 = 0xdeadbeef;
7315 p = pRtlLocateExtendedFeature(context_ex, 2, &length2);
7316 ok((!p && length2 == sizeof(YMMCONTEXT))
7317 || broken(p && length2 == sizeof(YMMCONTEXT)) /* win10pro */,
7318 "Got unexpected p %p, length %#x, flags %#x.\n", p, length2, flags);
7320 length2 = 0xdeadbeef;
7321 p = pLocateXStateFeature(context, 2, &length2);
7322 ok(!p && length2 == (flags & CONTEXT_NATIVE) ? sizeof(YMMCONTEXT) : 0xdeadbeef,
7323 "Got unexpected p %p, length %#x, flags %#x.\n", p, length2, flags);
7325 context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
7326 ok(context_flags == flags, "Got unexpected ContextFlags %#x, flags %#x.\n", context_flags, flags);
7328 context_ex = (CONTEXT_EX *)(context_buffer + context_arch[test].context_length);
7329 ok(context_ex->Legacy.Offset == -(int)context_arch[test].context_length,
7330 "Got unexpected Offset %d, flags %#x.\n", context_ex->Legacy.Offset, flags);
7331 ok(context_ex->Legacy.Length == ((flags & 0x20) ? context_arch[test].context_length
7332 : context_arch[test].legacy_length),
7333 "Got unexpected Length %#x, flags %#x.\n", context_ex->Legacy.Length, flags);
7335 expected_offset = (((ULONG_PTR)context + context_arch[test].context_length
7336 + context_arch[test].context_ex_length + 63) & ~(ULONG64)63) - (ULONG_PTR)context
7337 - context_arch[test].context_length;
7338 ok(context_ex->XState.Offset == expected_offset,
7339 "Got unexpected Offset %d, flags %#x.\n", context_ex->XState.Offset, flags);
7340 ok(context_ex->XState.Length == sizeof(XSTATE) - sizeof(YMMCONTEXT)
7341 || broken(context_ex->XState.Length == sizeof(XSTATE)) /* win10pro */,
7342 "Got unexpected Length %#x, flags %#x.\n", context_ex->XState.Length, flags);
7344 ok(context_ex->All.Offset == -(int)context_arch[test].context_length,
7345 "Got unexpected Offset %d, flags %#x.\n", context_ex->All.Offset, flags);
7346 /* No extra 8 bytes in x64 CONTEXT_EX here. */
7347 ok(context_ex->All.Length == context_arch[test].context_length
7348 + context_ex->XState.Offset + context_ex->XState.Length,
7349 "Got unexpected Length %#x, flags %#x.\n", context_ex->All.Length, flags);
7351 expected_compaction = compaction_enabled ? (ULONG64)1 << 63 : 0;
7352 xs = (XSTATE *)((BYTE *)context_ex + context_ex->XState.Offset);
7353 ok(!xs->Mask, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
7354 ok(xs->CompactionMask == expected_compaction,
7355 "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(xs->CompactionMask));
7356 ok(!xs->Reserved[0], "Got unexpected Reserved[0] %s.\n", wine_dbgstr_longlong(xs->Reserved[0]));
7358 pRtlSetExtendedFeaturesMask(context_ex, ~(ULONG64)0);
7359 ok(xs->Mask == (enabled_features & ~(ULONG64)3), "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
7360 ok(xs->CompactionMask == expected_compaction, "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(xs->CompactionMask));
7364 length = 0xdeadbeef;
7365 ret = pRtlGetExtendedContextLength(context_arch[0].flag | context_arch[1].flag, &length);
7366 ok(ret == STATUS_INVALID_PARAMETER && length == 0xdeadbeef, "Got unexpected result ret %#x, length %#x.\n",
7367 ret, length);
7369 if (0)
7371 /* Crashes on Windows. */
7372 pRtlGetExtendedContextLength(CONTEXT_FULL, NULL);
7373 length = sizeof(context_buffer);
7374 pInitializeContext(context_buffer, CONTEXT_FULL, NULL, &length);
7375 pInitializeContext(context_buffer, CONTEXT_FULL, &context, NULL);
7378 if (!(enabled_features & (1 << XSTATE_AVX)))
7380 skip("AVX is not supported.\n");
7381 return;
7384 /* Test RtlCaptureContext (doesn't support xstates). */
7385 length = sizeof(context_buffer);
7386 memset(context_buffer, 0xcc, sizeof(context_buffer));
7387 bret = pInitializeContext(context_buffer, CONTEXT_XSTATE, &context, &length);
7388 ok(bret, "Got unexpected bret %#x.\n", bret);
7389 context_ex = (CONTEXT_EX *)(context + 1);
7390 xs = (XSTATE *)((BYTE *)context_ex + context_ex->XState.Offset);
7392 *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_addr) = RtlCaptureContext;
7393 *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_param1) = context;
7394 *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_param2) = (void *)0xdeadbeef;
7395 *(void **)(call_func_code_set_ymm0 + call_func_offsets.ymm0_save) = data;
7396 memcpy(code_mem, call_func_code_set_ymm0, sizeof(call_func_code_set_ymm0));
7398 memcpy(data, test_extended_context_data, sizeof(data));
7399 func();
7400 ok(context->ContextFlags == (CONTEXT_FULL | CONTEXT_SEGMENTS), "Got unexpected ContextFlags %#x.\n",
7401 context->ContextFlags);
7402 for (i = 0; i < 8; ++i)
7403 ok(data[i] == test_extended_context_data[i], "Got unexpected data %#x, i %u.\n", data[i], i);
7405 /* Test GetThreadContext (current thread, ymm0 set). */
7406 length = sizeof(context_buffer);
7407 memset(context_buffer, 0xcc, sizeof(context_buffer));
7408 bret = pInitializeContext(context_buffer, CONTEXT_FULL | CONTEXT_XSTATE | CONTEXT_FLOATING_POINT,
7409 &context, &length);
7410 ok(bret, "Got unexpected bret %#x.\n", bret);
7411 memset(&xs->YmmContext, 0xcc, sizeof(xs->YmmContext));
7413 expected_flags = CONTEXT_FULL | CONTEXT_XSTATE | CONTEXT_FLOATING_POINT;
7414 #ifdef __i386__
7415 expected_flags |= CONTEXT_EXTENDED_REGISTERS;
7416 #endif
7417 pSetXStateFeaturesMask(context, ~(ULONG64)0);
7418 ok(context->ContextFlags == expected_flags, "Got unexpected ContextFlags %#x.\n",
7419 context->ContextFlags);
7420 *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_addr) = GetThreadContext;
7421 *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_param1) = (void *)GetCurrentThread();
7422 *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_param2) = context;
7423 *(void **)(call_func_code_set_ymm0 + call_func_offsets.ymm0_save) = data;
7424 memcpy(code_mem, call_func_code_set_ymm0, sizeof(call_func_code_set_ymm0));
7425 xs->CompactionMask = 2;
7426 if (!compaction_enabled)
7427 xs->Mask = 0;
7428 context_ex->XState.Length = sizeof(XSTATE);
7430 bret = func();
7431 ok(bret, "Got unexpected bret %#x, GetLastError() %u.\n", bret, GetLastError());
7433 ok(context->ContextFlags == expected_flags, "Got unexpected ContextFlags %#x.\n",
7434 context->ContextFlags);
7435 expected_compaction = compaction_enabled ? (ULONG64)1 << 63 : 0;
7437 ok(!xs->Mask || broken(xs->Mask == 4) /* win10pro */,
7438 "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
7439 ok(xs->CompactionMask == expected_compaction, "Got unexpected CompactionMask %s.\n",
7440 wine_dbgstr_longlong(xs->CompactionMask));
7442 for (i = 4; i < 8; ++i)
7443 ok(data[i] == test_extended_context_data[i], "Got unexpected data %#x, i %u.\n", data[i], i);
7445 for (i = 0; i < 4; ++i)
7446 ok(((ULONG *)&xs->YmmContext)[i] == (xs->Mask == 4 ? test_extended_context_data[i + 4] : 0xcccccccc),
7447 "Got unexpected data %#x, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i);
7449 expected_compaction = compaction_enabled ? ((ULONG64)1 << 63) | 4 : 0;
7451 xs->CompactionMask = 4;
7452 xs->Mask = compaction_enabled ? 0 : 4;
7453 context_ex->XState.Length = sizeof(XSTATE) + 64;
7454 bret = func();
7455 ok(!bret && GetLastError() == ERROR_INVALID_PARAMETER,
7456 "Got unexpected bret %#x, GetLastError() %u.\n", bret, GetLastError());
7457 ok(context->ContextFlags == expected_flags, "Got unexpected ContextFlags %#x.\n",
7458 context->ContextFlags);
7459 ok(xs->Mask == (compaction_enabled ? 0 : 4), "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
7460 ok(xs->CompactionMask == 4, "Got unexpected CompactionMask %s.\n",
7461 wine_dbgstr_longlong(xs->CompactionMask));
7462 for (i = 0; i < 4; ++i)
7463 ok(((ULONG *)&xs->YmmContext)[i] == 0xcccccccc
7464 || broken(((ULONG *)&xs->YmmContext)[i] == test_extended_context_data[i + 4]) /* win10pro */,
7465 "Got unexpected data %#x, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i);
7467 xs->CompactionMask = 4;
7468 xs->Mask = compaction_enabled ? 0 : 4;
7469 context_ex->XState.Length = offsetof(XSTATE, YmmContext);
7470 bret = func();
7471 ok(context->ContextFlags == expected_flags, "Got unexpected ContextFlags %#x.\n",
7472 context->ContextFlags);
7473 ok(!bret && GetLastError() == ERROR_MORE_DATA,
7474 "Got unexpected bret %#x, GetLastError() %u.\n", bret, GetLastError());
7475 ok(xs->Mask == 4, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
7476 ok(xs->CompactionMask == expected_compaction, "Got unexpected CompactionMask %s.\n",
7477 wine_dbgstr_longlong(xs->CompactionMask));
7478 for (i = 0; i < 4; ++i)
7479 ok(((ULONG *)&xs->YmmContext)[i] == 0xcccccccc
7480 || broken(((ULONG *)&xs->YmmContext)[i] == test_extended_context_data[i + 4]) /* win10pro */,
7481 "Got unexpected data %#x, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i);
7483 context_ex->XState.Length = sizeof(XSTATE);
7484 xs->CompactionMask = 4;
7485 xs->Mask = compaction_enabled ? 0 : 4;
7486 bret = func();
7487 ok(bret, "Got unexpected bret %#x, GetLastError() %u.\n", bret, GetLastError());
7489 ok(context->ContextFlags == expected_flags, "Got unexpected ContextFlags %#x.\n",
7490 context->ContextFlags);
7492 ok(xs->Mask == 4, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
7493 ok(xs->CompactionMask == expected_compaction, "Got unexpected CompactionMask %s.\n",
7494 wine_dbgstr_longlong(xs->CompactionMask));
7496 for (i = 4; i < 8; ++i)
7497 ok(data[i] == test_extended_context_data[i], "Got unexpected data %#x, i %u.\n", data[i], i);
7499 for (i = 0; i < 4; ++i)
7500 ok(((ULONG *)&xs->YmmContext)[i] == test_extended_context_data[i + 4],
7501 "Got unexpected data %#x, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i);
7503 /* Test GetThreadContext (current thread, ymm state cleared). */
7504 length = sizeof(context_buffer);
7505 memset(context_buffer, 0xcc, sizeof(context_buffer));
7506 bret = pInitializeContext(context_buffer, CONTEXT_FULL | CONTEXT_XSTATE | CONTEXT_FLOATING_POINT,
7507 &context, &length);
7508 memset(&xs->YmmContext, 0xcc, sizeof(xs->YmmContext));
7509 ok(bret, "Got unexpected bret %#x.\n", bret);
7510 pSetXStateFeaturesMask(context, ~(ULONG64)0);
7511 *(void **)(call_func_code_reset_ymm_state + call_func_offsets.func_addr) = GetThreadContext;
7512 *(void **)(call_func_code_reset_ymm_state + call_func_offsets.func_param1) = (void *)GetCurrentThread();
7513 *(void **)(call_func_code_reset_ymm_state + call_func_offsets.func_param2) = context;
7514 *(void **)(call_func_code_reset_ymm_state + call_func_offsets.ymm0_save) = data;
7515 memcpy(code_mem, call_func_code_reset_ymm_state, sizeof(call_func_code_reset_ymm_state));
7517 bret = func();
7518 ok(bret, "Got unexpected bret %#x, GetLastError() %u.\n", bret, GetLastError());
7520 expected_flags = CONTEXT_FULL | CONTEXT_XSTATE | CONTEXT_FLOATING_POINT;
7521 #ifdef __i386__
7522 expected_flags |= CONTEXT_EXTENDED_REGISTERS;
7523 #endif
7524 ok(context->ContextFlags == expected_flags, "Got unexpected ContextFlags %#x.\n",
7525 context->ContextFlags);
7527 expected_compaction = compaction_enabled ? ((ULONG64)1 << 63) | 4 : 0;
7529 xs = (XSTATE *)((BYTE *)context_ex + context_ex->XState.Offset);
7530 ok(!xs->Mask, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
7531 ok(xs->CompactionMask == expected_compaction, "Got unexpected CompactionMask %s.\n",
7532 wine_dbgstr_longlong(xs->CompactionMask));
7534 for (i = 4; i < 8; ++i)
7535 ok(!data[i], "Got unexpected data %#x, i %u.\n", data[i], i);
7537 for (i = 0; i < 4; ++i)
7538 ok(((ULONG *)&xs->YmmContext)[i] == 0xcccccccc
7539 || broken(((ULONG *)&xs->YmmContext)[i] == test_extended_context_data[i + 4]),
7540 "Got unexpected data %#x, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i);
7542 /* Test fault exception context. */
7543 memset(data, 0xff, sizeof(data));
7544 test_extended_context_modified_state = FALSE;
7545 run_exception_test(test_extended_context_handler, NULL, except_code_reset_ymm_state,
7546 ARRAY_SIZE(except_code_reset_ymm_state), PAGE_EXECUTE_READ);
7547 for (i = 0; i < 8; ++i)
7549 /* Older Windows version do not reset AVX context to INIT_STATE on x86. */
7550 ok(!data[i] || broken(i >= 4 && sizeof(void *) == 4 && data[i] == test_extended_context_spoil_data2[i]),
7551 "Got unexpected data %#x, i %u.\n", data[i], i);
7554 memcpy(data, test_extended_context_data, sizeof(data));
7555 test_extended_context_modified_state = TRUE;
7556 run_exception_test(test_extended_context_handler, NULL, except_code_set_ymm0,
7557 ARRAY_SIZE(except_code_set_ymm0), PAGE_EXECUTE_READ);
7559 for (i = 0; i < 8; ++i)
7560 ok(data[i] == test_extended_context_data[i], "Got unexpected data %#x, i %u.\n", data[i], i);
7562 /* Test GetThreadContext for the other thread. */
7563 thread = CreateThread(NULL, 0, test_extended_context_thread, 0, CREATE_SUSPENDED, NULL);
7564 ok(!!thread, "Failed to create thread.\n");
7566 bret = pInitializeContext(context_buffer, CONTEXT_FULL | CONTEXT_XSTATE | CONTEXT_FLOATING_POINT,
7567 &context, &length);
7568 ok(bret, "Got unexpected bret %#x.\n", bret);
7569 memset(&xs->YmmContext, 0xcc, sizeof(xs->YmmContext));
7570 context_ex = (CONTEXT_EX *)(context + 1);
7571 xs = (XSTATE *)((BYTE *)context_ex + context_ex->XState.Offset);
7572 pSetXStateFeaturesMask(context, 4);
7574 bret = GetThreadContext(thread, context);
7575 ok(bret, "Got unexpected bret %#x, GetLastError() %u.\n", bret, GetLastError());
7576 ok(!xs->Mask, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
7577 ok(xs->CompactionMask == expected_compaction, "Got unexpected CompactionMask %s.\n",
7578 wine_dbgstr_longlong(xs->CompactionMask));
7579 for (i = 0; i < 16 * 4; ++i)
7580 ok(((ULONG *)&xs->YmmContext)[i] == 0xcccccccc, "Got unexpected value %#x, i %u.\n",
7581 ((ULONG *)&xs->YmmContext)[i], i);
7583 pSetXStateFeaturesMask(context, 4);
7584 memset(&xs->YmmContext, 0, sizeof(xs->YmmContext));
7585 bret = SetThreadContext(thread, context);
7586 ok(bret, "Got unexpected bret %#x, GetLastError() %u.\n", bret, GetLastError());
7588 memset(&xs->YmmContext, 0xcc, sizeof(xs->YmmContext));
7589 bret = GetThreadContext(thread, context);
7590 ok(bret, "Got unexpected bret %#x, GetLastError() %u.\n", bret, GetLastError());
7591 ok(!xs->Mask || broken(xs->Mask == 4), "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
7592 ok(xs->CompactionMask == expected_compaction, "Got unexpected CompactionMask %s.\n",
7593 wine_dbgstr_longlong(xs->CompactionMask));
7594 for (i = 0; i < 16 * 4; ++i)
7595 ok(((ULONG *)&xs->YmmContext)[i] == 0xcccccccc || broken(xs->Mask == 4 && !((ULONG *)&xs->YmmContext)[i]),
7596 "Got unexpected value %#x, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i);
7598 pSetXStateFeaturesMask(context, 4);
7599 memset(&xs->YmmContext, 0x28, sizeof(xs->YmmContext));
7600 bret = SetThreadContext(thread, context);
7601 ok(bret, "Got unexpected bret %#x, GetLastError() %u.\n", bret, GetLastError());
7602 memset(&xs->YmmContext, 0xcc, sizeof(xs->YmmContext));
7603 bret = GetThreadContext(thread, context);
7604 ok(bret, "Got unexpected bret %#x, GetLastError() %u.\n", bret, GetLastError());
7605 ok(xs->Mask == 4, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
7606 ok(xs->CompactionMask == expected_compaction, "Got unexpected CompactionMask %s.\n",
7607 wine_dbgstr_longlong(xs->CompactionMask));
7608 for (i = 0; i < 16 * 4; ++i)
7609 ok(((ULONG *)&xs->YmmContext)[i] == 0x28282828, "Got unexpected value %#x, i %u.\n",
7610 ((ULONG *)&xs->YmmContext)[i], i);
7612 wait_for_thread_next_suspend(thread);
7614 bret = GetThreadContext(thread, context);
7615 ok(bret, "Got unexpected bret %#x, GetLastError() %u.\n", bret, GetLastError());
7616 pSetXStateFeaturesMask(context, 4);
7617 memset(&xs->YmmContext, 0x48, sizeof(xs->YmmContext));
7618 bret = SetThreadContext(thread, context);
7619 ok(bret, "Got unexpected bret %#x, GetLastError() %u.\n", bret, GetLastError());
7621 wait_for_thread_next_suspend(thread);
7623 memset(&xs->YmmContext, 0xcc, sizeof(xs->YmmContext));
7624 bret = GetThreadContext(thread, context);
7625 ok(bret, "Got unexpected bret %#x, GetLastError() %u.\n", bret, GetLastError());
7626 ok(xs->Mask == 4, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
7628 for (i = 0; i < 4; ++i)
7629 ok(((ULONG *)&xs->YmmContext)[i] == 0x68686868, "Got unexpected value %#x, i %u.\n",
7630 ((ULONG *)&xs->YmmContext)[i], i);
7632 wait_for_thread_next_suspend(thread);
7634 memset(&xs->YmmContext, 0xcc, sizeof(xs->YmmContext));
7635 bret = GetThreadContext(thread, context);
7636 ok(bret, "Got unexpected bret %#x, GetLastError() %u.\n", bret, GetLastError());
7637 ok(xs->Mask == (sizeof(void *) == 4 ? 4 : 0) || broken(sizeof(void *) == 4 && !xs->Mask) /* Win7u */,
7638 "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
7639 for (i = 0; i < 16 * 4; ++i)
7640 ok(((ULONG *)&xs->YmmContext)[i] == (xs->Mask ? (i < 8 * 4 ? 0 : 0x48484848) : 0xcccccccc),
7641 "Got unexpected value %#x, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i);
7643 bret = ResumeThread(thread);
7644 ok(bret, "Got unexpected bret %#x, GetLastError() %u.\n", bret, GetLastError());
7646 WaitForSingleObject(thread, INFINITE);
7647 CloseHandle(thread);
7650 struct modified_range
7652 ULONG start;
7653 ULONG flag;
7656 #define check_changes_in_range(a, b, c, d) check_changes_in_range_(__FILE__, __LINE__, a, b, c, d)
7657 static void check_changes_in_range_(const char *file, unsigned int line, const BYTE *p,
7658 const struct modified_range *range, ULONG flags, unsigned int length)
7660 ULONG range_flag, flag;
7661 unsigned int once = 0;
7662 unsigned int i;
7664 range_flag = 0;
7665 for (i = 0; i < length; i++)
7667 if (i == range->start)
7669 range_flag = range->flag;
7670 ++range;
7673 if ((flag = range_flag) == ~0)
7674 continue;
7676 if (flag & 0x80000000)
7678 if (flag & flags && p[i] == 0xcc)
7680 if (!once++)
7681 ok(broken(1), "Matched broken result at %#x, flags %#x.\n", i, flags);
7682 continue;
7684 flag = 0;
7687 if (flag & flags && p[i] != 0xcc)
7689 ok_(file, line)(0, "Got unexpected byte %#x at %#x, flags %#x.\n", p[i], i, flags);
7690 return;
7692 else if (!(flag & flags) && p[i] != 0xdd)
7694 ok_(file, line)(0, "Got unexpected byte %#x at %#x, flags %#x.\n", p[i], i, flags);
7695 return;
7698 ok_(file, line)(1, "Range matches.\n");
7701 static void test_copy_context(void)
7703 static const struct modified_range ranges_amd64[] =
7705 {0x30, ~0}, {0x38, 0x1}, {0x3a, 0x4}, {0x42, 0x1}, {0x48, 0x10}, {0x78, 0x2}, {0x98, 0x1},
7706 {0xa0, 0x2}, {0xf8, 0x1}, {0x100, 0x8}, {0x2a0, 0x80000008}, {0x4b0, 0x10}, {0x4d0, ~0},
7707 {0x4e8, 0}, {0x500, ~0}, {0x640, 0}, {0x1000, 0},
7709 static const struct modified_range ranges_x86[] =
7711 {0x0, ~0}, {0x4, 0x10}, {0x1c, 0x8}, {0x8c, 0x4}, {0x9c, 0x2}, {0xb4, 0x1}, {0xcc, 0x20}, {0x1ec, 0x80000020},
7712 {0x2cc, ~0}, {0x294, 0}, {0x1000, 0},
7714 static const struct modified_range single_range[] =
7716 {0x0, 0x1}, {0x1000, 0},
7719 static const struct
7721 ULONG flags;
7723 tests[] =
7725 /* AMD64 */
7726 {0x100000 | 0x01}, /* CONTEXT_CONTROL */
7727 {0x100000 | 0x02}, /* CONTEXT_INTEGER */
7728 {0x100000 | 0x04}, /* CONTEXT_SEGMENTS */
7729 {0x100000 | 0x08}, /* CONTEXT_FLOATING_POINT */
7730 {0x100000 | 0x10}, /* CONTEXT_DEBUG_REGISTERS */
7731 {0x100000 | 0x0b}, /* CONTEXT_FULL */
7732 {0x100000 | 0x40}, /* CONTEXT_XSTATE */
7733 {0x100000 | 0x1f}, /* CONTEXT_ALL */
7734 /* X86 */
7735 { 0x10000 | 0x01}, /* CONTEXT_CONTROL */
7736 { 0x10000 | 0x02}, /* CONTEXT_INTEGER */
7737 { 0x10000 | 0x04}, /* CONTEXT_SEGMENTS */
7738 { 0x10000 | 0x08}, /* CONTEXT_FLOATING_POINT */
7739 { 0x10000 | 0x10}, /* CONTEXT_DEBUG_REGISTERS */
7740 { 0x10000 | 0x20}, /* CONTEXT_EXTENDED_REGISTERS */
7741 { 0x10000 | 0x40}, /* CONTEXT_XSTATE */
7742 { 0x10000 | 0x3f}, /* CONTEXT_ALL */
7744 static const ULONG arch_flags[] = {0x100000, 0x10000};
7746 DECLSPEC_ALIGN(64) BYTE src_context_buffer[4096];
7747 DECLSPEC_ALIGN(64) BYTE dst_context_buffer[4096];
7748 ULONG64 enabled_features, expected_compaction;
7749 unsigned int context_length, flags_offset, i;
7750 CONTEXT_EX *src_ex, *dst_ex;
7751 XSTATE *dst_xs, *src_xs;
7752 BOOL compaction, bret;
7753 CONTEXT *src, *dst;
7754 NTSTATUS status;
7755 DWORD length;
7756 ULONG flags;
7758 if (!pRtlCopyExtendedContext)
7760 win_skip("RtlCopyExtendedContext is not available.\n");
7761 return;
7764 if (!pRtlGetEnabledExtendedFeatures)
7766 skip("RtlGetEnabledExtendedFeatures is not available.\n");
7767 return;
7770 enabled_features = pRtlGetEnabledExtendedFeatures(~(ULONG64)0);
7772 for (i = 0; i < ARRAY_SIZE(tests); ++i)
7774 flags = tests[i].flags;
7775 flags_offset = (flags & 0x100000) ? 0x30 : 0;
7777 memset(dst_context_buffer, 0xdd, sizeof(dst_context_buffer));
7778 memset(src_context_buffer, 0xcc, sizeof(src_context_buffer));
7780 status = pRtlInitializeExtendedContext(src_context_buffer, flags, &src_ex);
7781 if (enabled_features || !(flags & 0x40))
7783 ok(!status, "Got unexpected status %#x, flags %#x.\n", status, flags);
7785 else
7787 ok(status == STATUS_NOT_SUPPORTED, "Got unexpected status %#x, flags %#x.\n", status, flags);
7788 continue;
7790 status = pRtlInitializeExtendedContext(dst_context_buffer, flags, &dst_ex);
7791 ok(!status, "Got unexpected status %#x, flags %#x.\n", status, flags);
7793 src = pRtlLocateLegacyContext(src_ex, NULL);
7794 dst = pRtlLocateLegacyContext(dst_ex, NULL);
7796 *(DWORD *)((BYTE *)dst + flags_offset) = 0;
7797 *(DWORD *)((BYTE *)src + flags_offset) = 0;
7799 src_xs = (XSTATE *)((BYTE *)src_ex + src_ex->XState.Offset);
7800 memset(src_xs, 0xcc, sizeof(XSTATE));
7801 src_xs->Mask = 3;
7802 src_xs->CompactionMask = ~(ULONG64)0;
7804 status = pRtlCopyExtendedContext(dst_ex, flags, src_ex);
7805 ok(!status, "Got unexpected status %#x, flags %#x.\n", status, flags);
7807 context_length = (BYTE *)dst_ex - (BYTE *)dst + dst_ex->All.Length;
7808 check_changes_in_range((BYTE *)dst, flags & 0x100000 ? &ranges_amd64[0] : &ranges_x86[0],
7809 flags, context_length);
7811 ok(*(DWORD *)((BYTE *)dst + flags_offset) == flags, "Got unexpected ContextFlags %#x, flags %#x.\n",
7812 *(DWORD *)((BYTE *)dst + flags_offset), flags);
7814 memset(dst_context_buffer, 0xdd, sizeof(dst_context_buffer));
7815 status = pRtlInitializeExtendedContext(dst_context_buffer, flags, &dst_ex);
7816 ok(!status, "Got unexpected status %#x, flags %#x.\n", status, flags);
7817 *(DWORD *)((BYTE *)src + flags_offset) = 0;
7818 *(DWORD *)((BYTE *)dst + flags_offset) = 0;
7819 SetLastError(0xdeadbeef);
7820 bret = pCopyContext(dst, flags | 0x40, src);
7821 ok((!bret && GetLastError() == (enabled_features ? ERROR_INVALID_PARAMETER : ERROR_NOT_SUPPORTED))
7822 || broken(!bret && GetLastError() == ERROR_INVALID_PARAMETER),
7823 "Got unexpected bret %#x, GetLastError() %#x, flags %#x.\n",
7824 bret, GetLastError(), flags);
7825 ok(*(DWORD *)((BYTE *)dst + flags_offset) == 0, "Got unexpected ContextFlags %#x, flags %#x.\n",
7826 *(DWORD *)((BYTE *)dst + flags_offset), flags);
7827 check_changes_in_range((BYTE *)dst, flags & 0x100000 ? &ranges_amd64[0] : &ranges_x86[0],
7828 0, context_length);
7830 *(DWORD *)((BYTE *)dst + flags_offset) = flags & 0x110000;
7831 *(DWORD *)((BYTE *)src + flags_offset) = flags;
7832 SetLastError(0xdeadbeef);
7833 bret = pCopyContext(dst, flags, src);
7834 if (flags & 0x40)
7835 ok((!bret && GetLastError() == ERROR_MORE_DATA)
7836 || broken(!(flags & CONTEXT_NATIVE) && !bret && GetLastError() == ERROR_INVALID_PARAMETER),
7837 "Got unexpected bret %#x, GetLastError() %#x, flags %#x.\n",
7838 bret, GetLastError(), flags);
7839 else
7840 ok((bret && GetLastError() == 0xdeadbeef)
7841 || broken(!(flags & CONTEXT_NATIVE) && !bret && GetLastError() == ERROR_INVALID_PARAMETER),
7842 "Got unexpected bret %#x, GetLastError() %#x, flags %#x.\n",
7843 bret, GetLastError(), flags);
7844 if (bret)
7846 ok(*(DWORD *)((BYTE *)dst + flags_offset) == flags, "Got unexpected ContextFlags %#x, flags %#x.\n",
7847 *(DWORD *)((BYTE *)dst + flags_offset), flags);
7848 check_changes_in_range((BYTE *)dst, flags & 0x100000 ? &ranges_amd64[0] : &ranges_x86[0],
7849 flags, context_length);
7851 else
7853 ok(*(DWORD *)((BYTE *)dst + flags_offset) == (flags & 0x110000),
7854 "Got unexpected ContextFlags %#x, flags %#x.\n",
7855 *(DWORD *)((BYTE *)dst + flags_offset), flags);
7856 check_changes_in_range((BYTE *)dst, flags & 0x100000 ? &ranges_amd64[0] : &ranges_x86[0],
7857 0, context_length);
7861 for (i = 0; i < ARRAY_SIZE(arch_flags); ++i)
7863 flags = arch_flags[i] | 0x42;
7864 flags_offset = (flags & 0x100000) ? 0x30 : 0;
7865 context_length = (flags & 0x100000) ? 0x4d0 : 0x2cc;
7867 memset(dst_context_buffer, 0xdd, sizeof(dst_context_buffer));
7868 memset(src_context_buffer, 0xcc, sizeof(src_context_buffer));
7869 length = sizeof(src_context_buffer);
7870 bret = pInitializeContext(src_context_buffer, flags, &src, &length);
7871 ok(bret, "Got unexpected bret %#x, flags %#x.\n", bret, flags);
7873 length = sizeof(dst_context_buffer);
7874 bret = pInitializeContext(dst_context_buffer, flags, &dst, &length);
7875 ok(bret, "Got unexpected bret %#x, flags %#x.\n", bret, flags);
7877 dst_ex = (CONTEXT_EX *)((BYTE *)dst + context_length);
7878 src_ex = (CONTEXT_EX *)((BYTE *)src + context_length);
7880 dst_xs = (XSTATE *)((BYTE *)dst_ex + dst_ex->XState.Offset);
7881 src_xs = (XSTATE *)((BYTE *)src_ex + src_ex->XState.Offset);
7883 *(DWORD *)((BYTE *)dst + flags_offset) = 0;
7884 *(DWORD *)((BYTE *)src + flags_offset) = 0;
7886 compaction = !!(src_xs->CompactionMask & ((ULONG64)1 << 63));
7887 expected_compaction = (compaction ? ((ULONG64)1 << (ULONG64)63) | enabled_features : 0);
7889 memset(&src_xs->YmmContext, 0xcc, sizeof(src_xs->YmmContext));
7890 src_xs->CompactionMask = ~(ULONG64)0;
7892 src_xs->Mask = 0;
7893 memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext));
7894 dst_xs->CompactionMask = 0xdddddddddddddddd;
7895 dst_xs->Mask = 0xdddddddddddddddd;
7896 dst_ex->XState.Length = 0;
7897 status = pRtlCopyExtendedContext(dst_ex, flags, src_ex);
7898 ok(status == (enabled_features ? STATUS_BUFFER_OVERFLOW : STATUS_NOT_SUPPORTED),
7899 "Got unexpected status %#x, flags %#x.\n", status, flags);
7901 if (!enabled_features)
7902 continue;
7904 ok(*(DWORD *)((BYTE *)dst + flags_offset) == flags, "Got unexpected ContextFlags %#x, flags %#x.\n",
7905 *(DWORD *)((BYTE *)dst + flags_offset), flags);
7907 src_xs->Mask = ~(ULONG64)0;
7909 memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext));
7910 dst_xs->CompactionMask = 0xdddddddddddddddd;
7911 dst_xs->Mask = 0xdddddddddddddddd;
7912 dst_ex->XState.Length = 0;
7913 status = pRtlCopyExtendedContext(dst_ex, flags, src_ex);
7914 ok(status == STATUS_BUFFER_OVERFLOW, "Got unexpected status %#x, flags %#x.\n", status, flags);
7915 ok(*(DWORD *)((BYTE *)dst + flags_offset) == flags, "Got unexpected ContextFlags %#x, flags %#x.\n",
7916 *(DWORD *)((BYTE *)dst + flags_offset), flags);
7918 ok(dst_xs->Mask == 0xdddddddddddddddd, "Got unexpected Mask %s.\n",
7919 wine_dbgstr_longlong(dst_xs->Mask));
7920 ok(dst_xs->CompactionMask == 0xdddddddddddddddd, "Got unexpected CompactionMask %s.\n",
7921 wine_dbgstr_longlong(dst_xs->CompactionMask));
7922 check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range, 0, sizeof(dst_xs->YmmContext));
7924 src_xs->Mask = 3;
7925 memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext));
7926 dst_xs->CompactionMask = 0xdddddddddddddddd;
7927 dst_xs->Mask = 0xdddddddddddddddd;
7928 dst_ex->XState.Length = offsetof(XSTATE, YmmContext);
7929 status = pRtlCopyExtendedContext(dst_ex, flags, src_ex);
7930 ok(!status, "Got unexpected status %#x, flags %#x.\n", status, flags);
7931 ok(*(DWORD *)((BYTE *)dst + flags_offset) == flags, "Got unexpected ContextFlags %#x, flags %#x.\n",
7932 *(DWORD *)((BYTE *)dst + flags_offset), flags);
7933 ok(dst_xs->Mask == 0, "Got unexpected Mask %s.\n",
7934 wine_dbgstr_longlong(dst_xs->Mask));
7935 ok(dst_xs->CompactionMask == expected_compaction,
7936 "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(dst_xs->CompactionMask));
7937 check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range, 0, sizeof(dst_xs->YmmContext));
7939 memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext));
7940 dst_xs->CompactionMask = 0xdddddddddddddddd;
7941 dst_xs->Mask = 0xdddddddddddddddd;
7942 dst_ex->XState.Length = sizeof(XSTATE);
7943 status = pRtlCopyExtendedContext(dst_ex, flags, src_ex);
7944 ok(!status, "Got unexpected status %#x, flags %#x.\n", status, flags);
7945 ok(dst_xs->Mask == 0, "Got unexpected Mask %s.\n",
7946 wine_dbgstr_longlong(dst_xs->Mask));
7947 ok(dst_xs->CompactionMask == expected_compaction,
7948 "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(dst_xs->CompactionMask));
7949 check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range, 0, sizeof(dst_xs->YmmContext));
7951 src_xs->Mask = 4;
7952 memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext));
7953 dst_xs->CompactionMask = 0xdddddddddddddddd;
7954 dst_xs->Mask = 0xdddddddddddddddd;
7955 status = pRtlCopyExtendedContext(dst_ex, flags, src_ex);
7956 ok(!status, "Got unexpected status %#x, flags %#x.\n", status, flags);
7957 ok(dst_xs->Mask == 4, "Got unexpected Mask %s.\n",
7958 wine_dbgstr_longlong(dst_xs->Mask));
7959 ok(dst_xs->CompactionMask == expected_compaction,
7960 "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(dst_xs->CompactionMask));
7961 check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range, 1, sizeof(dst_xs->YmmContext));
7963 src_xs->Mask = 3;
7964 memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext));
7965 dst_xs->CompactionMask = 0xdddddddddddddddd;
7966 dst_xs->Mask = 0xdddddddddddddddd;
7967 status = pRtlCopyExtendedContext(dst_ex, flags, src_ex);
7968 ok(!status, "Got unexpected status %#x, flags %#x.\n", status, flags);
7969 ok(dst_xs->Mask == 0, "Got unexpected Mask %s.\n",
7970 wine_dbgstr_longlong(dst_xs->Mask));
7971 ok(dst_xs->CompactionMask == expected_compaction,
7972 "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(dst_xs->CompactionMask));
7973 check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range, 0, sizeof(dst_xs->YmmContext));
7976 *(DWORD *)((BYTE *)src + flags_offset) = arch_flags[i];
7978 src_xs->Mask = 7;
7979 memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext));
7980 dst_xs->CompactionMask = 0xdddddddddddddddd;
7981 dst_xs->Mask = 0xdddddddddddddddd;
7982 status = pRtlCopyExtendedContext(dst_ex, flags, src_ex);
7983 ok(!status, "Got unexpected status %#x, flags %#x.\n", status, flags);
7984 ok(dst_xs->Mask == 4, "Got unexpected Mask %s.\n",
7985 wine_dbgstr_longlong(dst_xs->Mask));
7986 ok(dst_xs->CompactionMask == expected_compaction,
7987 "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(dst_xs->CompactionMask));
7988 check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range, 1, sizeof(dst_xs->YmmContext));
7990 src_xs->Mask = 7;
7991 memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext));
7992 dst_xs->CompactionMask = 0xdddddddddddddddd;
7993 dst_xs->Mask = 0xdddddddddddddddd;
7994 SetLastError(0xdeadbeef);
7995 bret = pCopyContext(dst, flags, src);
7996 ok((bret && GetLastError() == 0xdeadbeef)
7997 || broken(!(flags & CONTEXT_NATIVE) && !bret && GetLastError() == ERROR_INVALID_PARAMETER),
7998 "Got unexpected bret %#x, GetLastError() %#x, flags %#x.\n",
7999 bret, GetLastError(), flags);
8000 ok(dst_xs->Mask == 0xdddddddddddddddd || broken(dst_xs->Mask == 4), "Got unexpected Mask %s, flags %#x.\n",
8001 wine_dbgstr_longlong(dst_xs->Mask), flags);
8002 ok(dst_xs->CompactionMask == 0xdddddddddddddddd || broken(dst_xs->CompactionMask == expected_compaction),
8003 "Got unexpected CompactionMask %s, flags %#x.\n", wine_dbgstr_longlong(dst_xs->CompactionMask), flags);
8004 check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range,
8005 dst_xs->Mask == 4, sizeof(dst_xs->YmmContext));
8008 #endif
8010 START_TEST(exception)
8012 HMODULE hntdll = GetModuleHandleA("ntdll.dll");
8013 HMODULE hkernel32 = GetModuleHandleA("kernel32.dll");
8014 #if defined(__x86_64__)
8015 HMODULE hmsvcrt = LoadLibraryA("msvcrt.dll");
8016 #endif
8018 my_argc = winetest_get_mainargs( &my_argv );
8020 if (my_argc >= 3 && !strcmp(my_argv[2], "suspend_process"))
8022 suspend_process_proc();
8023 return;
8026 code_mem = VirtualAlloc(NULL, 65536, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
8027 if(!code_mem) {
8028 trace("VirtualAlloc failed\n");
8029 return;
8032 #define X(f) p##f = (void*)GetProcAddress(hntdll, #f)
8033 X(NtGetContextThread);
8034 X(NtSetContextThread);
8035 X(NtReadVirtualMemory);
8036 X(NtClose);
8037 X(RtlUnwind);
8038 X(RtlRaiseException);
8039 X(RtlCaptureContext);
8040 X(NtTerminateProcess);
8041 X(RtlAddVectoredExceptionHandler);
8042 X(RtlRemoveVectoredExceptionHandler);
8043 X(RtlAddVectoredContinueHandler);
8044 X(RtlRemoveVectoredContinueHandler);
8045 X(RtlSetUnhandledExceptionFilter);
8046 X(NtQueryInformationProcess);
8047 X(NtQueryInformationThread);
8048 X(NtSetInformationProcess);
8049 X(NtSuspendProcess);
8050 X(NtResumeProcess);
8051 X(RtlGetUnloadEventTrace);
8052 X(RtlGetUnloadEventTraceEx);
8053 X(RtlGetEnabledExtendedFeatures);
8054 X(RtlGetExtendedContextLength);
8055 X(RtlGetExtendedContextLength2);
8056 X(RtlInitializeExtendedContext);
8057 X(RtlInitializeExtendedContext2);
8058 X(RtlLocateExtendedFeature);
8059 X(RtlLocateLegacyContext);
8060 X(RtlSetExtendedFeaturesMask);
8061 X(RtlGetExtendedFeaturesMask);
8062 X(RtlCopyExtendedContext);
8063 #undef X
8065 #define X(f) p##f = (void*)GetProcAddress(hkernel32, #f)
8066 X(IsWow64Process);
8067 if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE;
8069 X(InitializeContext);
8070 X(InitializeContext2);
8071 X(LocateXStateFeature);
8072 X(SetXStateFeaturesMask);
8073 X(GetXStateFeaturesMask);
8074 X(CopyContext);
8075 #undef X
8077 if (pRtlAddVectoredExceptionHandler && pRtlRemoveVectoredExceptionHandler)
8078 have_vectored_api = TRUE;
8079 else
8080 skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n");
8082 my_argc = winetest_get_mainargs( &my_argv );
8083 if (my_argc >= 4)
8085 void *addr;
8086 sscanf( my_argv[3], "%p", &addr );
8088 if (addr != &test_stage)
8090 skip( "child process not mapped at same address (%p/%p)\n", &test_stage, addr);
8091 return;
8094 /* child must be run under a debugger */
8095 if (!NtCurrentTeb()->Peb->BeingDebugged)
8097 ok(FALSE, "child process not being debugged?\n");
8098 return;
8101 #if defined(__i386__) || defined(__x86_64__)
8102 if (pRtlRaiseException)
8104 test_stage = 1;
8105 run_rtlraiseexception_test(0x12345);
8106 run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
8107 run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
8108 test_stage = 2;
8109 run_rtlraiseexception_test(0x12345);
8110 run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
8111 run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
8113 else skip( "RtlRaiseException not found\n" );
8114 #endif
8115 test_stage = 3;
8116 test_outputdebugstring(0, FALSE);
8117 test_stage = 4;
8118 test_outputdebugstring(2, TRUE); /* is this a Windows bug? */
8119 test_stage = 5;
8120 test_ripevent(0);
8121 test_stage = 6;
8122 test_ripevent(1);
8123 test_stage = 7;
8124 test_debug_service(0);
8125 test_stage = 8;
8126 test_debug_service(1);
8127 test_stage = 9;
8128 test_breakpoint(0);
8129 test_stage = 10;
8130 test_breakpoint(1);
8131 test_stage = 11;
8132 test_closehandle(0, (HANDLE)0xdeadbeef);
8133 test_stage = 12;
8134 test_closehandle(1, (HANDLE)0xdeadbeef);
8135 test_stage = 13;
8136 test_closehandle(0, 0); /* Special case. */
8137 #if defined(__i386__) || defined(__x86_64__)
8138 test_stage = 14;
8139 test_debuggee_xstate();
8140 test_stage = 15;
8141 test_debuggee_xstate();
8142 #endif
8144 /* rest of tests only run in parent */
8145 return;
8148 #ifdef __i386__
8150 test_unwind();
8151 test_exceptions();
8152 test_rtlraiseexception();
8153 test_debug_registers();
8154 test_debug_service(1);
8155 test_simd_exceptions();
8156 test_fpu_exceptions();
8157 test_dpe_exceptions();
8158 test_prot_fault();
8159 test_kiuserexceptiondispatcher();
8160 test_extended_context();
8161 test_copy_context();
8163 #elif defined(__x86_64__)
8165 #define X(f) p##f = (void*)GetProcAddress(hntdll, #f)
8166 X(RtlAddFunctionTable);
8167 X(RtlDeleteFunctionTable);
8168 X(RtlInstallFunctionTableCallback);
8169 X(RtlLookupFunctionEntry);
8170 X(RtlAddGrowableFunctionTable);
8171 X(RtlGrowFunctionTable);
8172 X(RtlDeleteGrowableFunctionTable);
8173 X(__C_specific_handler);
8174 X(RtlCaptureContext);
8175 X(RtlRestoreContext);
8176 X(RtlUnwindEx);
8177 X(RtlWow64GetThreadContext);
8178 X(RtlWow64SetThreadContext);
8179 #undef X
8181 p_setjmp = (void *)GetProcAddress( hmsvcrt,
8182 "_setjmp" );
8184 test_rtlraiseexception();
8185 test_debug_registers();
8186 test_debug_service(1);
8187 test_virtual_unwind();
8188 test___C_specific_handler();
8189 test_restore_context();
8190 test_prot_fault();
8191 test_dpe_exceptions();
8192 test_wow64_context();
8193 test_kiuserexceptiondispatcher();
8194 test_nested_exception();
8196 if (pRtlAddFunctionTable && pRtlDeleteFunctionTable && pRtlInstallFunctionTableCallback && pRtlLookupFunctionEntry)
8197 test_dynamic_unwind();
8198 else
8199 skip( "Dynamic unwind functions not found\n" );
8200 test_extended_context();
8201 test_copy_context();
8203 #elif defined(__aarch64__)
8205 test_virtual_unwind();
8207 #endif
8209 test_debugger();
8210 test_thread_context();
8211 test_outputdebugstring(1, FALSE);
8212 test_ripevent(1);
8213 test_breakpoint(1);
8214 test_closehandle(0, (HANDLE)0xdeadbeef);
8215 /* Call of Duty WWII writes to BeingDebugged then closes an invalid handle,
8216 * crashing the game if an exception is raised. */
8217 NtCurrentTeb()->Peb->BeingDebugged = 0x98;
8218 test_closehandle(0, (HANDLE)0xdeadbeef);
8219 NtCurrentTeb()->Peb->BeingDebugged = 0;
8221 test_vectored_continue_handler();
8222 test_suspend_thread();
8223 test_suspend_process();
8224 test_unload_trace();
8225 VirtualFree(code_mem, 0, MEM_RELEASE);