ReactOS 0.4.17-dev-116-ga4b6fe9
exception.c
Go to the documentation of this file.
1/*
2 * Unit test suite for ntdll exceptions
3 *
4 * Copyright 2005 Alexandre Julliard
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21#include <stdarg.h>
22#include <stdio.h>
23#include <setjmp.h>
24
25#include "ntstatus.h"
26#define WIN32_NO_STATUS
27#include "windef.h"
28#include "winbase.h"
29#include "winnt.h"
30#include "winreg.h"
31#include "winuser.h"
32#include "winternl.h"
33#include "rtlsupportapi.h"
34#include "ddk/wdm.h"
35#include "excpt.h"
36#include "wine/test.h"
37#include "intrin.h"
38#ifdef __REACTOS__
39#include <wine/exception.h>
40#ifdef _M_AMD64
41USHORT __readsegds(void);
42USHORT __readseges(void);
43USHORT __readsegfs(void);
44USHORT __readseggs(void);
45USHORT __readsegss(void);
46void __cld(void);
47void Call_NtRaiseException(PEXCEPTION_RECORD ExceptionRecord, PCONTEXT Context, BOOLEAN FirstChance, PVOID pNtRaiseException);
48#endif // _M_AMD64
49#endif // __REACTOS__
50
51static void *code_mem;
54
55static NTSTATUS (WINAPI *pNtGetContextThread)(HANDLE,CONTEXT*);
56static NTSTATUS (WINAPI *pNtSetContextThread)(HANDLE,CONTEXT*);
57static NTSTATUS (WINAPI *pNtQueueApcThread)(HANDLE handle, PNTAPCFUNC func,
59static NTSTATUS (WINAPI *pNtContinueEx)(CONTEXT*,KCONTINUE_ARGUMENT*);
60static NTSTATUS (WINAPI *pRtlRaiseException)(EXCEPTION_RECORD *rec);
61static PVOID (WINAPI *pRtlUnwind)(PVOID, PVOID, PEXCEPTION_RECORD, PVOID);
62static VOID (WINAPI *pRtlCaptureContext)(CONTEXT*);
63static PVOID (WINAPI *pRtlAddVectoredExceptionHandler)(ULONG first, PVECTORED_EXCEPTION_HANDLER func);
64static ULONG (WINAPI *pRtlRemoveVectoredExceptionHandler)(PVOID handler);
65static PVOID (WINAPI *pRtlAddVectoredContinueHandler)(ULONG first, PVECTORED_EXCEPTION_HANDLER func);
66static ULONG (WINAPI *pRtlRemoveVectoredContinueHandler)(PVOID handler);
67static void (WINAPI *pRtlSetUnhandledExceptionFilter)(PRTL_EXCEPTION_FILTER filter);
68static ULONG64 (WINAPI *pRtlGetEnabledExtendedFeatures)(ULONG64);
69static NTSTATUS (WINAPI *pRtlGetExtendedContextLength)(ULONG context_flags, ULONG *length);
70static NTSTATUS (WINAPI *pRtlGetExtendedContextLength2)(ULONG context_flags, ULONG *length, ULONG64 compaction_mask);
71static NTSTATUS (WINAPI *pRtlInitializeExtendedContext)(void *context, ULONG context_flags, CONTEXT_EX **context_ex);
72static NTSTATUS (WINAPI *pRtlInitializeExtendedContext2)(void *context, ULONG context_flags, CONTEXT_EX **context_ex,
74static NTSTATUS (WINAPI *pRtlCopyContext)(CONTEXT *dst, DWORD context_flags, CONTEXT *src);
75static NTSTATUS (WINAPI *pRtlCopyExtendedContext)(CONTEXT_EX *dst, ULONG context_flags, CONTEXT_EX *src);
78static void (WINAPI *pRtlSetExtendedFeaturesMask)(CONTEXT_EX *context_ex, ULONG64 feature_mask);
79static ULONG64 (WINAPI *pRtlGetExtendedFeaturesMask)(CONTEXT_EX *context_ex);
81static void (WINAPI *pRtlGetCallersAddress)(void**,void**);
82static NTSTATUS (WINAPI *pNtRaiseException)(EXCEPTION_RECORD *rec, CONTEXT *context, BOOL first_chance);
83static NTSTATUS (WINAPI *pNtReadVirtualMemory)(HANDLE, const void*, void*, SIZE_T, SIZE_T*);
84static NTSTATUS (WINAPI *pNtTerminateProcess)(HANDLE handle, LONG exit_code);
85static NTSTATUS (WINAPI *pNtQueryInformationThread)(HANDLE, THREADINFOCLASS, PVOID, ULONG, PULONG);
86static NTSTATUS (WINAPI *pNtSetInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG);
87static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL);
88static NTSTATUS (WINAPI *pNtClose)(HANDLE);
89static NTSTATUS (WINAPI *pNtSuspendProcess)(HANDLE process);
90static NTSTATUS (WINAPI *pNtResumeProcess)(HANDLE process);
91static BOOL (WINAPI *pInitializeContext)(void *buffer, DWORD context_flags, CONTEXT **context,
92 DWORD *length);
93static BOOL (WINAPI *pInitializeContext2)(void *buffer, DWORD context_flags, CONTEXT **context,
96static BOOL (WINAPI *pSetXStateFeaturesMask)(CONTEXT *context, DWORD64 feature_mask);
97static BOOL (WINAPI *pGetXStateFeaturesMask)(CONTEXT *context, DWORD64 *feature_mask);
98static BOOL (WINAPI *pWaitForDebugEventEx)(DEBUG_EVENT *, DWORD);
99#ifndef __i386__
100static VOID (WINAPI *pRtlUnwindEx)(VOID*, VOID*, EXCEPTION_RECORD*, VOID*, CONTEXT*, UNWIND_HISTORY_TABLE*);
101static BOOLEAN (CDECL *pRtlAddFunctionTable)(RUNTIME_FUNCTION*, DWORD, DWORD64);
102static BOOLEAN (CDECL *pRtlDeleteFunctionTable)(RUNTIME_FUNCTION*);
103static VOID (CDECL *pRtlRestoreContext)(CONTEXT*, EXCEPTION_RECORD*);
104static NTSTATUS (WINAPI *pRtlGetNativeSystemInformation)(SYSTEM_INFORMATION_CLASS,void*,ULONG,ULONG*);
105#endif
106
110
111#define RTL_UNLOAD_EVENT_TRACE_NUMBER 64
112
114{
122
124static void (WINAPI *pRtlGetUnloadEventTraceEx)(ULONG **element_size, ULONG **element_count, void **event_trace);
125
126#if defined(__x86_64__)
127
128typedef union _UNWIND_CODE
129{
130 struct
131 {
133 BYTE UnwindOp : 4;
134 BYTE OpInfo : 4;
135 } s;
138
139typedef struct _UNWIND_INFO
140{
141 BYTE Version : 3;
142 BYTE Flags : 5;
146 BYTE FrameOffset : 4;
147 UNWIND_CODE UnwindCode[1]; /* actually CountOfCodes (aligned) */
148/*
149 * union
150 * {
151 * OPTIONAL ULONG ExceptionHandler;
152 * OPTIONAL ULONG FunctionEntry;
153 * };
154 * OPTIONAL ULONG ExceptionData[];
155 */
157
159static NTSTATUS (WINAPI *pRtlWow64GetThreadContext)(HANDLE, WOW64_CONTEXT *);
160static NTSTATUS (WINAPI *pRtlWow64SetThreadContext)(HANDLE, const WOW64_CONTEXT *);
161static NTSTATUS (WINAPI *pRtlWow64GetCpuAreaInfo)(WOW64_CPURESERVED*,ULONG,WOW64_CPU_AREA_INFO*);
162#endif
163
165{
184};
185
186static int my_argc;
187static char** my_argv;
189static BOOL old_wow64; /* Wine old-style wow64 */
193
195{
196 ok( arg1 == 0x1234 + apc_count, "wrong arg1 %Ix\n", arg1 );
197 ok( arg2 == 0x5678, "wrong arg2 %Ix\n", arg2 );
198 ok( arg3 == 0xdeadbeef, "wrong arg3 %Ix\n", arg3 );
199 apc_count++;
200}
201
202#if defined(__i386__) || defined(__x86_64__)
203static void test_debugger_xstate(HANDLE thread, CONTEXT *ctx, enum debugger_stages stage)
204{
205 char context_buffer[sizeof(CONTEXT) + sizeof(CONTEXT_EX) + sizeof(XSTATE) + 3072];
206 CONTEXT_EX *c_ex;
208 YMMCONTEXT *ymm;
209 CONTEXT *xctx;
211 XSTATE *xs;
212 M128A *xmm;
213 BOOL bret;
214
215 if (!pRtlGetEnabledExtendedFeatures || !pRtlGetEnabledExtendedFeatures(1 << XSTATE_AVX))
216 return;
217
218 if (stage == STAGE_XSTATE)
219 return;
220
221 length = sizeof(context_buffer);
222 bret = pInitializeContext(context_buffer, ctx->ContextFlags | CONTEXT_XSTATE, &xctx, &length);
223 ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
224
226 ok(!!ymm, "Got zero ymm.\n");
227 memset(ymm, 0xcc, sizeof(*ymm));
228
230 ok(length == sizeof(*xmm) * (sizeof(void *) == 8 ? 16 : 8), "Got unexpected length %#lx.\n", length);
231 ok(!!xmm, "Got zero xmm.\n");
232 memset(xmm, 0xcc, length);
233
234 status = pNtGetContextThread(thread, xctx);
235 ok(!status, "NtSetContextThread failed with 0x%lx\n", status);
236
237 c_ex = (CONTEXT_EX *)(xctx + 1);
238 xs = (XSTATE *)((char *)c_ex + c_ex->XState.Offset);
239 ok((xs->Mask & 7) == 4 || broken(!xs->Mask) /* Win7 */,
240 "Got unexpected xs->Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
241
242 ok(xmm[0].Low == 0x200000001, "Got unexpected data %s.\n", wine_dbgstr_longlong(xmm[0].Low));
243 ok(xmm[0].High == 0x400000003, "Got unexpected data %s.\n", wine_dbgstr_longlong(xmm[0].High));
244
245 ok(ymm->Ymm0.Low == 0x600000005 || broken(!xs->Mask && ymm->Ymm0.Low == 0xcccccccccccccccc) /* Win7 */,
246 "Got unexpected data %s.\n", wine_dbgstr_longlong(ymm->Ymm0.Low));
247 ok(ymm->Ymm0.High == 0x800000007 || broken(!xs->Mask && ymm->Ymm0.High == 0xcccccccccccccccc) /* Win7 */,
248 "Got unexpected data %s.\n", wine_dbgstr_longlong(ymm->Ymm0.High));
249
251 ok(!!xmm, "Got zero xmm.\n");
252
253 xmm[0].Low = 0x2828282828282828;
254 xmm[0].High = xmm[0].Low;
255 ymm->Ymm0.Low = 0x4848484848484848;
256 ymm->Ymm0.High = ymm->Ymm0.Low;
257
258 status = pNtSetContextThread(thread, xctx);
259 ok(!status, "NtSetContextThread failed with 0x%lx\n", status);
260}
261
262#define check_context_exception_request( a, b ) check_context_exception_request_( a, b, __LINE__ )
263static void check_context_exception_request_( DWORD flags, BOOL hardware_exception, unsigned int line )
264{
265 static const DWORD exception_reporting_flags = CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING
268
269 if (!(flags & CONTEXT_EXCEPTION_REPORTING)) return;
270 expected_flags |= hardware_exception ? CONTEXT_EXCEPTION_ACTIVE : CONTEXT_SERVICE_ACTIVE;
271 ok_(__FILE__, line)( (flags & exception_reporting_flags) == expected_flags, "got %#lx, expected %#lx.\n",
272 flags, expected_flags );
273}
274
275static BOOL test_hwbpt_in_syscall_trap;
276
277static LONG WINAPI test_hwbpt_in_syscall_handler( EXCEPTION_POINTERS *eptr )
278{
280
281 test_hwbpt_in_syscall_trap = TRUE;
282 ok(rec->ExceptionCode == EXCEPTION_SINGLE_STEP, "got %#lx.\n", rec->ExceptionCode);
284}
285
286static void test_hwbpt_in_syscall(void)
287{
288 TEB *teb = NtCurrentTeb();
290 void *handler;
291 CONTEXT c;
292 DWORD ind;
293 BOOL bret;
294
295 ind = TlsAlloc();
296 ok(ind < ARRAY_SIZE(teb->TlsSlots), "got %lu.\n", ind);
297 handler = AddVectoredExceptionHandler(TRUE, test_hwbpt_in_syscall_handler);
298 memset(&c, 0, sizeof(c));
299 c.ContextFlags = CONTEXT_DEBUG_REGISTERS;
300 c.Dr0 = (ULONG_PTR)&teb->TlsSlots[ind];
301 c.Dr7 = 3 | (3 << 16) | (3 << 18); /* read / write 4 byte breakpoint. */
303 ok(bret, "got error %lu.\n", GetLastError());
304 test_hwbpt_in_syscall_trap = FALSE;
305 teb->TlsSlots[ind] = (void *)0xdeadbeef;
306 ok(test_hwbpt_in_syscall_trap, "expected trap.\n");
307
308 test_hwbpt_in_syscall_trap = FALSE;
310 ok(!status, "got %#lx.\n", status);
311 ok(!test_hwbpt_in_syscall_trap, "got trap.\n");
312 c.Dr7 = 0;
314 ok(bret, "got error %lu.\n", GetLastError());
315 ok(!teb->TlsSlots[ind], "got %p.\n", teb->TlsSlots[ind]);
316 RemoveVectoredExceptionHandler(handler);
317 TlsFree(ind);
318}
319#endif
320
321#ifdef __i386__
322
323#ifndef __WINE_WINTRNL_H
324#define ProcessExecuteFlags 0x22
325#define MEM_EXECUTE_OPTION_DISABLE 0x01
326#define MEM_EXECUTE_OPTION_ENABLE 0x02
327#define MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION 0x04
328#define MEM_EXECUTE_OPTION_PERMANENT 0x08
329#endif
330
331/* Test various instruction combinations that cause a protection fault on the i386,
332 * and check what the resulting exception looks like.
333 */
334
335static const struct exception
336{
337 BYTE code[18]; /* asm code */
338 BYTE offset; /* offset of faulting instruction */
339 BYTE length; /* length of faulting instruction */
340 BOOL wow64_broken; /* broken on Wow64, should be skipped */
341 NTSTATUS status; /* expected status code */
342 DWORD nb_params; /* expected number of parameters */
343 DWORD params[4]; /* expected parameters */
344 NTSTATUS alt_status; /* alternative status code */
345 DWORD alt_nb_params; /* alternative number of parameters */
346 DWORD alt_params[4]; /* alternative parameters */
347} exceptions[] =
348{
349/* 0 */
350 /* test some privileged instructions */
351 { { 0xfb, 0xc3 }, /* 0: sti; ret */
353 { { 0x6c, 0xc3 }, /* 1: insb (%dx); ret */
355 { { 0x6d, 0xc3 }, /* 2: insl (%dx); ret */
357 { { 0x6e, 0xc3 }, /* 3: outsb (%dx); ret */
359 { { 0x6f, 0xc3 }, /* 4: outsl (%dx); ret */
361/* 5 */
362 { { 0xe4, 0x11, 0xc3 }, /* 5: inb $0x11,%al; ret */
364 { { 0xe5, 0x11, 0xc3 }, /* 6: inl $0x11,%eax; ret */
366 { { 0xe6, 0x11, 0xc3 }, /* 7: outb %al,$0x11; ret */
368 { { 0xe7, 0x11, 0xc3 }, /* 8: outl %eax,$0x11; ret */
370 { { 0xed, 0xc3 }, /* 9: inl (%dx),%eax; ret */
372/* 10 */
373 { { 0xee, 0xc3 }, /* 10: outb %al,(%dx); ret */
375 { { 0xef, 0xc3 }, /* 11: outl %eax,(%dx); ret */
377 { { 0xf4, 0xc3 }, /* 12: hlt; ret */
379 { { 0xfa, 0xc3 }, /* 13: cli; ret */
381
382 /* test long jump to invalid selector */
383 { { 0xea, 0, 0, 0, 0, 0, 0, 0xc3 }, /* 14: ljmp $0,$0; ret */
384 0, 7, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
385
386/* 15 */
387 /* test iret to invalid selector */
388 { { 0x6a, 0x00, 0x6a, 0x00, 0x6a, 0x00, 0xcf, 0x83, 0xc4, 0x0c, 0xc3 },
389 /* 15: pushl $0; pushl $0; pushl $0; iret; addl $12,%esp; ret */
390 6, 1, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
391
392 /* test loading an invalid selector */
393 { { 0xb8, 0xef, 0xbe, 0x00, 0x00, 0x8e, 0xe8, 0xc3 }, /* 16: mov $beef,%ax; mov %ax,%gs; ret */
394 5, 2, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xbee8 } }, /* 0xbee8 or 0xffffffff */
395
396 /* test accessing a zero selector (%es broken on Wow64) */
397 { { 0x06, 0x31, 0xc0, 0x8e, 0xc0, 0x26, 0xa1, 0, 0, 0, 0, 0x07, 0xc3 },
398 /* push %es; xor %eax,%eax; mov %ax,%es; mov %es:(0),%ax; pop %es; ret */
399 5, 6, TRUE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
400 { { 0x0f, 0xa8, 0x31, 0xc0, 0x8e, 0xe8, 0x65, 0xa1, 0, 0, 0, 0, 0x0f, 0xa9, 0xc3 },
401 /* push %gs; xor %eax,%eax; mov %ax,%gs; mov %gs:(0),%ax; pop %gs; ret */
402 6, 6, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
403
404 /* test moving %cs -> %ss */
405 { { 0x0e, 0x17, 0x58, 0xc3 }, /* pushl %cs; popl %ss; popl %eax; ret */
406 1, 1, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
407
408/* 20 */
409 /* test overlong instruction (limit is 15 bytes, 5 on Win7) */
410 { { 0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0xfa,0xc3 },
411 0, 16, TRUE, STATUS_ILLEGAL_INSTRUCTION, 0, { 0 },
412 STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
413 { { 0x64,0x64,0x64,0x64,0xfa,0xc3 },
415
416 /* test invalid interrupt */
417 { { 0xcd, 0xff, 0xc3 }, /* int $0xff; ret */
418 0, 2, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
419
420 /* test moves to/from Crx */
421 { { 0x0f, 0x20, 0xc0, 0xc3 }, /* movl %cr0,%eax; ret */
423 { { 0x0f, 0x20, 0xe0, 0xc3 }, /* movl %cr4,%eax; ret */
425/* 25 */
426 { { 0x0f, 0x22, 0xc0, 0xc3 }, /* movl %eax,%cr0; ret */
428 { { 0x0f, 0x22, 0xe0, 0xc3 }, /* movl %eax,%cr4; ret */
430
431 /* test moves to/from Drx */
432 { { 0x0f, 0x21, 0xc0, 0xc3 }, /* movl %dr0,%eax; ret */
434 { { 0x0f, 0x21, 0xc8, 0xc3 }, /* movl %dr1,%eax; ret */
436 { { 0x0f, 0x21, 0xf8, 0xc3 }, /* movl %dr7,%eax; ret */
438/* 30 */
439 { { 0x0f, 0x23, 0xc0, 0xc3 }, /* movl %eax,%dr0; ret */
441 { { 0x0f, 0x23, 0xc8, 0xc3 }, /* movl %eax,%dr1; ret */
443 { { 0x0f, 0x23, 0xf8, 0xc3 }, /* movl %eax,%dr7; ret */
445
446 /* test memory reads */
447 { { 0xa1, 0xfc, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xfffffffc,%eax; ret */
448 0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffc } },
449 { { 0xa1, 0xfd, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xfffffffd,%eax; ret */
450 0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffd } },
451/* 35 */
452 { { 0xa1, 0xfe, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xfffffffe,%eax; ret */
453 0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffe } },
454 { { 0xa1, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xffffffff,%eax; ret */
455 0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
456
457 /* test memory writes */
458 { { 0xa3, 0xfc, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xfffffffc; ret */
459 0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffc } },
460 { { 0xa3, 0xfd, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xfffffffd; ret */
461 0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffd } },
462 { { 0xa3, 0xfe, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xfffffffe; ret */
463 0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffe } },
464/* 40 */
465 { { 0xa3, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xffffffff; ret */
466 0, 5, FALSE, STATUS_ACCESS_VIOLATION, 2, { 1, 0xffffffff } },
467
468 /* test exception with cleared %ds and %es (broken on Wow64) */
469 { { 0x1e, 0x06, 0x31, 0xc0, 0x8e, 0xd8, 0x8e, 0xc0, 0xfa, 0x07, 0x1f, 0xc3 },
470 /* push %ds; push %es; xorl %eax,%eax; mov %ax,%ds; mov %ax,%es; cli; pop %es; pop %ds; ret */
472
473 { { 0xf1, 0x90, 0xc3 }, /* icebp; nop; ret */
474 1, 1, FALSE, STATUS_SINGLE_STEP, 0 },
475 { { 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, /* mov $0xb8b8b8b8, %eax */
476 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, /* mov $0xb9b9b9b9, %ecx */
477 0xba, 0xba, 0xba, 0xba, 0xba, /* mov $0xbabababa, %edx */
478 0xcd, 0x2d, 0xc3 }, /* int $0x2d; ret */
479 17, 0, FALSE, STATUS_BREAKPOINT, 3, { 0xb8b8b8b8, 0xb9b9b9b9, 0xbabababa } },
480};
481
482static int got_exception;
483
484static void run_exception_test(void *handler, const void* context,
485 const void *code, unsigned int code_size,
487{
488 struct {
490 const void *context;
491 } exc_frame;
492 void (*func)(void) = code_mem;
493 DWORD oldaccess, oldaccess2;
494
495 exc_frame.frame.Handler = handler;
496 exc_frame.frame.Prev = NtCurrentTeb()->Tib.ExceptionList;
497 exc_frame.context = context;
498
499 memcpy(code_mem, code, code_size);
500 if(access)
501 VirtualProtect(code_mem, code_size, access, &oldaccess);
502
503 NtCurrentTeb()->Tib.ExceptionList = &exc_frame.frame;
504 func();
505 NtCurrentTeb()->Tib.ExceptionList = exc_frame.frame.Prev;
506
507 if(access)
508 VirtualProtect(code_mem, code_size, oldaccess, &oldaccess2);
509}
510
511static LONG CALLBACK rtlraiseexception_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
512{
513 PCONTEXT context = ExceptionInfo->ContextRecord;
514 PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
515
516 ok(rec->ExceptionAddress == (char *)code_mem + 0xb
517 || broken(rec->ExceptionAddress == code_mem || !rec->ExceptionAddress) /* 2008 */,
518 "ExceptionAddress at %p instead of %p\n", rec->ExceptionAddress, (char *)code_mem + 0xb);
519
521 ok((void *)context->Eax == pRtlRaiseException ||
522 broken( is_wow64 && context->Eax == 0xf00f00f1 ), /* broken on vista */
523 "debugger managed to modify Eax to %lx should be %p\n",
524 context->Eax, pRtlRaiseException);
525
526 /* check that context.Eip is fixed up only for EXCEPTION_BREAKPOINT
527 * even if raised by RtlRaiseException
528 */
530 {
531 ok(context->Eip == (DWORD)code_mem + 0xa ||
532 (is_wow64 && context->Eip == (DWORD)code_mem + 0xb) ||
533 broken(context->Eip == (DWORD)code_mem + 0xd) /* w2008 */,
534 "Eip at %lx instead of %lx or %lx\n", context->Eip,
535 (DWORD)code_mem + 0xa, (DWORD)code_mem + 0xb);
536 }
537 else
538 {
539 ok(context->Eip == (DWORD)code_mem + 0xb ||
540 broken(context->Eip == (DWORD)code_mem + 0xd) /* w2008 */,
541 "Eip at %lx instead of %lx\n", context->Eip, (DWORD)code_mem + 0xb);
542 }
543
544 /* test if context change is preserved from vectored handler to stack handlers */
545 context->Eax = 0xf00f00f0;
547}
548
549static DWORD rtlraiseexception_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
551{
552 trace( "exception: %08lx flags:%lx addr:%p context: Eip:%lx\n",
553 rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, context->Eip );
554
555 ok(rec->ExceptionAddress == (char *)code_mem + 0xb
556 || broken(rec->ExceptionAddress == code_mem || !rec->ExceptionAddress) /* 2008 */,
557 "ExceptionAddress at %p instead of %p\n", rec->ExceptionAddress, (char *)code_mem + 0xb);
558
559 ok( context->ContextFlags == CONTEXT_ALL || context->ContextFlags == (CONTEXT_ALL | CONTEXT_XSTATE) ||
560 broken(context->ContextFlags == CONTEXT_FULL), /* win2003 */
561 "wrong context flags %lx\n", context->ContextFlags );
562
563 /* check that context.Eip is fixed up only for EXCEPTION_BREAKPOINT
564 * even if raised by RtlRaiseException
565 */
567 {
568 ok(context->Eip == (DWORD)code_mem + 0xa ||
569 (is_wow64 && context->Eip == (DWORD)code_mem + 0xb) ||
570 broken(context->Eip == (DWORD)code_mem + 0xd) /* w2008 */,
571 "Eip at %lx instead of %lx or %lx\n", context->Eip,
572 (DWORD)code_mem + 0xa, (DWORD)code_mem + 0xb);
573 }
574 else
575 {
576 ok(context->Eip == (DWORD)code_mem + 0xb ||
577 broken(context->Eip == (DWORD)code_mem + 0xd) /* w2008 */,
578 "Eip at %lx instead of %lx\n", context->Eip, (DWORD)code_mem + 0xb);
579 }
580
582 ok(context->Eax == 0xf00f00f0, "Eax is %lx, should have been set to 0xf00f00f0 in vectored handler\n",
583 context->Eax);
584
585 /* give the debugger a chance to examine the state a second time */
586 /* without the exception handler changing Eip */
589
590 /* Eip in context is decreased by 1
591 * Increase it again, else execution will continue in the middle of an instruction */
592 if(rec->ExceptionCode == EXCEPTION_BREAKPOINT && (context->Eip == (DWORD)code_mem + 0xa))
593 context->Eip += 1;
595}
596
597
598static const BYTE call_one_arg_code[] = {
599 0x8b, 0x44, 0x24, 0x08, /* mov 0x8(%esp),%eax */
600 0x50, /* push %eax */
601 0x8b, 0x44, 0x24, 0x08, /* mov 0x8(%esp),%eax */
602 0xff, 0xd0, /* call *%eax */
603 0x90, /* nop */
604 0x90, /* nop */
605 0x90, /* nop */
606 0x90, /* nop */
607 0xc3, /* ret */
608};
609
610
611static void run_rtlraiseexception_test(DWORD exceptioncode)
612{
615 PVOID vectored_handler = NULL;
616
617 void (*func)(void* function, EXCEPTION_RECORD* record) = code_mem;
618
619 record.ExceptionCode = exceptioncode;
620 record.ExceptionFlags = 0;
621 record.ExceptionRecord = NULL;
622 record.ExceptionAddress = NULL; /* does not matter, copied return address */
623 record.NumberParameters = 0;
624
625 frame.Handler = rtlraiseexception_handler;
626 frame.Prev = NtCurrentTeb()->Tib.ExceptionList;
627
628 memcpy(code_mem, call_one_arg_code, sizeof(call_one_arg_code));
629
630 NtCurrentTeb()->Tib.ExceptionList = &frame;
632 {
633 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &rtlraiseexception_vectored_handler);
634 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
635 }
636
637 func(pRtlRaiseException, &record);
638 ok( record.ExceptionAddress == (char *)code_mem + 0x0b,
639 "address set to %p instead of %p\n", record.ExceptionAddress, (char *)code_mem + 0x0b );
640
642 pRtlRemoveVectoredExceptionHandler(vectored_handler);
643 NtCurrentTeb()->Tib.ExceptionList = frame.Prev;
644}
645
646static void test_rtlraiseexception(void)
647{
648 if (!pRtlRaiseException)
649 {
650 skip("RtlRaiseException not found\n");
651 return;
652 }
653
654 /* test without debugger */
655 run_rtlraiseexception_test(0x12345);
656 run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
657 run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
658}
659
660static DWORD unwind_expected_eax;
661
662static DWORD unwind_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
664{
665 trace("exception: %08lx flags:%lx addr:%p context: Eip:%lx\n",
667
668 ok(rec->ExceptionCode == STATUS_UNWIND, "ExceptionCode is %08lx instead of %08lx\n",
670 ok(rec->ExceptionAddress == (char *)code_mem + 0x22 || broken(TRUE) /* Win10 1709 */,
671 "ExceptionAddress at %p instead of %p\n", rec->ExceptionAddress, (char *)code_mem + 0x22);
672 ok(context->Eip == (DWORD)code_mem + 0x22, "context->Eip is %08lx instead of %08lx\n",
673 context->Eip, (DWORD)code_mem + 0x22);
674 ok(context->Eax == unwind_expected_eax, "context->Eax is %08lx instead of %08lx\n",
675 context->Eax, unwind_expected_eax);
676
677 context->Eax += 1;
679}
680
681static const BYTE call_unwind_code[] = {
682 0x55, /* push %ebp */
683 0x53, /* push %ebx */
684 0x56, /* push %esi */
685 0x57, /* push %edi */
686 0xe8, 0x00, 0x00, 0x00, 0x00, /* call 0 */
687 0x58, /* 0: pop %eax */
688 0x05, 0x1e, 0x00, 0x00, 0x00, /* add $0x1e,%eax */
689 0xff, 0x74, 0x24, 0x20, /* push 0x20(%esp) */
690 0xff, 0x74, 0x24, 0x20, /* push 0x20(%esp) */
691 0x50, /* push %eax */
692 0xff, 0x74, 0x24, 0x24, /* push 0x24(%esp) */
693 0x8B, 0x44, 0x24, 0x24, /* mov 0x24(%esp),%eax */
694 0xff, 0xd0, /* call *%eax */
695 0x5f, /* pop %edi */
696 0x5e, /* pop %esi */
697 0x5b, /* pop %ebx */
698 0x5d, /* pop %ebp */
699 0xc3, /* ret */
700 0xcc, /* int $3 */
701};
702
703static void test_unwind(void)
704{
705 EXCEPTION_REGISTRATION_RECORD frames[2], *frame2 = &frames[0], *frame1 = &frames[1];
708
709 memcpy(code_mem, call_unwind_code, sizeof(call_unwind_code));
710
711 /* add first unwind handler */
712 frame1->Handler = unwind_handler;
713 frame1->Prev = NtCurrentTeb()->Tib.ExceptionList;
714 NtCurrentTeb()->Tib.ExceptionList = frame1;
715
716 /* add second unwind handler */
717 frame2->Handler = unwind_handler;
718 frame2->Prev = NtCurrentTeb()->Tib.ExceptionList;
719 NtCurrentTeb()->Tib.ExceptionList = frame2;
720
721 /* test unwind to current frame */
722 unwind_expected_eax = 0xDEAD0000;
723 retval = func(pRtlUnwind, frame2, NULL, 0xDEAD0000);
724 ok(retval == 0xDEAD0000, "RtlUnwind returned eax %08lx instead of %08x\n", retval, 0xDEAD0000);
725 ok(NtCurrentTeb()->Tib.ExceptionList == frame2, "Exception record points to %p instead of %p\n",
726 NtCurrentTeb()->Tib.ExceptionList, frame2);
727
728 /* unwind to frame1 */
729 unwind_expected_eax = 0xDEAD0000;
730 retval = func(pRtlUnwind, frame1, NULL, 0xDEAD0000);
731 ok(retval == 0xDEAD0001, "RtlUnwind returned eax %08lx instead of %08x\n", retval, 0xDEAD0001);
732 ok(NtCurrentTeb()->Tib.ExceptionList == frame1, "Exception record points to %p instead of %p\n",
733 NtCurrentTeb()->Tib.ExceptionList, frame1);
734
735 /* restore original handler */
736 NtCurrentTeb()->Tib.ExceptionList = frame1->Prev;
737}
738
739static DWORD prot_fault_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
741{
742 const struct exception *except = *(const struct exception **)(frame + 1);
743 unsigned int i, parameter_count, entry = except - exceptions;
744
746 trace( "exception %u: %lx flags:%lx addr:%p\n",
748
749 ok( rec->ExceptionCode == except->status ||
750 (except->alt_status != 0 && rec->ExceptionCode == except->alt_status),
751 "%u: Wrong exception code %lx/%lx\n", entry, rec->ExceptionCode, except->status );
752 ok( context->Eip == (DWORD)code_mem + except->offset,
753 "%u: Unexpected eip %#lx/%#lx\n", entry,
754 context->Eip, (DWORD)code_mem + except->offset );
755 ok( rec->ExceptionAddress == (char*)context->Eip ||
756 (rec->ExceptionCode == STATUS_BREAKPOINT && rec->ExceptionAddress == (char*)context->Eip + 1),
757 "%u: Unexpected exception address %p/%p\n", entry,
758 rec->ExceptionAddress, (char*)context->Eip );
759
760 if (except->status == STATUS_BREAKPOINT && is_wow64)
761 parameter_count = 1;
762 else if (except->alt_status == 0 || rec->ExceptionCode != except->alt_status)
763 parameter_count = except->nb_params;
764 else
765 parameter_count = except->alt_nb_params;
766
767 ok( rec->NumberParameters == parameter_count,
768 "%u: Unexpected parameter count %lu/%u\n", entry, rec->NumberParameters, parameter_count );
769
770 /* Most CPUs (except Intel Core apparently) report a segment limit violation */
771 /* instead of page faults for accesses beyond 0xffffffff */
772 if (except->nb_params == 2 && except->params[1] >= 0xfffffffd)
773 {
774 if (rec->ExceptionInformation[0] == 0 && rec->ExceptionInformation[1] == 0xffffffff)
775 goto skip_params;
776 }
777
778 /* Seems that both 0xbee8 and 0xfffffffff can be returned in windows */
779 if (except->nb_params == 2 && rec->NumberParameters == 2
780 && except->params[1] == 0xbee8 && rec->ExceptionInformation[1] == 0xffffffff
781 && except->params[0] == rec->ExceptionInformation[0])
782 {
783 goto skip_params;
784 }
785
786 if (except->alt_status == 0 || rec->ExceptionCode != except->alt_status)
787 {
788 for (i = 0; i < rec->NumberParameters; i++)
789 ok( rec->ExceptionInformation[i] == except->params[i],
790 "%u: Wrong parameter %d: %Ix/%lx\n",
791 entry, i, rec->ExceptionInformation[i], except->params[i] );
792 }
793 else
794 {
795 for (i = 0; i < rec->NumberParameters; i++)
796 ok( rec->ExceptionInformation[i] == except->alt_params[i],
797 "%u: Wrong parameter %d: %Ix/%lx\n",
798 entry, i, rec->ExceptionInformation[i], except->alt_params[i] );
799 }
800
801skip_params:
802 context->Eip = (DWORD_PTR)code_mem + except->offset + except->length;
804}
805
806static void test_prot_fault(void)
807{
808 unsigned int i;
809
810 for (i = 0; i < ARRAY_SIZE(exceptions); i++)
811 {
812 if (is_wow64 && exceptions[i].wow64_broken && !strcmp( winetest_platform, "windows" ))
813 {
814 skip( "Exception %u broken on Wow64\n", i );
815 continue;
816 }
817 got_exception = 0;
818 run_exception_test(prot_fault_handler, &exceptions[i], &exceptions[i].code,
819 sizeof(exceptions[i].code), 0);
820 if (!i && !got_exception)
821 {
822 trace( "No exception, assuming win9x, no point in testing further\n" );
823 break;
824 }
825 ok( got_exception == (exceptions[i].status != 0),
826 "%u: bad exception count %d\n", i, got_exception );
827 }
828}
829
830struct dbgreg_test {
831 DWORD dr0, dr1, dr2, dr3, dr6, dr7;
832};
833
834/* test handling of debug registers */
835static DWORD dreg_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
837{
838 const struct dbgreg_test *test = *(const struct dbgreg_test **)(frame + 1);
839
840 context->Eip += 2; /* Skips the popl (%eax) */
841 context->Dr0 = test->dr0;
842 context->Dr1 = test->dr1;
843 context->Dr2 = test->dr2;
844 context->Dr3 = test->dr3;
845 context->Dr6 = test->dr6;
846 context->Dr7 = test->dr7;
848}
849
850#define CHECK_DEBUG_REG(n, m) \
851 ok((ctx.Dr##n & m) == test->dr##n, "(%d) failed to set debug register " #n " to %lx, got %lx\n", \
852 test_num, test->dr##n, ctx.Dr##n)
853
854static void check_debug_registers(int test_num, const struct dbgreg_test *test)
855{
856 CONTEXT ctx;
858
859 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
860 status = pNtGetContextThread(GetCurrentThread(), &ctx);
861 ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %lx\n", status);
862
863 CHECK_DEBUG_REG(0, ~0);
864 CHECK_DEBUG_REG(1, ~0);
865 CHECK_DEBUG_REG(2, ~0);
866 CHECK_DEBUG_REG(3, ~0);
867 CHECK_DEBUG_REG(6, 0x0f);
868 CHECK_DEBUG_REG(7, ~0xdc00);
869}
870
871static const BYTE segfault_code[5] = {
872 0x31, 0xc0, /* xor %eax,%eax */
873 0x8f, 0x00, /* popl (%eax) - cause exception */
874 0xc3 /* ret */
875};
876
877/* test the single step exception behaviour */
878static DWORD single_step_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
880{
882 ok (!(context->EFlags & 0x100), "eflags has single stepping bit set\n");
883
884 if( got_exception < 3)
885 context->EFlags |= 0x100; /* single step until popf instruction */
886 else {
887 /* show that the last single step exception on the popf instruction
888 * (which removed the TF bit), still is a EXCEPTION_SINGLE_STEP exception */
890 "exception is not EXCEPTION_SINGLE_STEP: %lx\n", rec->ExceptionCode);
891 }
892
894}
895
896static const BYTE single_stepcode[] = {
897 0x9c, /* pushf */
898 0x58, /* pop %eax */
899 0x0d,0,1,0,0, /* or $0x100,%eax */
900 0x50, /* push %eax */
901 0x9d, /* popf */
902 0x35,0,1,0,0, /* xor $0x100,%eax */
903 0x50, /* push %eax */
904 0x9d, /* popf */
905 0xc3
906};
907
908/* Test the alignment check (AC) flag handling. */
909static const BYTE align_check_code[] = {
910 0x55, /* push %ebp */
911 0x89,0xe5, /* mov %esp,%ebp */
912 0x9c, /* pushf */
913 0x9c, /* pushf */
914 0x58, /* pop %eax */
915 0x0d,0,0,4,0, /* or $0x40000,%eax */
916 0x50, /* push %eax */
917 0x9d, /* popf */
918 0x89,0xe0, /* mov %esp, %eax */
919 0x8b,0x40,0x1, /* mov 0x1(%eax), %eax - cause exception */
920 0x9d, /* popf */
921 0x5d, /* pop %ebp */
922 0xc3, /* ret */
923};
924
925static DWORD align_check_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
927{
928 ok (!(context->EFlags & 0x40000), "eflags has AC bit set\n");
931}
932
933/* Test the direction flag handling. */
934static const BYTE direction_flag_code[] = {
935 0x55, /* push %ebp */
936 0x89,0xe5, /* mov %esp,%ebp */
937 0xfd, /* std */
938 0xfa, /* cli - cause exception */
939 0x5d, /* pop %ebp */
940 0xc3, /* ret */
941};
942
943static DWORD direction_flag_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
945{
946#ifdef __GNUC__
947 unsigned int flags;
948 __asm__("pushfl; popl %0; cld" : "=r" (flags) );
949 /* older windows versions don't clear DF properly so don't test */
950 if (flags & 0x400) trace( "eflags has DF bit set\n" );
951#endif
952 ok( context->EFlags & 0x400, "context eflags has DF bit cleared\n" );
954 context->Eip++; /* skip cli */
955 context->EFlags &= ~0x400; /* make sure it is cleared on return */
957}
958
959/* test single stepping over hardware breakpoint */
960static DWORD bpx_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
962{
965 "wrong exception code: %lx\n", rec->ExceptionCode);
966
967 if(got_exception == 1) {
968 /* hw bp exception on first nop */
969 ok( context->Eip == (DWORD)code_mem, "eip is wrong: %lx instead of %lx\n",
970 context->Eip, (DWORD)code_mem);
971 ok( (context->Dr6 & 0xf) == 1, "B0 flag is not set in Dr6\n");
972 ok( !(context->Dr6 & 0x4000), "BS flag is set in Dr6\n");
973 context->Dr0 = context->Dr0 + 1; /* set hw bp again on next instruction */
974 context->EFlags |= 0x100; /* enable single stepping */
975 } else if( got_exception == 2) {
976 /* single step exception on second nop */
977 ok( context->Eip == (DWORD)code_mem + 1, "eip is wrong: %lx instead of %lx\n",
978 context->Eip, (DWORD)code_mem + 1);
979 ok( (context->Dr6 & 0x4000), "BS flag is not set in Dr6\n");
980 /* depending on the win version the B0 bit is already set here as well
981 ok( (context->Dr6 & 0xf) == 0, "B0...3 flags in Dr6 shouldn't be set\n"); */
982 context->EFlags |= 0x100;
983 } else if( got_exception == 3) {
984 /* hw bp exception on second nop */
985 ok( context->Eip == (DWORD)code_mem + 1, "eip is wrong: %lx instead of %lx\n",
986 context->Eip, (DWORD)code_mem + 1);
987 ok( (context->Dr6 & 0xf) == 1, "B0 flag is not set in Dr6\n");
988 ok( !(context->Dr6 & 0x4000), "BS flag is set in Dr6\n");
989 context->Dr0 = 0; /* clear breakpoint */
990 context->EFlags |= 0x100;
991 } else {
992 /* single step exception on ret */
993 ok( context->Eip == (DWORD)code_mem + 2, "eip is wrong: %lx instead of %lx\n",
994 context->Eip, (DWORD)code_mem + 2);
995 ok( (context->Dr6 & 0xf) == 0, "B0...3 flags in Dr6 shouldn't be set\n");
996 ok( (context->Dr6 & 0x4000), "BS flag is not set in Dr6\n");
997 }
998
999 context->Dr6 = 0; /* clear status register */
1001}
1002
1003static const BYTE dummy_code[] = { 0x90, 0x90, 0xc3 }; /* nop, nop, ret */
1004
1005/* test int3 handling */
1006static DWORD int3_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
1008{
1009 ok( rec->ExceptionAddress == code_mem, "exception address not at: %p, but at %p\n",
1011 ok( context->Eip == (DWORD)code_mem, "eip not at: %p, but at %#lx\n", code_mem, context->Eip);
1012 if(context->Eip == (DWORD)code_mem) context->Eip++; /* skip breakpoint */
1013
1015}
1016
1017static const BYTE int3_code[] = { 0xCC, 0xc3 }; /* int 3, ret */
1018
1019static DWORD WINAPI hw_reg_exception_thread( void *arg )
1020{
1021 int expect = (ULONG_PTR)arg;
1022 got_exception = 0;
1023 run_exception_test( bpx_handler, NULL, dummy_code, sizeof(dummy_code), 0 );
1024 ok( got_exception == expect, "expected %u exceptions, got %d\n", expect, got_exception );
1025 return 0;
1026}
1027
1028static void test_exceptions(void)
1029{
1030 CONTEXT ctx;
1031 NTSTATUS res;
1032 struct dbgreg_test dreg_test;
1033 HANDLE h;
1034
1035 if (!pNtGetContextThread || !pNtSetContextThread)
1036 {
1037 skip( "NtGetContextThread/NtSetContextThread not found\n" );
1038 return;
1039 }
1040
1041 /* test handling of debug registers */
1042 memset(&dreg_test, 0, sizeof(dreg_test));
1043
1044 dreg_test.dr0 = 0x42424240;
1045 dreg_test.dr2 = 0x126bb070;
1046 dreg_test.dr3 = 0x0badbad0;
1047 dreg_test.dr7 = 0xffff0115;
1048 run_exception_test(dreg_handler, &dreg_test, &segfault_code, sizeof(segfault_code), 0);
1049 check_debug_registers(1, &dreg_test);
1050
1051 dreg_test.dr0 = 0x42424242;
1052 dreg_test.dr2 = 0x100f0fe7;
1053 dreg_test.dr3 = 0x0abebabe;
1054 dreg_test.dr7 = 0x115;
1055 run_exception_test(dreg_handler, &dreg_test, &segfault_code, sizeof(segfault_code), 0);
1056 check_debug_registers(2, &dreg_test);
1057
1058#if defined(__REACTOS__)
1059 if (is_reactos())
1060 {
1061 skip("Skipping test of single stepping behavior and int3 handling on ReactOS due to crashes\n");
1062 return;
1063 }
1064#endif
1065
1066 /* test single stepping behavior */
1067 got_exception = 0;
1068 run_exception_test(single_step_handler, NULL, &single_stepcode, sizeof(single_stepcode), 0);
1069 ok(got_exception == 3, "expected 3 single step exceptions, got %d\n", got_exception);
1070
1071 /* test alignment exceptions */
1072 got_exception = 0;
1073 run_exception_test(align_check_handler, NULL, align_check_code, sizeof(align_check_code), 0);
1074 ok(got_exception == 0, "got %d alignment faults, expected 0\n", got_exception);
1075
1076 /* test direction flag */
1077 got_exception = 0;
1078 run_exception_test(direction_flag_handler, NULL, direction_flag_code, sizeof(direction_flag_code), 0);
1079 ok(got_exception == 1, "got %d exceptions, expected 1\n", got_exception);
1080
1081 /* test single stepping over hardware breakpoint */
1082 memset(&ctx, 0, sizeof(ctx));
1083 ctx.Dr0 = (DWORD) code_mem; /* set hw bp on first nop */
1084 ctx.Dr7 = 3;
1085 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
1086 res = pNtSetContextThread( GetCurrentThread(), &ctx);
1087 ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %lx\n", res);
1088
1089 got_exception = 0;
1090 run_exception_test(bpx_handler, NULL, dummy_code, sizeof(dummy_code), 0);
1091 ok( got_exception == 4,"expected 4 exceptions, got %d\n", got_exception);
1092
1093 /* test int3 handling */
1094 run_exception_test(int3_handler, NULL, int3_code, sizeof(int3_code), 0);
1095
1096 /* test that hardware breakpoints are not inherited by created threads */
1097 res = pNtSetContextThread( GetCurrentThread(), &ctx );
1098 ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %lx\n", res );
1099
1100 h = CreateThread( NULL, 0, hw_reg_exception_thread, 0, 0, NULL );
1101 WaitForSingleObject( h, 10000 );
1102 CloseHandle( h );
1103
1104 h = CreateThread( NULL, 0, hw_reg_exception_thread, (void *)4, CREATE_SUSPENDED, NULL );
1105 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
1106 res = pNtGetContextThread( h, &ctx );
1107 ok( res == STATUS_SUCCESS, "NtGetContextThread failed with %lx\n", res );
1108 ok( ctx.Dr0 == 0, "dr0 %lx\n", ctx.Dr0 );
1109 ok( ctx.Dr7 == 0, "dr7 %lx\n", ctx.Dr7 );
1110 ctx.Dr0 = (DWORD)code_mem;
1111 ctx.Dr7 = 3;
1112 res = pNtSetContextThread( h, &ctx );
1113 ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %lx\n", res );
1114 ResumeThread( h );
1115 WaitForSingleObject( h, 10000 );
1116 CloseHandle( h );
1117
1118 ctx.Dr0 = 0;
1119 ctx.Dr7 = 0;
1120 res = pNtSetContextThread( GetCurrentThread(), &ctx );
1121 ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %lx\n", res );
1122}
1123
1124static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx)
1125{
1126 char cmdline[MAX_PATH];
1128 STARTUPINFOA si = { 0 };
1129 DEBUG_EVENT de;
1130 DWORD continuestatus;
1131 PVOID code_mem_address = NULL;
1133 SIZE_T size_read;
1134 BOOL ret;
1135 int counter = 0;
1136 si.cb = sizeof(si);
1137
1138 if(!pNtGetContextThread || !pNtSetContextThread || !pNtReadVirtualMemory || !pNtTerminateProcess)
1139 {
1140 skip("NtGetContextThread, NtSetContextThread, NtReadVirtualMemory or NtTerminateProcess not found\n");
1141 return;
1142 }
1143
1144 if (with_WaitForDebugEventEx && !pWaitForDebugEventEx)
1145 {
1146 skip("WaitForDebugEventEx not found, skipping unicode strings in OutputDebugStringW\n");
1147 return;
1148 }
1149
1150 sprintf(cmdline, "%s %s %s %p", my_argv[0], my_argv[1], "debuggee", &test_stage);
1152 ok(ret, "could not create child process error: %lu\n", GetLastError());
1153 if (!ret)
1154 return;
1155
1156 do
1157 {
1158 continuestatus = cont_status;
1159 ret = with_WaitForDebugEventEx ? pWaitForDebugEventEx(&de, INFINITE) : WaitForDebugEvent(&de, INFINITE);
1160 ok(ret, "reading debug event\n");
1161
1162 ret = ContinueDebugEvent(de.dwProcessId, de.dwThreadId, 0xdeadbeef);
1163 ok(!ret, "ContinueDebugEvent unexpectedly succeeded\n");
1164 ok(GetLastError() == ERROR_INVALID_PARAMETER, "Unexpected last error: %lu\n", GetLastError());
1165
1166 if (de.dwThreadId != pi.dwThreadId)
1167 {
1168 trace("event %ld not coming from main thread, ignoring\n", de.dwDebugEventCode);
1169 ContinueDebugEvent(de.dwProcessId, de.dwThreadId, cont_status);
1170 continue;
1171 }
1172
1174 {
1176 {
1177 skip("child process loaded at different address, terminating it\n");
1178 pNtTerminateProcess(pi.hProcess, 0);
1179 }
1180 }
1182 {
1183 CONTEXT ctx;
1184 enum debugger_stages stage;
1185
1186 counter++;
1187 status = pNtReadVirtualMemory(pi.hProcess, &code_mem, &code_mem_address,
1188 sizeof(code_mem_address), &size_read);
1189 ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status);
1190 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
1191 sizeof(stage), &size_read);
1192 ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status);
1193
1195 status = pNtGetContextThread(pi.hThread, &ctx);
1196 ok(!status, "NtGetContextThread failed with 0x%lx\n", status);
1197 ok(ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING
1198 || broken( !(ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) ) /* Win7 WoW64 */,
1199 "got %#lx.\n", ctx.ContextFlags);
1200
1201 trace("exception 0x%lx at %p firstchance=%ld Eip=0x%lx, Eax=0x%lx ctx.ContextFlags %#lx\n",
1204
1205 if (counter > 100)
1206 {
1207 ok(FALSE, "got way too many exceptions, probably caught in an infinite loop, terminating child\n");
1208 pNtTerminateProcess(pi.hProcess, 1);
1209 }
1210 else if (counter < 2) /* startup breakpoint */
1211 {
1212 /* breakpoint is inside ntdll */
1213 void *ntdll = GetModuleHandleA( "ntdll.dll" );
1215
1216 ok( (char *)ctx.Eip >= (char *)ntdll &&
1217 (char *)ctx.Eip < (char *)ntdll + nt->OptionalHeader.SizeOfImage,
1218 "wrong eip %p ntdll %p-%p\n", (void *)ctx.Eip, ntdll,
1219 (char *)ntdll + nt->OptionalHeader.SizeOfImage );
1220 check_context_exception_request( ctx.ContextFlags, TRUE );
1221 }
1222 else
1223 {
1224 if (stage == STAGE_RTLRAISE_NOT_HANDLED)
1225 {
1226 ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at %lx instead of %p\n",
1227 ctx.Eip, (char *)code_mem_address + 0xb);
1228 /* setting the context from debugger does not affect the context that the
1229 * exception handler gets, except on w2008 */
1230 ctx.Eip = (UINT_PTR)code_mem_address + 0xd;
1231 ctx.Eax = 0xf00f00f1;
1232 /* let the debuggee handle the exception */
1233 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1234 check_context_exception_request( ctx.ContextFlags, !is_wow64 );
1235 }
1236 else if (stage == STAGE_RTLRAISE_HANDLE_LAST_CHANCE)
1237 {
1238 if (de.u.Exception.dwFirstChance)
1239 {
1240 /* debugger gets first chance exception with unmodified ctx.Eip */
1241 ok((char *)ctx.Eip == (char *)code_mem_address + 0xb, "Eip at 0x%lx instead of %p\n",
1242 ctx.Eip, (char *)code_mem_address + 0xb);
1243 ctx.Eip = (UINT_PTR)code_mem_address + 0xd;
1244 ctx.Eax = 0xf00f00f1;
1245 /* pass exception to debuggee
1246 * exception will not be handled and a second chance exception will be raised */
1247 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1248 }
1249 else
1250 {
1251 /* debugger gets context after exception handler has played with it */
1252 /* ctx.Eip is the same value the exception handler got */
1254 {
1255 ok((char *)ctx.Eip == (char *)code_mem_address + 0xa ||
1256 (is_wow64 && (char *)ctx.Eip == (char *)code_mem_address + 0xb) ||
1257 broken((char *)ctx.Eip == (char *)code_mem_address + 0xd) /* w2008 */,
1258 "Eip at 0x%lx instead of %p\n",
1259 ctx.Eip, (char *)code_mem_address + 0xa);
1260 /* need to fixup Eip for debuggee */
1261 if ((char *)ctx.Eip == (char *)code_mem_address + 0xa)
1262 ctx.Eip += 1;
1263 }
1264 else
1265 ok((char *)ctx.Eip == (char *)code_mem_address + 0xb ||
1266 broken((char *)ctx.Eip == (char *)code_mem_address + 0xd) /* w2008 */,
1267 "Eip at 0x%lx instead of %p\n",
1268 ctx.Eip, (char *)code_mem_address + 0xb);
1269 /* here we handle exception */
1270 }
1271 check_context_exception_request( ctx.ContextFlags, !is_wow64 );
1272 }
1273 else if (stage == STAGE_SERVICE_CONTINUE || stage == STAGE_SERVICE_NOT_HANDLED)
1274 {
1276 "expected EXCEPTION_BREAKPOINT, got %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode);
1277 ok((char *)ctx.Eip == (char *)code_mem_address + 0x1d,
1278 "expected Eip = %p, got 0x%lx\n", (char *)code_mem_address + 0x1d, ctx.Eip);
1279
1280 if (stage == STAGE_SERVICE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1281 check_context_exception_request( ctx.ContextFlags, TRUE );
1282 }
1283 else if (stage == STAGE_BREAKPOINT_CONTINUE || stage == STAGE_BREAKPOINT_NOT_HANDLED)
1284 {
1286 "expected EXCEPTION_BREAKPOINT, got %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode);
1287 ok((char *)ctx.Eip == (char *)code_mem_address + 2,
1288 "expected Eip = %p, got 0x%lx\n", (char *)code_mem_address + 2, ctx.Eip);
1289
1290 if (stage == STAGE_BREAKPOINT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1291 check_context_exception_request( ctx.ContextFlags, TRUE );
1292 }
1294 {
1296 "unexpected exception code %08lx, expected %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode,
1299 "unexpected number of parameters %ld, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters);
1300
1302 check_context_exception_request( ctx.ContextFlags, !is_wow64 );
1303 }
1305 {
1306 ok(FALSE || broken(TRUE) /* < Win10 */, "should not throw exception\n");
1307 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1308 check_context_exception_request( ctx.ContextFlags, !is_wow64 );
1309 }
1310 else if (stage == STAGE_XSTATE || stage == STAGE_XSTATE_LEGACY_SSE)
1311 {
1312 check_context_exception_request( ctx.ContextFlags, TRUE );
1313 test_debugger_xstate(pi.hThread, &ctx, stage);
1314 }
1315 else if (stage == STAGE_SEGMENTS)
1316 {
1317 USHORT ss;
1318#if defined(__REACTOS__) && defined(_MSC_VER)
1319 USHORT segss;
1320 __asm mov [segss], ss
1321 ss = segss;
1322#else
1323 __asm__( "movw %%ss,%0" : "=r" (ss) );
1324#endif
1325 ok( ctx.SegSs == ss, "wrong ss %04lx / %04x\n", ctx.SegSs, ss );
1326 ok( ctx.SegFs != ctx.SegSs, "wrong fs %04lx / %04lx\n", ctx.SegFs, ctx.SegSs );
1327 if (is_wow64) todo_wine_if( !ctx.SegDs ) /* old wow64 */
1328 {
1329 ok( ctx.SegDs == ctx.SegSs, "wrong ds %04lx / %04lx\n", ctx.SegDs, ctx.SegSs );
1330 ok( ctx.SegEs == ctx.SegSs, "wrong es %04lx / %04lx\n", ctx.SegEs, ctx.SegSs );
1331 ok( ctx.SegGs == ctx.SegSs, "wrong gs %04lx / %04lx\n", ctx.SegGs, ctx.SegSs );
1332 }
1333 else
1334 {
1335 ok( !ctx.SegDs, "wrong ds %04lx / %04lx\n", ctx.SegDs, ctx.SegSs );
1336 ok( !ctx.SegEs, "wrong es %04lx / %04lx\n", ctx.SegEs, ctx.SegSs );
1337 ok( !ctx.SegGs, "wrong gs %04lx / %04lx\n", ctx.SegGs, ctx.SegSs );
1338 }
1339 check_context_exception_request( ctx.ContextFlags, TRUE );
1340 }
1341 else
1342 ok(FALSE, "unexpected stage %u\n", stage);
1343
1344 status = pNtSetContextThread(pi.hThread, &ctx);
1345 ok(!status, "NtSetContextThread failed with 0x%lx\n", status);
1346 }
1347 }
1349 {
1350 enum debugger_stages stage;
1351 char buffer[64 * sizeof(WCHAR)];
1352 unsigned char_size = de.u.DebugString.fUnicode ? sizeof(WCHAR) : sizeof(char);
1353
1354 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
1355 sizeof(stage), &size_read);
1356 ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status);
1357
1358 if (de.u.DebugString.fUnicode)
1359 ok(with_WaitForDebugEventEx &&
1361 "unexpected unicode debug string event\n");
1362 else
1363 ok(!with_WaitForDebugEventEx || stage != STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || cont_status != DBG_CONTINUE,
1364 "unexpected ansi debug string event %u %s %lx\n",
1365 stage, with_WaitForDebugEventEx ? "with" : "without", cont_status);
1366
1367 ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) / char_size - 1,
1368 "buffer not large enough to hold %d bytes\n", de.u.DebugString.nDebugStringLength);
1369
1370 memset(buffer, 0, sizeof(buffer));
1371 status = pNtReadVirtualMemory(pi.hProcess, de.u.DebugString.lpDebugStringData, buffer,
1372 de.u.DebugString.nDebugStringLength * char_size, &size_read);
1373 ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status);
1374
1377 {
1378 if (de.u.DebugString.fUnicode)
1379 ok(!wcscmp((WCHAR*)buffer, L"Hello World"), "got unexpected debug string '%ls'\n", (WCHAR*)buffer);
1380 else
1381 ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer);
1382 }
1383 else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */
1384 ok(strstr(buffer, "SHIMVIEW") != NULL, "unexpected stage %x, got debug string event '%s'\n", stage, buffer);
1385
1387 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1388 }
1389 else if (de.dwDebugEventCode == RIP_EVENT)
1390 {
1391 enum debugger_stages stage;
1392
1393 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
1394 sizeof(stage), &size_read);
1395 ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status);
1396
1397 if (stage == STAGE_RIPEVENT_CONTINUE || stage == STAGE_RIPEVENT_NOT_HANDLED)
1398 {
1399 ok(de.u.RipInfo.dwError == 0x11223344, "got unexpected rip error code %08lx, expected %08x\n",
1400 de.u.RipInfo.dwError, 0x11223344);
1401 ok(de.u.RipInfo.dwType == 0x55667788, "got unexpected rip type %08lx, expected %08x\n",
1402 de.u.RipInfo.dwType, 0x55667788);
1403 }
1404 else
1405 ok(FALSE, "unexpected stage %x\n", stage);
1406
1407 if (stage == STAGE_RIPEVENT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
1408 }
1409
1410 ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus);
1411
1413
1416 ok(ret, "error %lu\n", GetLastError());
1418 ok(ret, "error %lu\n", GetLastError());
1419
1420 return;
1421}
1422
1423static DWORD simd_fault_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
1425{
1426 int *stage = *(int **)(frame + 1);
1427
1428 got_exception++;
1429
1430 if( *stage == 1) {
1431 /* fault while executing sse instruction */
1432 context->Eip += 3; /* skip addps */
1434 }
1435 else if ( *stage == 2 || *stage == 3 ) {
1436 /* stage 2 - divide by zero fault */
1437 /* stage 3 - invalid operation fault */
1439 skip("system doesn't support SIMD exceptions\n");
1440 else {
1442 "exception code: %#lx, should be %#lx\n",
1444 ok( rec->NumberParameters == is_wow64 ? 2 : 1, "# of params: %li\n", rec->NumberParameters);
1445 ok( rec->ExceptionInformation[0] == 0, "param #1: %Ix, should be 0\n", rec->ExceptionInformation[0]);
1446 if (rec->NumberParameters == 2)
1447 ok( rec->ExceptionInformation[1] == ((XSAVE_FORMAT *)context->ExtendedRegisters)->MxCsr,
1448 "param #1: %Ix / %lx\n", rec->ExceptionInformation[1],
1449 ((XSAVE_FORMAT *)context->ExtendedRegisters)->MxCsr);
1450 }
1451 context->Eip += 3; /* skip divps */
1452 }
1453 else
1454 ok(FALSE, "unexpected stage %x\n", *stage);
1455
1457}
1458
1459static const BYTE simd_exception_test[] = {
1460 0x83, 0xec, 0x4, /* sub $0x4, %esp */
1461 0x0f, 0xae, 0x1c, 0x24, /* stmxcsr (%esp) */
1462 0x8b, 0x04, 0x24, /* mov (%esp),%eax * store mxcsr */
1463 0x66, 0x81, 0x24, 0x24, 0xff, 0xfd, /* andw $0xfdff,(%esp) * enable divide by */
1464 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%esp) * zero exceptions */
1465 0x6a, 0x01, /* push $0x1 */
1466 0x6a, 0x01, /* push $0x1 */
1467 0x6a, 0x01, /* push $0x1 */
1468 0x6a, 0x01, /* push $0x1 */
1469 0x0f, 0x10, 0x0c, 0x24, /* movups (%esp),%xmm1 * fill dividend */
1470 0x0f, 0x57, 0xc0, /* xorps %xmm0,%xmm0 * clear divisor */
1471 0x0f, 0x5e, 0xc8, /* divps %xmm0,%xmm1 * generate fault */
1472 0x83, 0xc4, 0x10, /* add $0x10,%esp */
1473 0x89, 0x04, 0x24, /* mov %eax,(%esp) * restore to old mxcsr */
1474 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%esp) */
1475 0x83, 0xc4, 0x04, /* add $0x4,%esp */
1476 0xc3, /* ret */
1477};
1478
1479static const BYTE simd_exception_test2[] = {
1480 0x83, 0xec, 0x4, /* sub $0x4, %esp */
1481 0x0f, 0xae, 0x1c, 0x24, /* stmxcsr (%esp) */
1482 0x8b, 0x04, 0x24, /* mov (%esp),%eax * store mxcsr */
1483 0x66, 0x81, 0x24, 0x24, 0x7f, 0xff, /* andw $0xff7f,(%esp) * enable invalid */
1484 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%esp) * operation exceptions */
1485 0x0f, 0x57, 0xc9, /* xorps %xmm1,%xmm1 * clear dividend */
1486 0x0f, 0x57, 0xc0, /* xorps %xmm0,%xmm0 * clear divisor */
1487 0x0f, 0x5e, 0xc8, /* divps %xmm0,%xmm1 * generate fault */
1488 0x89, 0x04, 0x24, /* mov %eax,(%esp) * restore to old mxcsr */
1489 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%esp) */
1490 0x83, 0xc4, 0x04, /* add $0x4,%esp */
1491 0xc3, /* ret */
1492};
1493
1494static const BYTE sse_check[] = {
1495 0x0f, 0x58, 0xc8, /* addps %xmm0,%xmm1 */
1496 0xc3, /* ret */
1497};
1498
1499static void test_simd_exceptions(void)
1500{
1501 int stage;
1502
1503 /* test if CPU & OS can do sse */
1504 stage = 1;
1505 got_exception = 0;
1506 run_exception_test(simd_fault_handler, &stage, sse_check, sizeof(sse_check), 0);
1507 if(got_exception) {
1508 skip("system doesn't support SSE\n");
1509 return;
1510 }
1511
1512 /* generate a SIMD exception */
1513 stage = 2;
1514 got_exception = 0;
1515 run_exception_test(simd_fault_handler, &stage, simd_exception_test,
1516 sizeof(simd_exception_test), 0);
1517 ok(got_exception == 1, "got exception: %i, should be 1\n", got_exception);
1518
1519 /* generate a SIMD exception, test FPE_FLTINV */
1520 stage = 3;
1521 got_exception = 0;
1522 run_exception_test(simd_fault_handler, &stage, simd_exception_test2,
1523 sizeof(simd_exception_test2), 0);
1524 ok(got_exception == 1, "got exception: %i, should be 1\n", got_exception);
1525}
1526
1527struct fpu_exception_info
1528{
1530 DWORD exception_offset;
1531 DWORD eip_offset;
1532};
1533
1534static DWORD fpu_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
1536{
1537 struct fpu_exception_info *info = *(struct fpu_exception_info **)(frame + 1);
1538
1539 info->exception_code = rec->ExceptionCode;
1540 info->exception_offset = (BYTE *)rec->ExceptionAddress - (BYTE *)code_mem;
1541 info->eip_offset = context->Eip - (DWORD)code_mem;
1542
1543 ++context->Eip;
1545}
1546
1547static void test_fpu_exceptions(void)
1548{
1549 static const BYTE fpu_exception_test_ie[] =
1550 {
1551 0x83, 0xec, 0x04, /* sub $0x4,%esp */
1552 0x66, 0xc7, 0x04, 0x24, 0xfe, 0x03, /* movw $0x3fe,(%esp) */
1553 0x9b, 0xd9, 0x7c, 0x24, 0x02, /* fstcw 0x2(%esp) */
1554 0xd9, 0x2c, 0x24, /* fldcw (%esp) */
1555 0xd9, 0xee, /* fldz */
1556 0xd9, 0xe8, /* fld1 */
1557 0xde, 0xf1, /* fdivp */
1558 0xdd, 0xd8, /* fstp %st(0) */
1559 0xdd, 0xd8, /* fstp %st(0) */
1560 0x9b, /* fwait */
1561 0xdb, 0xe2, /* fnclex */
1562 0xd9, 0x6c, 0x24, 0x02, /* fldcw 0x2(%esp) */
1563 0x83, 0xc4, 0x04, /* add $0x4,%esp */
1564 0xc3, /* ret */
1565 };
1566
1567 static const BYTE fpu_exception_test_de[] =
1568 {
1569 0x83, 0xec, 0x04, /* sub $0x4,%esp */
1570 0x66, 0xc7, 0x04, 0x24, 0xfb, 0x03, /* movw $0x3fb,(%esp) */
1571 0x9b, 0xd9, 0x7c, 0x24, 0x02, /* fstcw 0x2(%esp) */
1572 0xd9, 0x2c, 0x24, /* fldcw (%esp) */
1573 0xdd, 0xd8, /* fstp %st(0) */
1574 0xd9, 0xee, /* fldz */
1575 0xd9, 0xe8, /* fld1 */
1576 0xde, 0xf1, /* fdivp */
1577 0x9b, /* fwait */
1578 0xdb, 0xe2, /* fnclex */
1579 0xdd, 0xd8, /* fstp %st(0) */
1580 0xdd, 0xd8, /* fstp %st(0) */
1581 0xd9, 0x6c, 0x24, 0x02, /* fldcw 0x2(%esp) */
1582 0x83, 0xc4, 0x04, /* add $0x4,%esp */
1583 0xc3, /* ret */
1584 };
1585
1586 struct fpu_exception_info info;
1587
1588 memset(&info, 0, sizeof(info));
1589 run_exception_test(fpu_exception_handler, &info, fpu_exception_test_ie, sizeof(fpu_exception_test_ie), 0);
1590 ok(info.exception_code == EXCEPTION_FLT_STACK_CHECK,
1591 "Got exception code %#lx, expected EXCEPTION_FLT_STACK_CHECK\n", info.exception_code);
1592 ok(info.exception_offset == 0x19 || info.exception_offset == info.eip_offset,
1593 "Got exception offset %#lx, expected 0x19\n", info.exception_offset);
1594 ok(info.eip_offset == 0x1b, "Got EIP offset %#lx, expected 0x1b\n", info.eip_offset);
1595
1596 memset(&info, 0, sizeof(info));
1597 run_exception_test(fpu_exception_handler, &info, fpu_exception_test_de, sizeof(fpu_exception_test_de), 0);
1598 ok(info.exception_code == EXCEPTION_FLT_DIVIDE_BY_ZERO,
1599 "Got exception code %#lx, expected EXCEPTION_FLT_DIVIDE_BY_ZERO\n", info.exception_code);
1600 ok(info.exception_offset == 0x17 || info.exception_offset == info.eip_offset,
1601 "Got exception offset %#lx, expected 0x17\n", info.exception_offset);
1602 ok(info.eip_offset == 0x19, "Got EIP offset %#lx, expected 0x19\n", info.eip_offset);
1603}
1604
1605struct dpe_exception_info {
1606 BOOL exception_caught;
1608};
1609
1610static DWORD dpe_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
1612{
1613 DWORD old_prot;
1614 struct dpe_exception_info *info = *(struct dpe_exception_info **)(frame + 1);
1615
1617 "Exception code %08lx\n", rec->ExceptionCode);
1618 ok(rec->NumberParameters == 2,
1619 "Parameter count: %ld\n", rec->NumberParameters);
1621 "Exception address: %p, expected %p\n",
1623
1624 info->exception_info = rec->ExceptionInformation[0];
1625 info->exception_caught = TRUE;
1626
1629}
1630
1631static void test_dpe_exceptions(void)
1632{
1633 static const BYTE single_ret[] = {0xC3};
1634 struct dpe_exception_info info;
1635 NTSTATUS stat;
1636 BOOL has_hw_support;
1637 BOOL is_permanent = FALSE, can_test_without = TRUE, can_test_with = TRUE;
1638 DWORD val;
1639 ULONG len;
1640
1641 /* Query DEP with len too small */
1644 {
1645 skip("This software platform does not support DEP\n");
1646 return;
1647 }
1648 ok(stat == STATUS_INFO_LENGTH_MISMATCH, "buffer too small: %08lx\n", stat);
1649
1650 /* Query DEP */
1652 ok(stat == STATUS_SUCCESS, "querying DEP: status %08lx\n", stat);
1653 if(stat == STATUS_SUCCESS)
1654 {
1655 ok(len == sizeof val, "returned length: %ld\n", len);
1657 {
1658 skip("toggling DEP impossible - status locked\n");
1659 is_permanent = TRUE;
1661 can_test_without = FALSE;
1662 else
1663 can_test_with = FALSE;
1664 }
1665 }
1666
1667 if(!is_permanent)
1668 {
1669 /* Enable DEP */
1671 stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
1672 ok(stat == STATUS_SUCCESS, "enabling DEP: status %08lx\n", stat);
1673 }
1674
1675 if(can_test_with)
1676 {
1677 /* Try access to locked page with DEP on*/
1678 info.exception_caught = FALSE;
1679 run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_NOACCESS);
1680 ok(info.exception_caught == TRUE, "Execution of disabled memory succeeded\n");
1681 ok(info.exception_info == EXCEPTION_READ_FAULT ||
1682 info.exception_info == EXCEPTION_EXECUTE_FAULT,
1683 "Access violation type: %08x\n", (unsigned)info.exception_info);
1684 has_hw_support = info.exception_info == EXCEPTION_EXECUTE_FAULT;
1685 trace("DEP hardware support: %s\n", has_hw_support?"Yes":"No");
1686
1687 /* Try execution of data with DEP on*/
1688 info.exception_caught = FALSE;
1689 run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_READWRITE);
1690 if(has_hw_support)
1691 {
1692 ok(info.exception_caught == TRUE, "Execution of data memory succeeded\n");
1693 ok(info.exception_info == EXCEPTION_EXECUTE_FAULT,
1694 "Access violation type: %08x\n", (unsigned)info.exception_info);
1695 }
1696 else
1697 ok(info.exception_caught == FALSE, "Execution trapped without hardware support\n");
1698 }
1699 else
1700 skip("DEP is in AlwaysOff state\n");
1701
1702 if(!is_permanent)
1703 {
1704 /* Disable DEP */
1706 stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
1707 ok(stat == STATUS_SUCCESS, "disabling DEP: status %08lx\n", stat);
1708 }
1709
1710 /* page is read without exec here */
1711 if(can_test_without)
1712 {
1713 /* Try execution of data with DEP off */
1714 info.exception_caught = FALSE;
1715 run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_READWRITE);
1716 ok(info.exception_caught == FALSE, "Execution trapped with DEP turned off\n");
1717
1718 /* Try access to locked page with DEP off - error code is different than
1719 with hardware DEP on */
1720 info.exception_caught = FALSE;
1721 run_exception_test(dpe_exception_handler, &info, single_ret, sizeof(single_ret), PAGE_NOACCESS);
1722 ok(info.exception_caught == TRUE, "Execution of disabled memory succeeded\n");
1723 ok(info.exception_info == EXCEPTION_READ_FAULT,
1724 "Access violation type: %08x\n", (unsigned)info.exception_info);
1725 }
1726 else
1727 skip("DEP is in AlwaysOn state\n");
1728
1729 if(!is_permanent)
1730 {
1731 /* Turn off DEP permanently */
1733 stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
1734 ok(stat == STATUS_SUCCESS, "disabling DEP permanently: status %08lx\n", stat);
1735 }
1736
1737 /* Try to turn off DEP */
1739 stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
1740 ok(stat == STATUS_ACCESS_DENIED, "disabling DEP while permanent: status %08lx\n", stat);
1741
1742 /* Try to turn on DEP */
1744 stat = pNtSetInformationProcess(GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val);
1745 ok(stat == STATUS_ACCESS_DENIED, "enabling DEP while permanent: status %08lx\n", stat);
1746}
1747
1748static void test_thread_context(void)
1749{
1752 struct expected
1753 {
1754 DWORD Eax, Ebx, Ecx, Edx, Esi, Edi, Ebp, Esp, Eip,
1755 SegCs, SegDs, SegEs, SegFs, SegGs, SegSs, EFlags, prev_frame,
1756 x87_control;
1757 } expect;
1758 NTSTATUS (*func_ptr)( struct expected *res, void *func, void *arg1, void *arg2,
1759 DWORD *new_x87_control ) = code_mem;
1760 DWORD new_x87_control;
1761
1762 static const BYTE call_func[] =
1763 {
1764 0x55, /* pushl %ebp */
1765 0x89, 0xe5, /* mov %esp,%ebp */
1766 0x50, /* pushl %eax ; add a bit of offset to the stack */
1767 0x50, /* pushl %eax */
1768 0x50, /* pushl %eax */
1769 0x50, /* pushl %eax */
1770 0x8b, 0x45, 0x08, /* mov 0x8(%ebp),%eax */
1771 0x8f, 0x00, /* popl (%eax) */
1772 0x89, 0x58, 0x04, /* mov %ebx,0x4(%eax) */
1773 0x89, 0x48, 0x08, /* mov %ecx,0x8(%eax) */
1774 0x89, 0x50, 0x0c, /* mov %edx,0xc(%eax) */
1775 0x89, 0x70, 0x10, /* mov %esi,0x10(%eax) */
1776 0x89, 0x78, 0x14, /* mov %edi,0x14(%eax) */
1777 0x89, 0x68, 0x18, /* mov %ebp,0x18(%eax) */
1778 0x89, 0x60, 0x1c, /* mov %esp,0x1c(%eax) */
1779 0xff, 0x75, 0x04, /* pushl 0x4(%ebp) */
1780 0x8f, 0x40, 0x20, /* popl 0x20(%eax) */
1781 0x8c, 0x48, 0x24, /* mov %cs,0x24(%eax) */
1782 0x8c, 0x58, 0x28, /* mov %ds,0x28(%eax) */
1783 0x8c, 0x40, 0x2c, /* mov %es,0x2c(%eax) */
1784 0x8c, 0x60, 0x30, /* mov %fs,0x30(%eax) */
1785 0x8c, 0x68, 0x34, /* mov %gs,0x34(%eax) */
1786 0x8c, 0x50, 0x38, /* mov %ss,0x38(%eax) */
1787 0x9c, /* pushf */
1788 0x8f, 0x40, 0x3c, /* popl 0x3c(%eax) */
1789 0xff, 0x75, 0x00, /* pushl 0x0(%ebp) ; previous stack frame */
1790 0x8f, 0x40, 0x40, /* popl 0x40(%eax) */
1791 /* pushl $0x47f */
1792 0x68, 0x7f, 0x04, 0x00, 0x00,
1793 0x8f, 0x40, 0x44, /* popl 0x44(%eax) */
1794 0xd9, 0x68, 0x44, /* fldcw 0x44(%eax) */
1795
1796 0x8b, 0x00, /* mov (%eax),%eax */
1797 0xff, 0x75, 0x14, /* pushl 0x14(%ebp) */
1798 0xff, 0x75, 0x10, /* pushl 0x10(%ebp) */
1799 0xff, 0x55, 0x0c, /* call *0xc(%ebp) */
1800
1801 0x8b, 0x55, 0x18, /* mov 0x18(%ebp),%edx */
1802 0x9b, 0xd9, 0x3a, /* fstcw (%edx) */
1803 0xdb, 0xe3, /* fninit */
1804
1805 0xc9, /* leave */
1806 0xc3, /* ret */
1807 };
1808
1809 memcpy( func_ptr, call_func, sizeof(call_func) );
1810
1811#define COMPARE(reg) \
1812 ok( context.reg == expect.reg, "wrong " #reg " %08lx/%08lx\n", context.reg, expect.reg )
1813
1814 memset( &context, 0xcc, sizeof(context) );
1815 memset( &expect, 0xcc, sizeof(expect) );
1816 func_ptr( &expect, pRtlCaptureContext, &context, 0, &new_x87_control );
1817 trace( "expect: eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx esi=%08lx edi=%08lx ebp=%08lx esp=%08lx "
1818 "eip=%08lx cs=%04lx ds=%04lx es=%04lx fs=%04lx gs=%04lx ss=%04lx flags=%08lx prev=%08lx\n",
1819 expect.Eax, expect.Ebx, expect.Ecx, expect.Edx, expect.Esi, expect.Edi,
1820 expect.Ebp, expect.Esp, expect.Eip, expect.SegCs, expect.SegDs, expect.SegEs,
1821 expect.SegFs, expect.SegGs, expect.SegSs, expect.EFlags, expect.prev_frame );
1822 trace( "actual: eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx esi=%08lx edi=%08lx ebp=%08lx esp=%08lx "
1823 "eip=%08lx cs=%04lx ds=%04lx es=%04lx fs=%04lx gs=%04lx ss=%04lx flags=%08lx\n",
1824 context.Eax, context.Ebx, context.Ecx, context.Edx, context.Esi, context.Edi,
1825 context.Ebp, context.Esp, context.Eip, context.SegCs, context.SegDs, context.SegEs,
1826 context.SegFs, context.SegGs, context.SegSs, context.EFlags );
1827
1829 broken( context.ContextFlags == 0xcccccccc ), /* <= vista */
1830 "wrong flags %08lx\n", context.ContextFlags );
1831 COMPARE( Eax );
1832 COMPARE( Ebx );
1833 COMPARE( Ecx );
1834 COMPARE( Edx );
1835 COMPARE( Esi );
1836 COMPARE( Edi );
1837 COMPARE( Eip );
1838 COMPARE( SegCs );
1839 COMPARE( SegDs );
1840 COMPARE( SegEs );
1841 COMPARE( SegFs );
1842 COMPARE( SegGs );
1843 COMPARE( SegSs );
1844 COMPARE( EFlags );
1845 /* Ebp is from the previous stackframe */
1846 ok( context.Ebp == expect.prev_frame, "wrong Ebp %08lx/%08lx\n", context.Ebp, expect.prev_frame );
1847 /* Esp is the value on entry to the previous stackframe */
1848 ok( context.Esp == expect.Ebp + 8, "wrong Esp %08lx/%08lx\n", context.Esp, expect.Ebp + 8 );
1849
1850 memset( &context, 0xcc, sizeof(context) );
1851 memset( &expect, 0xcc, sizeof(expect) );
1853
1854 status = func_ptr( &expect, pNtGetContextThread, (void *)GetCurrentThread(), &context, &new_x87_control );
1855 ok( status == STATUS_SUCCESS, "NtGetContextThread failed %08lx\n", status );
1856 trace( "expect: eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx esi=%08lx edi=%08lx ebp=%08lx esp=%08lx "
1857 "eip=%08lx cs=%04lx ds=%04lx es=%04lx fs=%04lx gs=%04lx ss=%04lx flags=%08lx prev=%08lx\n",
1858 expect.Eax, expect.Ebx, expect.Ecx, expect.Edx, expect.Esi, expect.Edi,
1859 expect.Ebp, expect.Esp, expect.Eip, expect.SegCs, expect.SegDs, expect.SegEs,
1860 expect.SegFs, expect.SegGs, expect.SegSs, expect.EFlags, expect.prev_frame );
1861 trace( "actual: eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx esi=%08lx edi=%08lx ebp=%08lx esp=%08lx "
1862 "eip=%08lx cs=%04lx ds=%04lx es=%04lx fs=%04lx gs=%04lx ss=%04lx flags=%08lx\n",
1863 context.Eax, context.Ebx, context.Ecx, context.Edx, context.Esi, context.Edi,
1864 context.Ebp, context.Esp, context.Eip, context.SegCs, context.SegDs, context.SegEs,
1865 context.SegFs, context.SegGs, context.SegSs, context.EFlags );
1866 /* Eax, Ecx, Edx, EFlags are not preserved */
1867 COMPARE( Ebx );
1868 COMPARE( Esi );
1869 COMPARE( Edi );
1870 COMPARE( Ebp );
1871 /* Esp is the stack upon entry to NtGetContextThread */
1872 ok( context.Esp == expect.Esp - 12 || context.Esp == expect.Esp - 16,
1873 "wrong Esp %08lx/%08lx\n", context.Esp, expect.Esp );
1874 /* Eip is somewhere close to the NtGetContextThread implementation */
1875 ok( (char *)context.Eip >= (char *)pNtGetContextThread - 0x40000 &&
1876 (char *)context.Eip <= (char *)pNtGetContextThread + 0x40000,
1877 "wrong Eip %08lx/%08lx\n", context.Eip, (DWORD)pNtGetContextThread );
1878 /* segment registers clear the high word */
1879 ok( context.SegCs == LOWORD(expect.SegCs), "wrong SegCs %08lx/%08lx\n", context.SegCs, expect.SegCs );
1880 ok( context.SegDs == LOWORD(expect.SegDs), "wrong SegDs %08lx/%08lx\n", context.SegDs, expect.SegDs );
1881 ok( context.SegEs == LOWORD(expect.SegEs), "wrong SegEs %08lx/%08lx\n", context.SegEs, expect.SegEs );
1882 ok( context.SegFs == LOWORD(expect.SegFs), "wrong SegFs %08lx/%08lx\n", context.SegFs, expect.SegFs );
1883 if (LOWORD(expect.SegGs)) ok( context.SegGs == LOWORD(expect.SegGs), "wrong SegGs %08lx/%08lx\n", context.SegGs, expect.SegGs );
1884 ok( context.SegSs == LOWORD(expect.SegSs), "wrong SegSs %08lx/%08lx\n", context.SegSs, expect.SegSs );
1885
1886 ok( LOWORD(context.FloatSave.ControlWord) == LOWORD(expect.x87_control),
1887 "wrong x87 control word %#lx/%#lx.\n", context.FloatSave.ControlWord, expect.x87_control );
1888 ok( LOWORD(expect.x87_control) == LOWORD(new_x87_control),
1889 "x87 control word changed in NtGetContextThread() %#x/%#x.\n",
1890 LOWORD(expect.x87_control), LOWORD(new_x87_control) );
1891
1892#undef COMPARE
1893}
1894
1895static BYTE saved_KiUserExceptionDispatcher_bytes[7];
1896static BOOL hook_called;
1897static void *hook_KiUserExceptionDispatcher_eip;
1898static void *dbg_except_continue_handler_eip;
1899static void *hook_exception_address;
1900
1901static struct
1902{
1903 DWORD old_eax;
1904 DWORD old_edx;
1905 DWORD old_esi;
1906 DWORD old_edi;
1907 DWORD old_ebp;
1908 DWORD old_esp;
1909 DWORD new_eax;
1910 DWORD new_edx;
1911 DWORD new_esi;
1912 DWORD new_edi;
1913 DWORD new_ebp;
1914 DWORD new_esp;
1915}
1916test_kiuserexceptiondispatcher_regs;
1917
1918static DWORD dbg_except_continue_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
1920{
1921 ok(hook_called, "Hook was not called.\n");
1922
1923 ok(rec->ExceptionCode == 0x80000003, "Got unexpected ExceptionCode %#lx.\n", rec->ExceptionCode);
1924
1925 got_exception = 1;
1926 dbg_except_continue_handler_eip = (void *)context->Eip;
1927 ++context->Eip;
1928
1929 context->Eip = (DWORD)code_mem + 0x1c;
1930 context->Eax = 0xdeadbeef;
1931 context->Esi = 0xdeadbeef;
1932 pRtlUnwind(NtCurrentTeb()->Tib.ExceptionList, (void *)context->Eip, rec, (void *)0xdeadbeef);
1934}
1935
1936static LONG WINAPI dbg_except_continue_vectored_handler(struct _EXCEPTION_POINTERS *e)
1937{
1938 EXCEPTION_RECORD *rec = e->ExceptionRecord;
1939 CONTEXT *context = e->ContextRecord;
1940
1941 trace("dbg_except_continue_vectored_handler, code %#lx, eip %#lx, ExceptionAddress %p.\n",
1942 rec->ExceptionCode, context->Eip, rec->ExceptionAddress);
1943
1944 ok(rec->ExceptionCode == 0x80000003, "Got unexpected ExceptionCode %#lx.\n", rec->ExceptionCode);
1945
1946 got_exception = 1;
1947
1948 if ((ULONG_PTR)rec->ExceptionAddress == context->Eip + 1)
1949 {
1950 /* XP and Vista+ have ExceptionAddress == Eip + 1, Eip is adjusted even
1951 * for software raised breakpoint exception.
1952 * Win2003 has Eip not adjusted and matching ExceptionAddress.
1953 * Win2008 has Eip not adjusted and ExceptionAddress not filled for
1954 * software raised exception. */
1955 context->Eip = (ULONG_PTR)rec->ExceptionAddress;
1956 }
1957
1959}
1960
1961/* Use CDECL to leave arguments on stack. */
1962static void * CDECL hook_KiUserExceptionDispatcher(EXCEPTION_RECORD *rec, CONTEXT *context)
1963{
1964 CONTEXT_EX *xctx = (CONTEXT_EX *)(context + 1);
1965
1966 trace( "rec %p context %p context->Eip %#lx, context->Esp %#lx (%x), ContextFlags %#lx.\n",
1967 rec, context, context->Eip, context->Esp,
1968 (char *)context->Esp - (char *)&rec, context->ContextFlags);
1969 trace( "xstate %lx = %p (%x) %lx\n", xctx->XState.Offset, (char *)xctx + xctx->XState.Offset,
1970 (char *)xctx + xctx->XState.Offset - (char *)&rec, xctx->XState.Length );
1971
1972 ok( (char *)rec->ExceptionInformation <= (char *)context &&
1973 (char *)(rec + 1) >= (char *)context, "wrong ptrs %p / %p\n", rec, context );
1974 ok( xctx->All.Offset == -sizeof(CONTEXT), "wrong All.Offset %lx\n", xctx->All.Offset );
1975 ok( xctx->All.Length >= sizeof(CONTEXT) + sizeof(CONTEXT_EX), "wrong All.Length %lx\n", xctx->All.Length );
1976 ok( xctx->Legacy.Offset == -sizeof(CONTEXT), "wrong Legacy.Offset %lx\n", xctx->All.Offset );
1977 ok( xctx->Legacy.Length == sizeof(CONTEXT), "wrong Legacy.Length %lx\n", xctx->All.Length );
1978
1979 hook_called = TRUE;
1980 hook_KiUserExceptionDispatcher_eip = (void *)context->Eip;
1981 hook_exception_address = rec->ExceptionAddress;
1982 memcpy(pKiUserExceptionDispatcher, saved_KiUserExceptionDispatcher_bytes,
1983 sizeof(saved_KiUserExceptionDispatcher_bytes));
1985}
1986
1987static void test_KiUserExceptionDispatcher(void)
1988{
1989 PVOID vectored_handler;
1990 static BYTE except_code[] =
1991 {
1992 0xb9, /* mov imm32, %ecx */
1993 /* offset: 0x1 */
1994 0x00, 0x00, 0x00, 0x00,
1995
1996 0x89, 0x01, /* mov %eax, (%ecx) */
1997 0x89, 0x51, 0x04, /* mov %edx, 0x4(%ecx) */
1998 0x89, 0x71, 0x08, /* mov %esi, 0x8(%ecx) */
1999 0x89, 0x79, 0x0c, /* mov %edi, 0xc(%ecx) */
2000 0x89, 0x69, 0x10, /* mov %ebp, 0x10(%ecx) */
2001 0x89, 0x61, 0x14, /* mov %esp, 0x14(%ecx) */
2002 0x83, 0xc1, 0x18, /* add $0x18, %ecx */
2003
2004 /* offset: 0x19 */
2005 0xcc, /* int3 */
2006
2007 0x0f, 0x0b, /* ud2, illegal instruction */
2008
2009 /* offset: 0x1c */
2010 0xb9, /* mov imm32, %ecx */
2011 /* offset: 0x1d */
2012 0x00, 0x00, 0x00, 0x00,
2013
2014 0x89, 0x01, /* mov %eax, (%ecx) */
2015 0x89, 0x51, 0x04, /* mov %edx, 0x4(%ecx) */
2016 0x89, 0x71, 0x08, /* mov %esi, 0x8(%ecx) */
2017 0x89, 0x79, 0x0c, /* mov %edi, 0xc(%ecx) */
2018 0x89, 0x69, 0x10, /* mov %ebp, 0x10(%ecx) */
2019 0x89, 0x61, 0x14, /* mov %esp, 0x14(%ecx) */
2020 0x8b, 0x71, 0xf0, /* mov -0x10(%ecx),%esi */
2021
2022 0xc3, /* ret */
2023 };
2024 static BYTE hook_trampoline[] =
2025 {
2026 0xff, 0x15,
2027 /* offset: 2 bytes */
2028 0x00, 0x00, 0x00, 0x00, /* call *addr */ /* call hook implementation. */
2029 0xff, 0xe0, /* jmp *%eax */
2030 };
2031 void *phook_KiUserExceptionDispatcher = hook_KiUserExceptionDispatcher;
2032 BYTE patched_KiUserExceptionDispatcher_bytes[7];
2033 DWORD old_protect1, old_protect2;
2035 void *bpt_address;
2036 BYTE *ptr;
2037 BOOL ret;
2038
2039 if (!pRtlUnwind)
2040 {
2041 win_skip("RtlUnwind is not available.\n");
2042 return;
2043 }
2044
2045#ifdef __REACTOS__ // This hangs on Windows 10 WOW64
2046 if (is_wow64)
2047 {
2048 win_skip("Skipping test that hangs on WOW64\n");
2049 return;
2050 }
2051#endif
2052
2053 *(DWORD *)(except_code + 1) = (DWORD)&test_kiuserexceptiondispatcher_regs;
2054 *(DWORD *)(except_code + 0x1d) = (DWORD)&test_kiuserexceptiondispatcher_regs.new_eax;
2055
2056 *(unsigned int *)(hook_trampoline + 2) = (ULONG_PTR)&phook_KiUserExceptionDispatcher;
2057
2058 ret = VirtualProtect(hook_trampoline, ARRAY_SIZE(hook_trampoline), PAGE_EXECUTE_READWRITE, &old_protect1);
2059 ok(ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError());
2060
2061 ret = VirtualProtect(pKiUserExceptionDispatcher, sizeof(saved_KiUserExceptionDispatcher_bytes),
2062 PAGE_EXECUTE_READWRITE, &old_protect2);
2063 ok(ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError());
2064
2065 memcpy(saved_KiUserExceptionDispatcher_bytes, pKiUserExceptionDispatcher,
2066 sizeof(saved_KiUserExceptionDispatcher_bytes));
2067
2068 ptr = patched_KiUserExceptionDispatcher_bytes;
2069 /* mov $hook_trampoline, %eax */
2070 *ptr++ = 0xb8;
2071 *(void **)ptr = hook_trampoline;
2072 ptr += sizeof(void *);
2073 /* jmp *eax */
2074 *ptr++ = 0xff;
2075 *ptr++ = 0xe0;
2076
2077 memcpy(pKiUserExceptionDispatcher, patched_KiUserExceptionDispatcher_bytes,
2078 sizeof(patched_KiUserExceptionDispatcher_bytes));
2079 got_exception = 0;
2080 run_exception_test(dbg_except_continue_handler, NULL, except_code, sizeof(except_code),
2082
2083 ok(got_exception, "Handler was not called.\n");
2084 ok(hook_called, "Hook was not called.\n");
2085
2086 ok(test_kiuserexceptiondispatcher_regs.new_eax == 0xdeadbeef, "Got unexpected eax %#lx.\n",
2087 test_kiuserexceptiondispatcher_regs.new_eax);
2088 ok(test_kiuserexceptiondispatcher_regs.new_esi == 0xdeadbeef, "Got unexpected esi %#lx.\n",
2089 test_kiuserexceptiondispatcher_regs.new_esi);
2090 ok(test_kiuserexceptiondispatcher_regs.old_edi
2091 == test_kiuserexceptiondispatcher_regs.new_edi, "edi does not match.\n");
2092 ok(test_kiuserexceptiondispatcher_regs.old_ebp
2093 == test_kiuserexceptiondispatcher_regs.new_ebp, "ebp does not match.\n");
2094
2095 bpt_address = (BYTE *)code_mem + 0x19;
2096
2097 ok(hook_exception_address == bpt_address || broken(!hook_exception_address) /* Win2008 */,
2098 "Got unexpected exception address %p, expected %p.\n",
2099 hook_exception_address, bpt_address);
2100 ok(hook_KiUserExceptionDispatcher_eip == bpt_address, "Got unexpected exception address %p, expected %p.\n",
2101 hook_KiUserExceptionDispatcher_eip, bpt_address);
2102 ok(dbg_except_continue_handler_eip == bpt_address, "Got unexpected exception address %p, expected %p.\n",
2103 dbg_except_continue_handler_eip, bpt_address);
2104
2105 record.ExceptionCode = 0x80000003;
2106 record.ExceptionFlags = 0;
2107 record.ExceptionRecord = NULL;
2108 record.ExceptionAddress = NULL; /* does not matter, copied return address */
2109 record.NumberParameters = 0;
2110
2111 vectored_handler = AddVectoredExceptionHandler(TRUE, dbg_except_continue_vectored_handler);
2112
2113 memcpy(pKiUserExceptionDispatcher, patched_KiUserExceptionDispatcher_bytes,
2114 sizeof(patched_KiUserExceptionDispatcher_bytes));
2115 got_exception = 0;
2116 hook_called = FALSE;
2117
2118 pRtlRaiseException(&record);
2119
2120 ok(got_exception, "Handler was not called.\n");
2121 ok(hook_called || broken(!hook_called) /* 2003 */, "Hook was not called.\n");
2122
2123 memcpy(pKiUserExceptionDispatcher, saved_KiUserExceptionDispatcher_bytes,
2124 sizeof(saved_KiUserExceptionDispatcher_bytes));
2125
2126 RemoveVectoredExceptionHandler(vectored_handler);
2127 ret = VirtualProtect(pKiUserExceptionDispatcher, sizeof(saved_KiUserExceptionDispatcher_bytes),
2128 old_protect2, &old_protect2);
2129 ok(ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError());
2130 ret = VirtualProtect(hook_trampoline, ARRAY_SIZE(hook_trampoline), old_protect1, &old_protect1);
2131 ok(ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError());
2132}
2133
2134static BYTE saved_KiUserApcDispatcher[7];
2135
2136static void * CDECL hook_KiUserApcDispatcher( void *func, ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3 )
2137{
2138 CONTEXT *context = (CONTEXT *)((ULONG_PTR)&arg3 + sizeof(ULONG));
2139
2140 ok( func == apc_func, "wrong function %p / %p\n", func, apc_func );
2141 ok( arg1 == 0x1234 + apc_count, "wrong arg1 %Ix\n", arg1 );
2142 ok( arg2 == 0x5678, "wrong arg2 %Ix\n", arg2 );
2143 ok( arg3 == 0xdeadbeef, "wrong arg3 %Ix\n", arg3 );
2144
2145 if (context->ContextFlags != 1)
2146 {
2147 trace( "context %p eip %lx ebp %lx esp %lx (%x)\n",
2148 context, context->Eip, context->Ebp, context->Esp, (char *)context->Esp - (char *)&func );
2149 }
2150 else /* new style with alertable arg and CONTEXT_EX */
2151 {
2152 CONTEXT_EX *xctx;
2153 ULONG *alertable = (ULONG *)context;
2154
2155 context = (CONTEXT *)(alertable + 1);
2156 xctx = (CONTEXT_EX *)(context + 1);
2157
2158 trace( "alertable %lx context %p eip %lx ebp %lx esp %lx (%x)\n", *alertable,
2159 context, context->Eip, context->Ebp, context->Esp, (char *)context->Esp - (char *)&func );
2160 if ((void *)(xctx + 1) < (void *)context->Esp)
2161 {
2162 ok( xctx->All.Offset == -sizeof(CONTEXT), "wrong All.Offset %lx\n", xctx->All.Offset );
2163 ok( xctx->All.Length >= sizeof(CONTEXT) + sizeof(CONTEXT_EX), "wrong All.Length %lx\n", xctx->All.Length );
2164 ok( xctx->Legacy.Offset == -sizeof(CONTEXT), "wrong Legacy.Offset %lx\n", xctx->All.Offset );
2165 ok( xctx->Legacy.Length == sizeof(CONTEXT), "wrong Legacy.Length %lx\n", xctx->All.Length );
2166 }
2167
2168 if (apc_count) *alertable = 0;
2169 pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234 + apc_count + 1, 0x5678, 0xdeadbeef );
2170 }
2171
2172 hook_called = TRUE;
2173 memcpy( pKiUserApcDispatcher, saved_KiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher));
2174 return pKiUserApcDispatcher;
2175}
2176
2177static void test_KiUserApcDispatcher(void)
2178{
2179 BYTE hook_trampoline[] =
2180 {
2181 0xff, 0x15,
2182 /* offset: 2 bytes */
2183 0x00, 0x00, 0x00, 0x00, /* call *addr */ /* call hook implementation. */
2184 0xff, 0xe0, /* jmp *%eax */
2185 };
2186
2187 BYTE patched_KiUserApcDispatcher[7];
2188 void *phook_KiUserApcDispatcher = hook_KiUserApcDispatcher;
2189 DWORD old_protect;
2190 BYTE *ptr;
2191 BOOL ret;
2192
2193 *(ULONG_PTR *)(hook_trampoline + 2) = (ULONG_PTR)&phook_KiUserApcDispatcher;
2194 memcpy(code_mem, hook_trampoline, sizeof(hook_trampoline));
2195
2196 ret = VirtualProtect( pKiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher),
2197 PAGE_EXECUTE_READWRITE, &old_protect );
2198 ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() );
2199
2200 memcpy( saved_KiUserApcDispatcher, pKiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher) );
2201 ptr = patched_KiUserApcDispatcher;
2202 /* mov $hook_trampoline, %eax */
2203 *ptr++ = 0xb8;
2204 *(void **)ptr = code_mem;
2205 ptr += sizeof(void *);
2206 /* jmp *eax */
2207 *ptr++ = 0xff;
2208 *ptr++ = 0xe0;
2209 memcpy( pKiUserApcDispatcher, patched_KiUserApcDispatcher, sizeof(patched_KiUserApcDispatcher) );
2210
2211 apc_count = 0;
2212 hook_called = FALSE;
2213 pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef );
2214 SleepEx( 0, TRUE );
2215 ok( apc_count == 1 || apc_count == 2, "APC count %u\n", apc_count );
2216 ok( hook_called, "hook was not called\n" );
2217
2218 if (apc_count == 2)
2219 {
2220 memcpy( pKiUserApcDispatcher, patched_KiUserApcDispatcher, sizeof(patched_KiUserApcDispatcher) );
2221 pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234 + apc_count, 0x5678, 0xdeadbeef );
2222 SleepEx( 0, TRUE );
2223 ok( apc_count == 3, "APC count %u\n", apc_count );
2224 SleepEx( 0, TRUE );
2225 ok( apc_count == 4, "APC count %u\n", apc_count );
2226 }
2227 VirtualProtect( pKiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher), old_protect, &old_protect );
2228}
2229
2230static void CDECL hook_KiUserCallbackDispatcher( void *eip, ULONG id, ULONG *args, ULONG len,
2231 ULONG unk1, ULONG unk2, ULONG arg0, ULONG arg1 )
2232{
2233 KERNEL_CALLBACK_PROC func = NtCurrentTeb()->Peb->KernelCallbackTable[id];
2234
2235 trace( "eip %p id %lx args %p (%x) len %lx unk1 %lx unk2 %lx args %lx,%lx\n",
2236 eip, id, args, (char *)args - (char *)&eip, len, unk1, unk2, arg0, arg1 );
2237
2238 if (args[0] != arg0) /* new style with extra esp */
2239 {
2240 void *esp = (void *)arg0;
2241
2242 ok( args[0] == arg1, "wrong arg1 %lx / %lx\n", args[0], arg1 );
2243 ok( (char *)esp - ((char *)args + len) < 0x10, "wrong esp offset %p / %p\n", esp, args );
2244 }
2245
2246 if (eip && pRtlPcToFileHeader)
2247 {
2248 void *mod, *win32u = GetModuleHandleA("win32u.dll");
2249
2250 pRtlPcToFileHeader( eip, &mod );
2251 if (win32u) ok( mod == win32u, "ret address %p not in win32u %p\n", eip, win32u );
2252 else trace( "ret address %p in %p\n", eip, mod );
2253 }
2254 NtCallbackReturn( NULL, 0, func( args, len ));
2255}
2256
2257static void test_KiUserCallbackDispatcher(void)
2258{
2259 BYTE saved_code[7], patched_code[7];
2260 DWORD old_protect;
2261 BYTE *ptr;
2262 BOOL ret;
2263
2264 ret = VirtualProtect( pKiUserCallbackDispatcher, sizeof(saved_code),
2265 PAGE_EXECUTE_READWRITE, &old_protect );
2266 ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() );
2267
2268 memcpy( saved_code, pKiUserCallbackDispatcher, sizeof(saved_code) );
2269 ptr = patched_code;
2270 /* mov $hook_trampoline, %eax */
2271 *ptr++ = 0xb8;
2272 *(void **)ptr = hook_KiUserCallbackDispatcher;
2273 ptr += sizeof(void *);
2274 /* call *eax */
2275 *ptr++ = 0xff;
2276 *ptr++ = 0xd0;
2277 memcpy( pKiUserCallbackDispatcher, patched_code, sizeof(patched_code) );
2278
2279 DestroyWindow( CreateWindowA( "Static", "test", 0, 0, 0, 0, 0, 0, 0, 0, 0 ));
2280
2281 memcpy( pKiUserCallbackDispatcher, saved_code, sizeof(saved_code));
2282 VirtualProtect( pKiUserCallbackDispatcher, sizeof(saved_code), old_protect, &old_protect );
2283}
2284
2285static void test_instrumentation_callback(void)
2286{
2287 static const BYTE instrumentation_callback[] =
2288 {
2289 0xff, 0x05, /* inc instrumentation_call_count */
2290 /* &instrumentation_call_count, offset 2 */ 0x00, 0x00, 0x00, 0x00,
2291 0xff, 0xe1, /* jmp *ecx */
2292 };
2293
2294 unsigned int instrumentation_call_count;
2296
2298
2299 memcpy( code_mem, instrumentation_callback, sizeof(instrumentation_callback) );
2300 *(volatile void **)((char *)code_mem + 2) = &instrumentation_call_count;
2301
2302 memset(&info, 0, sizeof(info));
2303 /* On 32 bit the structure is never used and just a callback pointer is expected. */
2304 info.Version = (ULONG_PTR)code_mem;
2305 instrumentation_call_count = 0;
2308 || broken( status == STATUS_PRIVILEGE_NOT_HELD ) /* some versions and machines before Win10 */,
2309 "got %#lx.\n", status );
2310 if (status)
2311 {
2312 win_skip( "Failed setting instrumenation callback.\n" );
2313 return;
2314 }
2315 DestroyWindow( CreateWindowA( "Static", "test", 0, 0, 0, 0, 0, 0, 0, 0, 0 ));
2316 todo_wine ok( instrumentation_call_count, "got %u.\n", instrumentation_call_count );
2317
2318 memset(&info, 0, sizeof(info));
2319 instrumentation_call_count = 0;
2321 ok( status == STATUS_SUCCESS, "got %#lx.\n", status );
2322 ok( !instrumentation_call_count, "got %u.\n", instrumentation_call_count );
2323}
2324
2325#elif defined(__x86_64__)
2326
2327static LONG consolidate_dummy_called;
2328static PVOID CALLBACK test_consolidate_dummy(EXCEPTION_RECORD *rec)
2329{
2331
2332 switch (InterlockedIncrement(&consolidate_dummy_called))
2333 {
2334 case 1: /* RtlRestoreContext */
2335 ok(ctx->Rip == 0xdeadbeef, "RtlRestoreContext wrong Rip, expected: 0xdeadbeef, got: %Ix\n", ctx->Rip);
2336 ok( rec->ExceptionInformation[10] == -1, "wrong info %Ix\n", rec->ExceptionInformation[10] );
2337 break;
2338 case 2: /* RtlUnwindEx */
2339 ok(ctx->Rip != 0xdeadbeef, "RtlUnwindEx wrong Rip, got: %Ix\n", ctx->Rip );
2340 if (is_arm64ec)
2341 {
2343 _JUMP_BUFFER *buf = (void *)rec->ExceptionInformation[3];
2345 int i;
2346
2347 ok( rec->ExceptionInformation[10] != -1, "wrong info %Ix\n", rec->ExceptionInformation[10] );
2348 ok( regs->GpNvRegs[0] == buf->R12, "wrong reg X19, %Ix / %Ix\n", regs->GpNvRegs[0], buf->R12 );
2349 ok( regs->GpNvRegs[1] == buf->R13, "wrong reg X20, %Ix / %Ix\n", regs->GpNvRegs[1], buf->R13 );
2350 ok( regs->GpNvRegs[2] == buf->R14, "wrong reg X21, %Ix / %Ix\n", regs->GpNvRegs[2], buf->R14 );
2351 ok( regs->GpNvRegs[3] == buf->R15, "wrong reg X22, %Ix / %Ix\n", regs->GpNvRegs[3], buf->R15 );
2352 ok( regs->GpNvRegs[4] == 0, "wrong reg X23, %Ix / 0\n", regs->GpNvRegs[4] );
2353 ok( regs->GpNvRegs[5] == 0, "wrong reg X24, %Ix / 0\n", regs->GpNvRegs[5] );
2354 ok( regs->GpNvRegs[6] == buf->Rsi, "wrong reg X25, %Ix / %Ix\n", regs->GpNvRegs[6], buf->Rsi );
2355 ok( regs->GpNvRegs[7] == buf->Rdi, "wrong reg X26, %Ix / %Ix\n", regs->GpNvRegs[7], buf->Rdi );
2356 ok( regs->GpNvRegs[8] == buf->Rbx, "wrong reg X27, %Ix / %Ix\n", regs->GpNvRegs[8], buf->Rbx );
2357 ok( regs->GpNvRegs[9] == 0, "wrong reg X28, %Ix / 0\n", regs->GpNvRegs[9] );
2358 ok( regs->GpNvRegs[10] == buf->Rbp,"wrong reg X29, %Ix / %Ix\n", regs->GpNvRegs[10], buf->Rbp );
2359 for (i = 0; i < 8; i++)
2360 ok(regs->FpNvRegs[i] == ec_ctx->V[i + 8].D[0], "wrong reg D%u, expected: %g, got: %g\n",
2361 i + 8, regs->FpNvRegs[i], ec_ctx->V[i + 8].D[0] );
2362 }
2363 else ok( rec->ExceptionInformation[10] == -1, "wrong info %Ix\n", rec->ExceptionInformation[10] );
2364 break;
2365 }
2366 return (PVOID)rec->ExceptionInformation[2];
2367}
2368
2369static void test_restore_context(void)
2370{
2371 SETJMP_FLOAT128 *fltsave;
2372 EXCEPTION_RECORD rec;
2373 _JUMP_BUFFER buf;
2374 CONTEXT ctx;
2375 int i;
2376 LONG pass;
2377
2378 if (!pRtlUnwindEx || !pRtlRestoreContext || !pRtlCaptureContext)
2379 {
2380 skip("RtlUnwindEx/RtlCaptureContext/RtlRestoreContext not found\n");
2381 return;
2382 }
2383
2384 /* RtlRestoreContext(NULL, NULL); crashes on Windows */
2385
2386 /* test simple case of capture and restore context */
2387 pass = 0;
2388 InterlockedIncrement(&pass); /* interlocked to prevent compiler from moving after capture */
2389 pRtlCaptureContext(&ctx);
2390 if (InterlockedIncrement(&pass) == 2) /* interlocked to prevent compiler from moving before capture */
2391 {
2392 pRtlRestoreContext(&ctx, NULL);
2393 ok(0, "shouldn't be reached\n");
2394 }
2395 else
2396 ok(pass < 4, "unexpected pass %ld\n", pass);
2397
2398 /* test with jmp using RtlRestoreContext */
2399 pass = 0;
2402 InterlockedIncrement(&pass); /* only called once */
2403 setjmp((_JBTYPE *)&buf);
2405 if (pass == 3)
2406 {
2408 rec.NumberParameters = 1;
2409 rec.ExceptionInformation[0] = (DWORD64)&buf;
2410
2411 ok(buf.FpCsr == 0x27f, "Got unexpected FpCsr %#x.\n", buf.FpCsr);
2412 buf.FpCsr = 0x7f;
2413 buf.MxCsr = 0x3f80;
2414 /* uses buf.Rip instead of ctx.Rip */
2415 pRtlRestoreContext(&ctx, &rec);
2416 ok(0, "shouldn't be reached\n");
2417 }
2418 else if (pass == 4)
2419 {
2420 ok(buf.Rbx == ctx.Rbx, "longjmp failed for Rbx, expected: %Ix, got: %Ix\n", buf.Rbx, ctx.Rbx);
2421 ok(buf.Rsp == ctx.Rsp, "longjmp failed for Rsp, expected: %Ix, got: %Ix\n", buf.Rsp, ctx.Rsp);
2422 ok(buf.Rbp == ctx.Rbp, "longjmp failed for Rbp, expected: %Ix, got: %Ix\n", buf.Rbp, ctx.Rbp);
2423 ok(buf.Rsi == ctx.Rsi, "longjmp failed for Rsi, expected: %Ix, got: %Ix\n", buf.Rsi, ctx.Rsi);
2424 ok(buf.Rdi == ctx.Rdi, "longjmp failed for Rdi, expected: %Ix, got: %Ix\n", buf.Rdi, ctx.Rdi);
2425 ok(buf.R12 == ctx.R12, "longjmp failed for R12, expected: %Ix, got: %Ix\n", buf.R12, ctx.R12);
2426 ok(buf.R13 == ctx.R13, "longjmp failed for R13, expected: %Ix, got: %Ix\n", buf.R13, ctx.R13);
2427 ok(buf.R14 == ctx.R14, "longjmp failed for R14, expected: %Ix, got: %Ix\n", buf.R14, ctx.R14);
2428 ok(buf.R15 == ctx.R15, "longjmp failed for R15, expected: %Ix, got: %Ix\n", buf.R15, ctx.R15);
2429
2430 fltsave = &buf.Xmm6;
2431 for (i = 0; i < 10; i++)
2432 {
2433 ok(fltsave[i].Part[0] == ctx.FltSave.XmmRegisters[i + 6].Low,
2434 "longjmp failed for Xmm%d, expected %Ix, got %Ix\n", i + 6,
2435 fltsave[i].Part[0], ctx.FltSave.XmmRegisters[i + 6].Low);
2436
2437 ok(fltsave[i].Part[1] == ctx.FltSave.XmmRegisters[i + 6].High,
2438 "longjmp failed for Xmm%d, expected %Ix, got %Ix\n", i + 6,
2439 fltsave[i].Part[1], ctx.FltSave.XmmRegisters[i + 6].High);
2440 }
2441 ok(ctx.FltSave.ControlWord == 0x7f, "Got unexpected float control word %#x.\n", ctx.FltSave.ControlWord);
2442 ok(ctx.MxCsr == 0x3f80, "Got unexpected MxCsr %#lx.\n", ctx.MxCsr);
2443 ok(ctx.FltSave.MxCsr == 0x3f80, "Got unexpected MxCsr %#lx.\n", ctx.FltSave.MxCsr);
2444 buf.FpCsr = 0x27f;
2445 buf.MxCsr = 0x1f80;
2446 pRtlRestoreContext(&ctx, &rec);
2447 ok(0, "shouldn't be reached\n");
2448 }
2449 else if (pass == 5)
2450 {
2451 ok(ctx.FltSave.ControlWord == 0x27f, "Got unexpected float control word %#x.\n", ctx.FltSave.ControlWord);
2452 ok(ctx.FltSave.MxCsr == 0x1f80, "Got unexpected MxCsr %#lx.\n", ctx.MxCsr);
2453 }
2454 else
2455 ok(0, "unexpected pass %ld\n", pass);
2456
2457 /* test with jmp through RtlUnwindEx */
2458 pass = 0;
2460 pRtlCaptureContext(&ctx);
2461 InterlockedIncrement(&pass); /* only called once */
2462 setjmp((_JBTYPE *)&buf);
2464 if (pass == 3)
2465 {
2467 rec.NumberParameters = 1;
2468 rec.ExceptionInformation[0] = (DWORD64)&buf;
2469
2470 /* uses buf.Rip instead of bogus 0xdeadbeef */
2471 pRtlUnwindEx((void*)buf.Frame, (void*)0xdeadbeef, &rec, NULL, &ctx, NULL);
2472 ok(0, "shouldn't be reached\n");
2473 }
2474 else
2475 ok(pass == 4, "unexpected pass %ld\n", pass);
2476
2477
2478 /* test with consolidate */
2479 pass = 0;
2483 if (pass == 2)
2484 {
2486 rec.NumberParameters = 3;
2487 rec.ExceptionInformation[0] = (DWORD64)test_consolidate_dummy;
2488 rec.ExceptionInformation[1] = (DWORD64)&ctx;
2489 rec.ExceptionInformation[2] = ctx.Rip;
2490 rec.ExceptionInformation[10] = -1;
2491 ctx.Rip = 0xdeadbeef;
2492
2493 pRtlRestoreContext(&ctx, &rec);
2494 ok(0, "shouldn't be reached\n");
2495 }
2496 else if (pass == 3)
2497 ok(consolidate_dummy_called == 1, "test_consolidate_dummy not called\n");
2498 else
2499 ok(0, "unexpected pass %ld\n", pass);
2500
2501 /* test with consolidate through RtlUnwindEx */
2502 pass = 0;
2504 pRtlCaptureContext(&ctx);
2506 if (pass == 2)
2507 {
2509 rec.NumberParameters = 4;
2510 rec.ExceptionInformation[0] = (DWORD64)test_consolidate_dummy;
2511 rec.ExceptionInformation[1] = (DWORD64)&ctx;
2512 rec.ExceptionInformation[2] = ctx.Rip;
2513 rec.ExceptionInformation[3] = (DWORD64)&buf;
2514 rec.ExceptionInformation[10] = -1; /* otherwise it doesn't get set */
2515 ctx.Rip = 0xdeadbeef;
2516 /* uses consolidate callback Rip instead of bogus 0xdeadbeef */
2517 setjmp((_JBTYPE *)&buf);
2518 pRtlUnwindEx((void*)buf.Frame, (void*)0xdeadbeef, &rec, NULL, &ctx, NULL);
2519 ok(0, "shouldn't be reached\n");
2520 }
2521 else if (pass == 3)
2522 ok(consolidate_dummy_called == 2, "test_consolidate_dummy not called\n");
2523 else
2524 ok(0, "unexpected pass %ld\n", pass);
2525}
2526
2527static int termination_handler_called;
2528static void WINAPI termination_handler(ULONG flags, ULONG64 frame)
2529{
2530 termination_handler_called++;
2531
2532 ok(flags == 1 || broken(flags == 0x401), "flags = %lx\n", flags);
2533 ok(frame == 0x1234, "frame = %p\n", (void*)frame);
2534}
2535
2536static void test___C_specific_handler(void)
2537{
2539 EXCEPTION_RECORD rec;
2541 ULONG64 frame;
2543 SCOPE_TABLE scope_table;
2544
2545 if (!p__C_specific_handler)
2546 {
2547 win_skip("__C_specific_handler not available\n");
2548 return;
2549 }
2550
2551 memset(&rec, 0, sizeof(rec));
2553 frame = 0x1234;
2554 memset(&dispatch, 0, sizeof(dispatch));
2556 dispatch.ControlPc = dispatch.ImageBase + 0x200;
2557 dispatch.HandlerData = &scope_table;
2558 dispatch.ContextRecord = &context;
2559 scope_table.Count = 1;
2560 scope_table.ScopeRecord[0].BeginAddress = 0x200;
2561 scope_table.ScopeRecord[0].EndAddress = 0x400;
2562 scope_table.ScopeRecord[0].HandlerAddress = (ULONG_PTR)termination_handler-dispatch.ImageBase;
2563 scope_table.ScopeRecord[0].JumpTarget = 0;
2564 memset(&context, 0, sizeof(context));
2565
2566 termination_handler_called = 0;
2567 ret = p__C_specific_handler(&rec, frame, &context, &dispatch);
2568 ok(ret == ExceptionContinueSearch, "__C_specific_handler returned %x\n", ret);
2569 ok(termination_handler_called == 1, "termination_handler_called = %d\n",
2570 termination_handler_called);
2571 ok(dispatch.ScopeIndex == 1, "dispatch.ScopeIndex = %ld\n", dispatch.ScopeIndex);
2572
2573 ret = p__C_specific_handler(&rec, frame, &context, &dispatch);
2574 ok(ret == ExceptionContinueSearch, "__C_specific_handler returned %x\n", ret);
2575 ok(termination_handler_called == 1, "termination_handler_called = %d\n",
2576 termination_handler_called);
2577 ok(dispatch.ScopeIndex == 1, "dispatch.ScopeIndex = %ld\n", dispatch.ScopeIndex);
2578}
2579
2580/* This is heavily based on the i386 exception tests. */
2581static const struct exception
2582{
2583 BYTE code[40]; /* asm code */
2584 BYTE offset; /* offset of faulting instruction */
2585 BYTE length; /* length of faulting instruction */
2586 NTSTATUS status; /* expected status code */
2587 DWORD nb_params; /* expected number of parameters */
2588 ULONG64 params[4]; /* expected parameters */
2589 NTSTATUS alt_status; /* alternative status code */
2590 DWORD alt_nb_params; /* alternative number of parameters */
2591 ULONG64 alt_params[4]; /* alternative parameters */
2592} exceptions[] =
2593{
2594/* 0 */
2595 /* test some privileged instructions */
2596 { { 0xfb, 0xc3 }, /* 0: sti; ret */
2598 { { 0x6c, 0xc3 }, /* 1: insb (%dx); ret */
2600 { { 0x6d, 0xc3 }, /* 2: insl (%dx); ret */
2602 { { 0x6e, 0xc3 }, /* 3: outsb (%dx); ret */
2604 { { 0x6f, 0xc3 }, /* 4: outsl (%dx); ret */
2606/* 5 */
2607 { { 0xe4, 0x11, 0xc3 }, /* 5: inb $0x11,%al; ret */
2609 { { 0xe5, 0x11, 0xc3 }, /* 6: inl $0x11,%eax; ret */
2611 { { 0xe6, 0x11, 0xc3 }, /* 7: outb %al,$0x11; ret */
2613 { { 0xe7, 0x11, 0xc3 }, /* 8: outl %eax,$0x11; ret */
2615 { { 0xed, 0xc3 }, /* 9: inl (%dx),%eax; ret */
2617/* 10 */
2618 { { 0xee, 0xc3 }, /* 10: outb %al,(%dx); ret */
2620 { { 0xef, 0xc3 }, /* 11: outl %eax,(%dx); ret */
2622 { { 0xf4, 0xc3 }, /* 12: hlt; ret */
2624 { { 0xfa, 0xc3 }, /* 13: cli; ret */
2626
2627 /* test iret to invalid selector */
2628 { { 0x6a, 0x00, 0x6a, 0x00, 0x6a, 0x00, 0xcf, 0x48, 0x83, 0xc4, 0x18, 0xc3 },
2629 /* 15: pushq $0; pushq $0; pushq $0; iret; addq $24,%rsp; ret */
2630 6, 1, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffffffffffff } },
2631/* 15 */
2632 /* test loading an invalid selector */
2633 { { 0xb8, 0xef, 0xbe, 0x00, 0x00, 0x8e, 0xe8, 0xc3 }, /* 16: mov $beef,%ax; mov %ax,%gs; ret */
2634 5, 2, STATUS_ACCESS_VIOLATION, 2, { 0, 0xbee8 } }, /* 0xbee8 or 0xffffffff */
2635
2636 /* test overlong instruction (limit is 15 bytes) */
2637 { { 0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0xfa,0xc3 },
2638 0, 16, STATUS_ILLEGAL_INSTRUCTION, 0, { 0 },
2639 STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffffffffffff } },
2640
2641 /* test invalid interrupt */
2642 { { 0xcd, 0xff, 0xc3 }, /* int $0xff; ret */
2643 0, 2, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffffffffffff } },
2644
2645 /* test moves to/from Crx */
2646 { { 0x0f, 0x20, 0xc0, 0xc3 }, /* movl %cr0,%eax; ret */
2648 { { 0x0f, 0x20, 0xe0, 0xc3 }, /* movl %cr4,%eax; ret */
2650/* 20 */
2651 { { 0x0f, 0x22, 0xc0, 0xc3 }, /* movl %eax,%cr0; ret */
2653 { { 0x0f, 0x22, 0xe0, 0xc3 }, /* movl %eax,%cr4; ret */
2655
2656 /* test moves to/from Drx */
2657 { { 0x0f, 0x21, 0xc0, 0xc3 }, /* movl %dr0,%eax; ret */
2659 { { 0x0f, 0x21, 0xc8, 0xc3 }, /* movl %dr1,%eax; ret */
2661 { { 0x0f, 0x21, 0xf8, 0xc3 }, /* movl %dr7,%eax; ret */
2663/* 25 */
2664 { { 0x0f, 0x23, 0xc0, 0xc3 }, /* movl %eax,%dr0; ret */
2666 { { 0x0f, 0x23, 0xc8, 0xc3 }, /* movl %eax,%dr1; ret */
2668 { { 0x0f, 0x23, 0xf8, 0xc3 }, /* movl %eax,%dr7; ret */
2670
2671 /* test memory reads */
2672 { { 0xa1, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xfffffffffffffffc,%eax; ret */
2673 0, 9, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffffffffffc } },
2674 { { 0xa1, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xfffffffffffffffd,%eax; ret */
2675 0, 9, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffffffffffd } },
2676/* 30 */
2677 { { 0xa1, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xfffffffffffffffe,%eax; ret */
2678 0, 9, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffffffffffe } },
2679 { { 0xa1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xffffffffffffffff,%eax; ret */
2680 0, 9, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffffffffffff } },
2681
2682 /* test memory writes */
2683 { { 0xa3, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xfffffffffffffffc; ret */
2684 0, 9, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffffffffffc } },
2685 { { 0xa3, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xfffffffffffffffd; ret */
2686 0, 9, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffffffffffd } },
2687 { { 0xa3, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xfffffffffffffffe; ret */
2688 0, 9, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffffffffffe } },
2689/* 35 */
2690 { { 0xa3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xffffffffffffffff; ret */
2691 0, 9, STATUS_ACCESS_VIOLATION, 2, { 1, 0xffffffffffffffff } },
2692
2693 /* test exception with cleared segment registers */
2694 { {
2695 0x8c, 0xc0, /* mov %es,%eax */
2696 0x50, /* push %rax */
2697 0x8c, 0xd8, /* mov %ds,%eax */
2698 0x50, /* push %rax */
2699 0x8c, 0xe0, /* mov %fs,%eax */
2700 0x50, /* push %rax */
2701 0x8c, 0xe8, /* mov %gs,%eax */
2702 0x50, /* push %rax */
2703 0x31, 0xc0, /* xor %eax,%eax */
2704 0x8e, 0xc0, /* mov %eax,%es */
2705 0x8e, 0xd8, /* mov %eax,%ds */
2706#if 0
2707 /* It is observed that fs/gs base is reset
2708 on some CPUs when setting the segment value
2709 even to 0 (regardless of CPU spec
2710 saying otherwise) and it is not currently
2711 handled in Wine.
2712 Disable this part to avoid crashing the test. */
2713 0x8e, 0xe0, /* mov %eax,%fs */
2714 0x8e, 0xe8, /* mov %eax,%gs */
2715#else
2716 0x90, 0x90, /* nop */
2717 0x90, 0x90, /* nop */
2718#endif
2719 0xfa, /* cli */
2720 0x58, /* pop %rax */
2721#if 0
2722 0x8e, 0xe8, /* mov %eax,%gs */
2723 0x58, /* pop %rax */
2724 0x8e, 0xe0, /* mov %eax,%fs */
2725#else
2726 0x58, /* pop %rax */
2727 0x90, 0x90, /* nop */
2728 0x90, 0x90, /* nop */
2729#endif
2730 0x58, /* pop %rax */
2731 0x8e, 0xd8, /* mov %eax,%ds */
2732 0x58, /* pop %rax */
2733 0x8e, 0xc0, /* mov %eax,%es */
2734 0xc3, /* retq */
2735 }, 22, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
2736
2737 { { 0xf1, 0x90, 0xc3 }, /* icebp; nop; ret */
2738 1, 1, STATUS_SINGLE_STEP, 0 },
2739 { { 0xcd, 0x2c, 0xc3 },
2740 0, 2, STATUS_ASSERTION_FAILURE, 0 },
2741 { { 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, /* mov $0xb8b8b8b8, %eax */
2742 0xcd, 0x2d, 0xfa, 0xc3 }, /* int $0x2d; cli; ret */
2743 7, 1, STATUS_BREAKPOINT, 1, { 0xb8b8b8b8 } },
2744/* 40 */
2745 { { 0xb8, 0x01, 0x00, 0x00, 0x00, /* mov $0x01, %eax */
2746 0xcd, 0x2d, 0xfa, 0xc3 }, /* int $0x2d; cli; ret */
2747 8, 0, STATUS_SUCCESS, 0 },
2748};
2749
2750static int got_exception;
2751
2752static void run_exception_test_flags(void *handler, const void* context,
2753 const void *code, unsigned int code_size,
2754 DWORD access, DWORD handler_flags)
2755{
2756 unsigned char buf[2 + 8 + 2 + 8 + 8];
2757 RUNTIME_FUNCTION runtime_func;
2758 UNWIND_INFO *unwind = (UNWIND_INFO *)buf;
2759 void (*func)(void) = code_mem;
2760 DWORD oldaccess, oldaccess2;
2761
2762 runtime_func.BeginAddress = 0;
2763 runtime_func.EndAddress = code_size;
2764 runtime_func.UnwindData = 0x1000;
2765
2766 unwind->Version = 1;
2767 unwind->Flags = handler_flags;
2768 unwind->SizeOfProlog = 0;
2769 unwind->CountOfCodes = 0;
2770 unwind->FrameRegister = 0;
2771 unwind->FrameOffset = 0;
2772 *(ULONG *)&buf[4] = 0x1010;
2773 *(const void **)&buf[8] = context;
2774
2775 /* movabs $<handler>, %rax */
2776 buf[16] = 0x48;
2777 buf[17] = 0xb8;
2778 *(void **)&buf[18] = handler;
2779 /* jmp *%rax */
2780 buf[26] = 0xff;
2781 buf[27] = 0xe0;
2782
2783 memcpy((unsigned char *)code_mem + 0x1000, buf, sizeof(buf));
2784 memcpy(code_mem, code, code_size);
2785 if(access)
2786 VirtualProtect(code_mem, code_size, access, &oldaccess);
2787
2788 pRtlAddFunctionTable(&runtime_func, 1, (ULONG_PTR)code_mem);
2789 func();
2790 pRtlDeleteFunctionTable(&runtime_func);
2791
2792 if(access)
2793 VirtualProtect(code_mem, code_size, oldaccess, &oldaccess2);
2794}
2795
2796static void run_exception_test(void *handler, const void* context,
2797 const void *code, unsigned int code_size,
2798 DWORD access)
2799{
2800 run_exception_test_flags(handler, context, code, code_size, access, UNW_FLAG_EHANDLER);
2801}
2802
2803static DWORD WINAPI prot_fault_handler( EXCEPTION_RECORD *rec, ULONG64 frame,
2804 CONTEXT *context, DISPATCHER_CONTEXT *dispatcher )
2805{
2806 const struct exception *except = *(const struct exception **)(dispatcher->HandlerData);
2807 unsigned int i, parameter_count, entry = except - exceptions;
2808
2809 got_exception++;
2810 winetest_push_context( "%u: %lx", entry, rec->ExceptionCode );
2811
2812 ok( rec->ExceptionCode == except->status ||
2813 (except->alt_status != 0 && rec->ExceptionCode == except->alt_status),
2814 "Wrong exception code %lx/%lx\n", rec->ExceptionCode, except->status );
2815 ok( context->Rip == (DWORD_PTR)code_mem + except->offset,
2816 "Unexpected eip %#Ix/%#Ix\n", context->Rip, (DWORD_PTR)code_mem + except->offset );
2817 ok( rec->ExceptionAddress == (char*)context->Rip ||
2818 (rec->ExceptionCode == STATUS_BREAKPOINT && rec->ExceptionAddress == (char*)context->Rip + 1),
2819 "Unexpected exception address %p/%p\n", rec->ExceptionAddress, (char*)context->Rip );
2820
2821#ifndef __arm64ec__
2822 if (!is_arm64ec)
2823 {
2824 USHORT ds, es, fs, gs, ss;
2825#if defined(__REACTOS__) && defined(_MSC_VER)
2826 ds = __readsegds();
2827 es = __readseges();
2828 fs = __readsegfs();
2829 gs = __readseggs();
2830 ss = __readsegss();
2831#else
2832 __asm__ volatile( "movw %%ds,%0" : "=g" (ds) );
2833 __asm__ volatile( "movw %%es,%0" : "=g" (es) );
2834 __asm__ volatile( "movw %%fs,%0" : "=g" (fs) );
2835 __asm__ volatile( "movw %%gs,%0" : "=g" (gs) );
2836 __asm__ volatile( "movw %%ss,%0" : "=g" (ss) );
2837#endif
2838 ok( context->SegDs == ds || !ds, "ds %#x does not match %#x\n", context->SegDs, ds );
2839 ok( context->SegEs == es || !es, "es %#x does not match %#x\n", context->SegEs, es );
2840 ok( context->SegFs == fs || !fs, "fs %#x does not match %#x\n", context->SegFs, fs );
2841 ok( context->SegGs == gs || !gs, "gs %#x does not match %#x\n", context->SegGs, gs );
2842 ok( context->SegSs == ss, "ss %#x does not match %#x\n", context->SegSs, ss );
2843 ok( context->SegDs == context->SegSs,
2844 "ds %#x does not match ss %#x\n", context->SegDs, context->SegSs );
2845 ok( context->SegEs == context->SegSs,
2846 "es %#x does not match ss %#x\n", context->SegEs, context->SegSs );
2847 ok( context->SegGs == context->SegSs,
2848 "gs %#x does not match ss %#x\n", context->SegGs, context->SegSs );
2849 todo_wine ok( context->SegFs && context->SegFs != context->SegSs,
2850 "got fs %#x\n", context->SegFs );
2851 }
2852#endif
2853
2854 if (except->status == STATUS_BREAKPOINT && is_wow64)
2855 parameter_count = 1;
2856 else if (except->alt_status == 0 || rec->ExceptionCode != except->alt_status)
2857 parameter_count = except->nb_params;
2858 else
2859 parameter_count = except->alt_nb_params;
2860
2861 ok( rec->NumberParameters == parameter_count,
2862 "Unexpected parameter count %lu/%u\n", rec->NumberParameters, parameter_count );
2863
2864 /* Most CPUs (except Intel Core apparently) report a segment limit violation */
2865 /* instead of page faults for accesses beyond 0xffffffffffffffff */
2866 if (except->nb_params == 2 && except->params[1] >= 0xfffffffffffffffd)
2867 {
2868 if (rec->ExceptionInformation[0] == 0 && rec->ExceptionInformation[1] == 0xffffffffffffffff)
2869 goto skip_params;
2870 }
2871
2872 /* Seems that both 0xbee8 and 0xfffffffffffffffff can be returned in windows */
2873 if (except->nb_params == 2 && rec->NumberParameters == 2
2874 && except->params[1] == 0xbee8 && rec->ExceptionInformation[1] == 0xffffffffffffffff
2875 && except->params[0] == rec->ExceptionInformation[0])
2876 {
2877 goto skip_params;
2878 }
2879
2880 if (except->alt_status == 0 || rec->ExceptionCode != except->alt_status)
2881 {
2882 for (i = 0; i < rec->NumberParameters; i++)
2883 ok( rec->ExceptionInformation[i] == except->params[i],
2884 "Wrong parameter %d: %Ix/%Ix\n",
2885 i, rec->ExceptionInformation[i], except->params[i] );
2886 }
2887 else
2888 {
2889 for (i = 0; i < rec->NumberParameters; i++)
2890 ok( rec->ExceptionInformation[i] == except->alt_params[i],
2891 "Wrong parameter %d: %Ix/%Ix\n",
2892 i, rec->ExceptionInformation[i], except->alt_params[i] );
2893 }
2894
2895skip_params:
2897
2898 context->Rip = (DWORD_PTR)code_mem + except->offset + except->length;
2900}
2901
2902static const BYTE segfault_code[5] =
2903{
2904 0x31, 0xc0, /* xor %eax,%eax */
2905 0x8f, 0x00, /* popq (%rax) - cause exception */
2906 0xc3 /* ret */
2907};
2908
2909struct dbgreg_test
2910{
2911 ULONG_PTR dr0, dr1, dr2, dr3, dr6, dr7;
2912};
2913/* test handling of debug registers */
2914static DWORD WINAPI dreg_handler( EXCEPTION_RECORD *rec, ULONG64 frame,
2915 CONTEXT *context, DISPATCHER_CONTEXT *dispatcher )
2916{
2917 const struct dbgreg_test *test = *(const struct dbgreg_test **)dispatcher->HandlerData;
2918
2919 context->Rip += 2; /* Skips the popq (%rax) */
2920 context->Dr0 = test->dr0;
2921 context->Dr1 = test->dr1;
2922 context->Dr2 = test->dr2;
2923 context->Dr3 = test->dr3;
2924 context->Dr6 = test->dr6;
2925 context->Dr7 = test->dr7;
2927}
2928
2929#define CHECK_DEBUG_REG(n, m) \
2930 ok((ctx.Dr##n & m) == test->dr##n, "(%d) failed to set debug register " #n " to %p, got %p\n", \
2931 test_num, (void *)test->dr##n, (void *)ctx.Dr##n)
2932
2933static int check_debug_registers(int test_num, const struct dbgreg_test *test)
2934{
2935 CONTEXT ctx;
2937
2938 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
2939 status = pNtGetContextThread(GetCurrentThread(), &ctx);
2940 ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %lx\n", status);
2941
2942 if (!ctx.Dr0 && !ctx.Dr1 && !ctx.Dr2 && !ctx.Dr3 && !ctx.Dr6 && !ctx.Dr7)
2943 {
2944 win_skip( "debug registers broken\n" );
2945 return 0;
2946 }
2947 CHECK_DEBUG_REG(0, ~0);
2948 CHECK_DEBUG_REG(1, ~0);
2949 CHECK_DEBUG_REG(2, ~0);
2950 CHECK_DEBUG_REG(3, ~0);
2951 CHECK_DEBUG_REG(6, 0x0f);
2952 CHECK_DEBUG_REG(7, ~0xdc00);
2953 return 1;
2954}
2955
2956static const BYTE single_stepcode[] =
2957{
2958 0x9c, /* pushf */
2959 0x58, /* pop %rax */
2960 0x0d,0,1,0,0, /* or $0x100,%eax */
2961 0x50, /* push %rax */
2962 0x9d, /* popf */
2963 0x35,0,1,0,0, /* xor $0x100,%eax */
2964 0x50, /* push %rax */
2965 0x9d, /* popf */
2966 0x90,
2967 0xc3
2968};
2969
2970/* test the single step exception behaviour */
2971static DWORD WINAPI single_step_handler( EXCEPTION_RECORD *rec, ULONG64 frame,
2972 CONTEXT *context, DISPATCHER_CONTEXT *dispatcher )
2973{
2974 got_exception++;
2975 ok (!(context->EFlags & 0x100), "eflags has single stepping bit set\n");
2976
2977 if( got_exception < 3)
2978 context->EFlags |= 0x100; /* single step until popf instruction */
2979 else {
2980 /* show that the last single step exception on the popf instruction
2981 * (which removed the TF bit), still is a EXCEPTION_SINGLE_STEP exception */
2983 "exception is not EXCEPTION_SINGLE_STEP: %lx\n", rec->ExceptionCode);
2984 }
2986}
2987
2988/* Test the alignment check (AC) flag handling. */
2989static const BYTE align_check_code[] =
2990{
2991 0x55, /* push %rbp */
2992 0x48,0x89,0xe5, /* mov %rsp,%rbp */
2993 0x9c, /* pushf */
2994 0x9c, /* pushf */
2995 0x58, /* pop %rax */
2996 0x0d,0,0,4,0, /* or $0x40000,%eax */
2997 0x50, /* push %rax */
2998 0x9d, /* popf */
2999 0x48,0x89,0xe0, /* mov %rsp, %rax */
3000 0x8b,0x40,0x1, /* mov 0x1(%rax), %eax - cause exception */
3001 0x9d, /* popf */
3002 0x5d, /* pop %rbp */
3003 0xc3, /* ret */
3004};
3005
3006static DWORD WINAPI align_check_handler( EXCEPTION_RECORD *rec, ULONG64 frame,
3007 CONTEXT *context, DISPATCHER_CONTEXT *dispatcher )
3008{
3009#ifdef __GNUC__
3010 __asm__ volatile( "pushfq; andl $~0x40000,(%rsp); popfq" );
3011#endif
3012 ok (context->EFlags & 0x40000, "eflags has AC bit unset\n");
3013 got_exception++;
3014 if (got_exception != 1)
3015 {
3016 ok(broken(1) /* win7 */, "exception should occur only once");
3017 context->EFlags &= ~0x40000;
3018 }
3020}
3021
3022/* Test the direction flag handling. */
3023static const BYTE direction_flag_code[] =
3024{
3025 0xfd, /* std */
3026 0xfa, /* cli - cause exception */
3027 0xc3, /* ret */
3028};
3029
3030static DWORD WINAPI direction_flag_handler( EXCEPTION_RECORD *rec, ULONG64 frame,
3031 CONTEXT *context, DISPATCHER_CONTEXT *dispatcher )
3032{
3033#ifdef __GNUC__
3035 __asm__("pushfq; popq %0; cld" : "=r" (flags) );
3036 /* older windows versions don't clear DF properly so don't test */
3037 if (flags & 0x400) trace( "eflags has DF bit set\n" );
3038#endif
3039 ok( context->EFlags & 0x400, "context eflags has DF bit cleared\n" );
3040 got_exception++;
3041 context->Rip++; /* skip cli */
3042 context->EFlags &= ~0x400; /* make sure it is cleared on return */
3044}
3045
3046/* test single stepping over hardware breakpoint */
3047static const BYTE dummy_code[] = { 0x90, 0x90, 0x90, 0xc3 }; /* nop, nop, nop, ret */
3048
3049static DWORD WINAPI bpx_handler( EXCEPTION_RECORD *rec, ULONG64 frame,
3050 CONTEXT *context, DISPATCHER_CONTEXT *dispatcher )
3051{
3052 got_exception++;
3054 "wrong exception code: %lx\n", rec->ExceptionCode);
3055
3056 if(got_exception == 1) {
3057 /* hw bp exception on first nop */
3058 ok( (void *)context->Rip == code_mem, "rip is wrong: %p instead of %p\n",
3059 (void *)context->Rip, code_mem );
3060 ok( (context->Dr6 & 0xf) == 1, "B0 flag is not set in Dr6\n");
3061 ok( !(context->Dr6 & 0x4000), "BS flag is set in Dr6\n");
3062 context->Dr0 = context->Dr0 + 1; /* set hw bp again on next instruction */
3063 context->EFlags |= 0x100; /* enable single stepping */
3064 } else if (got_exception == 2) {
3065 /* single step exception on second nop */
3066 ok( (char *)context->Rip == (char *)code_mem + 1, "rip is wrong: %p instead of %p\n",
3067 (void *)context->Rip, (char *)code_mem + 1);
3068 ok( (context->Dr6 & 0x4000), "BS flag is not set in Dr6\n");
3069 context->EFlags |= 0x100;
3070 } else if( got_exception == 3) {
3071 /* hw bp exception on second nop */
3072 ok( (void *)context->Rip == (char *)code_mem + 1, "rip is wrong: %p instead of %p\n",
3073 (void *)context->Rip, (char *)code_mem + 1);
3074 ok( (context->Dr6 & 0xf) == 1, "B0 flag is not set in Dr6\n");
3075 ok( !(context->Dr6 & 0x4000), "BS flag is set in Dr6\n");
3076 context->Dr0 = 0; /* clear breakpoint */
3077 context->EFlags |= 0x100;
3078 } else {
3079 /* single step exception on third nop */
3080 ok( (void *)context->Rip == (char *)code_mem + 2, "rip is wrong: %p instead of %p\n",
3081 (void *)context->Rip, (char *)code_mem + 2);
3082 ok( (context->Dr6 & 0xf) == 0, "B0...3 flags in Dr6 shouldn't be set\n");
3083 ok( (context->Dr6 & 0x4000), "BS flag is not set in Dr6\n");
3084 }
3085
3086 context->Dr6 = 0; /* clear status register */
3088}
3089
3090/* test int3 handling */
3091static const BYTE int3_code[] = { 0xcc, 0xc3 }; /* int 3, ret */
3092
3093static DWORD WINAPI int3_handler( EXCEPTION_RECORD *rec, ULONG64 frame,
3094 CONTEXT *context, DISPATCHER_CONTEXT *dispatcher )
3095{
3096 ok( rec->ExceptionAddress == code_mem, "exception address not at: %p, but at %p\n",
3098 ok( (void *)context->Rip == code_mem, "rip not at: %p, but at %p\n", code_mem, (void *)context->Rip);
3099 if ((void *)context->Rip == code_mem) context->Rip++; /* skip breakpoint */
3100
3102}
3103
3104/* trap unhandled exceptions */
3105static LONG CALLBACK exc_filter( EXCEPTION_POINTERS *ptrs )
3106{
3107 printf( "%04lx unhandled exception %08lx at %p rip %p eflags %lx\n",
3110 (void *)ptrs->ContextRecord->Rip, ptrs->ContextRecord->EFlags );
3111 fflush( stdout );
3113}
3114
3115static void test_exceptions(void)
3116{
3117 CONTEXT ctx;
3118 NTSTATUS res;
3119 struct dbgreg_test dreg_test;
3120
3121 /* test handling of debug registers */
3122 memset(&dreg_test, 0, sizeof(dreg_test));
3123
3124 dreg_test.dr0 = 0x42424240;
3125 dreg_test.dr2 = 0x126bb070;
3126 dreg_test.dr3 = 0x0badbad0;
3127 dreg_test.dr7 = 0xffff0115;
3128 run_exception_test(dreg_handler, &dreg_test, &segfault_code, sizeof(segfault_code), 0);
3129 if (check_debug_registers(1, &dreg_test))
3130 {
3131 dreg_test.dr0 = 0x42424242;
3132 dreg_test.dr2 = 0x100f0fe7;
3133 dreg_test.dr3 = 0x0abebabe;
3134 dreg_test.dr7 = 0x115;
3135 run_exception_test(dreg_handler, &dreg_test, &segfault_code, sizeof(segfault_code), 0);
3136 check_debug_registers(2, &dreg_test);
3137
3138 /* test single stepping over hardware breakpoint */
3139 memset(&ctx, 0, sizeof(ctx));
3140 ctx.Dr0 = (ULONG_PTR)code_mem; /* set hw bp on first nop */
3141 ctx.Dr7 = 1;
3142 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
3143 res = pNtSetContextThread( GetCurrentThread(), &ctx );
3144 ok( res == STATUS_SUCCESS, "NtSetContextThread failed with %lx\n", res);
3145
3146 got_exception = 0;
3147 run_exception_test(bpx_handler, NULL, dummy_code, sizeof(dummy_code), 0);
3148 ok( got_exception == 4,"expected 4 exceptions, got %d\n", got_exception);
3149 }
3150
3151#if defined(__REACTOS__)
3152 if (is_reactos())
3153 {
3154 skip("Skipping tests that crash\n");
3155 return;
3156 }
3157#endif
3158
3159 /* test single stepping behavior */
3160 SetUnhandledExceptionFilter( exc_filter );
3161 got_exception = 0;
3162 run_exception_test(single_step_handler, NULL, &single_stepcode, sizeof(single_stepcode), 0);
3163 ok(got_exception == 3, "expected 3 single step exceptions, got %d\n", got_exception);
3164
3165 /* test alignment exceptions */
3166 got_exception = 0;
3167 run_exception_test(align_check_handler, NULL, align_check_code, sizeof(align_check_code), 0);
3168 todo_wine
3169 ok(got_exception == 1 || broken(got_exception == 2) /* win7 */, "got %d alignment faults, expected 1\n", got_exception);
3170
3171 /* test direction flag */
3172 got_exception = 0;
3173 run_exception_test(direction_flag_handler, NULL, direction_flag_code, sizeof(direction_flag_code), 0);
3174 ok(got_exception == 1, "got %d exceptions, expected 1\n", got_exception);
3175#ifndef __arm64ec__
3176#if defined(__REACTOS__ ) && defined(_MSC_VER)
3177 if (is_arm64ec) __cld();
3178#else
3179 if (is_arm64ec) __asm__ volatile( "cld" ); /* needed on Windows */
3180#endif
3181#endif
3182
3183 /* test int3 handling */
3184 run_exception_test(int3_handler, NULL, int3_code, sizeof(int3_code), 0);
3185
3186#ifndef __arm64ec__
3187 if (!is_arm64ec)
3188 {
3189 USHORT ds, es, fs, gs, ss;
3190 /* test segment registers */
3191 ctx.ContextFlags = CONTEXT_CONTROL | CONTEXT_SEGMENTS;
3192 res = pNtGetContextThread( GetCurrentThread(), &ctx );
3193 ok( res == STATUS_SUCCESS, "NtGetContextThread failed with %lx\n", res );
3194#if defined(__REACTOS__) && defined(_MSC_VER)
3195 ds = __readsegds();
3196 es = __readseges();
3197 fs = __readsegfs();
3198 gs = __readseggs();
3199 ss = __readsegss();
3200#else
3201 __asm__ volatile( "movw %%ds,%0" : "=g" (ds) );
3202 __asm__ volatile( "movw %%es,%0" : "=g" (es) );
3203 __asm__ volatile( "movw %%fs,%0" : "=g" (fs) );
3204 __asm__ volatile( "movw %%gs,%0" : "=g" (gs) );
3205 __asm__ volatile( "movw %%ss,%0" : "=g" (ss) );
3206#endif
3207 ok( ctx.SegDs == ds, "wrong ds %04x / %04x\n", ctx.SegDs, ds );
3208 ok( ctx.SegEs == es, "wrong es %04x / %04x\n", ctx.SegEs, es );
3209 ok( ctx.SegFs == fs, "wrong fs %04x / %04x\n", ctx.SegFs, fs );
3210 ok( ctx.SegGs == gs || !gs, "wrong gs %04x / %04x\n", ctx.SegGs, gs );
3211 ok( ctx.SegSs == ss, "wrong ss %04x / %04x\n", ctx.SegSs, ss );
3212 ok( ctx.SegDs == ctx.SegSs, "wrong ds %04x / %04x\n", ctx.SegDs, ctx.SegSs );
3213 ok( ctx.SegEs == ctx.SegSs, "wrong es %04x / %04x\n", ctx.SegEs, ctx.SegSs );
3214 ok( ctx.SegFs != ctx.SegSs, "wrong fs %04x / %04x\n", ctx.SegFs, ctx.SegSs );
3215 ok( ctx.SegGs == ctx.SegSs, "wrong gs %04x / %04x\n", ctx.SegGs, ctx.SegSs );
3216 ctx.SegDs = 0;
3217 ctx.SegEs = ctx.SegFs;
3218 ctx.SegFs = ctx.SegSs;
3219 res = pNtSetContextThread( GetCurrentThread(), &ctx );
3220 ok( res == STATUS_SUCCESS, "NtGetContextThread failed with %lx\n", res );
3221#if defined(__REACTOS__) && defined(_MSC_VER)
3222 ds = __readsegds();
3223 es = __readseges();
3224 fs = __readsegfs();
3225 gs = __readseggs();
3226 ss = __readsegss();
3227#else
3228 __asm__ volatile( "movw %%ds,%0" : "=g" (ds) );
3229 __asm__ volatile( "movw %%es,%0" : "=g" (es) );
3230 __asm__ volatile( "movw %%fs,%0" : "=g" (fs) );
3231 __asm__ volatile( "movw %%gs,%0" : "=g" (gs) );
3232 __asm__ volatile( "movw %%ss,%0" : "=g" (ss) );
3233#endif
3234 res = pNtGetContextThread( GetCurrentThread(), &ctx );
3235 ok( res == STATUS_SUCCESS, "NtGetContextThread failed with %lx\n", res );
3236 ok( ctx.SegDs == ds, "wrong ds %04x / %04x\n", ctx.SegDs, ds );
3237 ok( ctx.SegEs == es, "wrong es %04x / %04x\n", ctx.SegEs, es );
3238 ok( ctx.SegFs == fs, "wrong fs %04x / %04x\n", ctx.SegFs, fs );
3239 ok( ctx.SegGs == gs || !gs, "wrong gs %04x / %04x\n", ctx.SegGs, gs );
3240 ok( ctx.SegSs == ss, "wrong ss %04x / %04x\n", ctx.SegSs, ss );
3241 ok( ctx.SegDs == ctx.SegSs, "wrong ds %04x / %04x\n", ctx.SegDs, ctx.SegSs );
3242 ok( ctx.SegEs == ctx.SegSs, "wrong es %04x / %04x\n", ctx.SegEs, ctx.SegSs );
3243 ok( ctx.SegFs != ctx.SegSs, "wrong fs %04x / %04x\n", ctx.SegFs, ctx.SegSs );
3244 ok( ctx.SegGs == ctx.SegSs, "wrong gs %04x / %04x\n", ctx.SegGs, ctx.SegSs );
3245 }
3246#endif
3247}
3248
3249static DWORD WINAPI simd_fault_handler( EXCEPTION_RECORD *rec, ULONG64 frame,
3250 CONTEXT *context, DISPATCHER_CONTEXT *dispatcher )
3251{
3252 int *stage = *(int **)dispatcher->HandlerData;
3253
3254 got_exception++;
3255
3256 if (*stage == 1)
3257 {
3258 /* fault while executing sse instruction */
3259 context->Rip += 3; /* skip addps */
3261 }
3262 else if (*stage == 2 || *stage == 3 )
3263 {
3264 /* stage 2 - divide by zero fault */
3265 /* stage 3 - invalid operation fault */
3267 skip("system doesn't support SIMD exceptions\n");
3268 else
3269 {
3271 ok( rec->ExceptionCode == expect, "exception code: %#lx, should be %#lx\n",
3272 rec->ExceptionCode, expect );
3273 ok( rec->NumberParameters == 2, "# of params: %li, should be 2\n", rec->NumberParameters);
3274 ok( rec->ExceptionInformation[0] == 0, "param #0: %Ix\n", rec->ExceptionInformation[0]);
3275 ok( rec->ExceptionInformation[1] == context->MxCsr, "param #1: %Ix / %lx\n",
3276 rec->ExceptionInformation[1], context->MxCsr);
3277 }
3278 context->Rip += 3; /* skip divps */
3279 }
3280 else
3281 ok(FALSE, "unexpected stage %x\n", *stage);
3282
3284}
3285
3286static const BYTE simd_exception_test[] =
3287{
3288 0x48, 0x83, 0xec, 0x8, /* sub $0x8, %rsp */
3289 0x0f, 0xae, 0x1c, 0x24, /* stmxcsr (%rsp) */
3290 0x8b, 0x04, 0x24, /* mov (%rsp),%eax * store mxcsr */
3291 0x66, 0x81, 0x24, 0x24, 0xff, 0xfd, /* andw $0xfdff,(%rsp) * enable divide by */
3292 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%rsp) * zero exceptions */
3293 0xb9, 0x01, 0x00, 0x00, 0x00, /* movl $0x1,%ecx */
3294 0x66, 0x48, 0x0f, 0x6e, 0xc9, /* movq %rcx,%xmm1 * fill dividend */
3295 0x0f, 0x57, 0xc0, /* xorps %xmm0,%xmm0 * clear divisor */
3296 0x0f, 0x5e, 0xc8, /* divps %xmm0,%xmm1 * generate fault */
3297 0x89, 0x04, 0x24, /* mov %eax,(%rsp) * restore to old mxcsr */
3298 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%rsp) */
3299 0x48, 0x83, 0xc4, 0x08, /* add $0x8,%rsp */
3300 0xc3, /* ret */
3301};
3302
3303static const BYTE simd_exception_test2[] =
3304{
3305 0x48, 0x83, 0xec, 0x8, /* sub $0x8, %rsp */
3306 0x0f, 0xae, 0x1c, 0x24, /* stmxcsr (%rsp) */
3307 0x8b, 0x04, 0x24, /* mov (%rsp),%eax * store mxcsr */
3308 0x66, 0x81, 0x24, 0x24, 0x7f, 0xff, /* andw $0xff7f,(%rsp) * enable invalid */
3309 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%rsp) * operation exceptions */
3310 0x0f, 0x57, 0xc9, /* xorps %xmm1,%xmm1 * clear dividend */
3311 0x0f, 0x57, 0xc0, /* xorps %xmm0,%xmm0 * clear divisor */
3312 0x0f, 0x5e, 0xc8, /* divps %xmm0,%xmm1 * generate fault */
3313 0x89, 0x04, 0x24, /* mov %eax,(%rsp) * restore to old mxcsr */
3314 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%rsp) */
3315 0x48, 0x83, 0xc4, 0x08, /* add $0x8,%rsp */
3316 0xc3, /* ret */
3317};
3318
3319static const BYTE sse_check[] =
3320{
3321 0x0f, 0x58, 0xc8, /* addps %xmm0,%xmm1 */
3322 0xc3, /* ret */
3323};
3324
3325static void test_simd_exceptions(void)
3326{
3327 int stage;
3328
3329 /* test if CPU & OS can do sse */
3330 stage = 1;
3331 got_exception = 0;
3332 run_exception_test(simd_fault_handler, &stage, sse_check, sizeof(sse_check), 0);
3333 if(got_exception) {
3334 skip("system doesn't support SSE\n");
3335 return;
3336 }
3337
3338 /* generate a SIMD exception */
3339 stage = 2;
3340 got_exception = 0;
3341 run_exception_test(simd_fault_handler, &stage, simd_exception_test,
3342 sizeof(simd_exception_test), 0);
3343 ok(got_exception == 1, "got exception: %i, should be 1\n", got_exception);
3344
3345 /* generate a SIMD exception, test FPE_FLTINV */
3346 stage = 3;
3347 got_exception = 0;
3348 run_exception_test(simd_fault_handler, &stage, simd_exception_test2,
3349 sizeof(simd_exception_test2), 0);
3350 ok(got_exception == 1, "got exception: %i, should be 1\n", got_exception);
3351}
3352
3353static void test_prot_fault(void)
3354{
3355 unsigned int i;
3356
3357 for (i = 0; i < ARRAY_SIZE(exceptions); i++)
3358 {
3359 got_exception = 0;
3360 run_exception_test(prot_fault_handler, &exceptions[i], &exceptions[i].code,
3361 sizeof(exceptions[i].code), 0);
3362 ok( got_exception == (exceptions[i].status != 0),
3363 "%u: bad exception count %d\n", i, got_exception );
3364 }
3365}
3366
3367static LONG CALLBACK dpe_handler(EXCEPTION_POINTERS *info)
3368{
3369 EXCEPTION_RECORD *rec = info->ExceptionRecord;
3370 DWORD old_prot;
3371
3372 got_exception++;
3373
3375 "got %#lx\n", rec->ExceptionCode);
3376 ok(rec->NumberParameters == 2, "got %lu params\n", rec->NumberParameters);
3378 "got %#Ix\n", rec->ExceptionInformation[0]);
3379 ok((void *)rec->ExceptionInformation[1] == code_mem,
3380 "got %p\n", (void *)rec->ExceptionInformation[1]);
3381
3383
3385}
3386
3387static void test_dpe_exceptions(void)
3388{
3389 static const BYTE ret[] = {0xc3};
3391 DWORD old_prot, val = 0, len = 0xdeadbeef;
3393 void *handler;
3394
3396 ok( status == STATUS_SUCCESS || status == STATUS_INVALID_PARAMETER, "got status %08lx\n", status );
3397 if (!status)
3398 {
3399 ok( len == sizeof(val), "wrong len %lu\n", len );
3402 "wrong val %08lx\n", val );
3403 }
3404 else ok( len == 0xdeadbeef, "wrong len %lu\n", len );
3405
3407 status = pNtSetInformationProcess( GetCurrentProcess(), ProcessExecuteFlags, &val, sizeof val );
3408 ok( status == STATUS_INVALID_PARAMETER, "got status %08lx\n", status );
3409
3410 memcpy(code_mem, ret, sizeof(ret));
3411
3412 handler = pRtlAddVectoredExceptionHandler(TRUE, &dpe_handler);
3413 ok(!!handler, "RtlAddVectoredExceptionHandler failed\n");
3414
3415 VirtualProtect(code_mem, 1, PAGE_NOACCESS, &old_prot);
3416
3417 got_exception = 0;
3418 func();
3419 ok(got_exception == 1, "got %u exceptions\n", got_exception);
3420
3421 VirtualProtect(code_mem, 1, old_prot, &old_prot);
3422
3423 VirtualProtect(code_mem, 1, PAGE_READWRITE, &old_prot);
3424
3425 got_exception = 0;
3426 func();
3427 ok(got_exception == 1, "got %u exceptions\n", got_exception);
3428
3429 VirtualProtect(code_mem, 1, old_prot, &old_prot);
3430
3431 pRtlRemoveVectoredExceptionHandler(handler);
3432}
3433
3434static const BYTE call_one_arg_code[] = {
3435 0x48, 0x83, 0xec, 0x28, /* sub $0x28,%rsp */
3436 0x48, 0x89, 0xc8, /* mov %rcx,%rax */
3437 0x48, 0x89, 0xd1, /* mov %rdx,%rcx */
3438 0xff, 0xd0, /* callq *%rax */
3439 0x90, /* nop */
3440 0x90, /* nop */
3441 0x90, /* nop */
3442 0x90, /* nop */
3443 0x48, 0x83, 0xc4, 0x28, /* add $0x28,%rsp */
3444 0xc3, /* retq */
3445};
3446
3447static int rtlraiseexception_unhandled_handler_called;
3448static int rtlraiseexception_teb_handler_called;
3449static int rtlraiseexception_handler_called;
3450
3451static void rtlraiseexception_handler_( EXCEPTION_RECORD *rec, void *frame, CONTEXT *context,
3452 void *dispatcher, BOOL unhandled_handler )
3453{
3454 void *addr = rec->ExceptionAddress;
3455
3456 trace( "exception: %08lx flags:%lx addr:%p context: Rip:%p\n",
3457 rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, (void *)context->Rip );
3458
3459 if (is_arm64ec) /* addr points to RtlRaiseException entry thunk */
3460 {
3461 ok( ((ULONG *)addr)[-1] == 0xd63f0120 /* blr x9 */,
3462 "ExceptionAddress not in entry thunk %p (ntdll+%Ix)\n",
3463 addr, (char *)addr - (char *)hntdll );
3464 ok( context->ContextFlags == (CONTEXT_FULL | CONTEXT_UNWOUND_TO_CALL),
3465 "wrong context flags %lx\n", context->ContextFlags );
3466 }
3467 else
3468 {
3469 ok( addr == (char *)code_mem + 0x0c || broken( addr == code_mem || !addr ) /* 2008 */,
3470 "ExceptionAddress at %p instead of %p\n", addr, (char *)code_mem + 0x0c );
3471 ok( context->ContextFlags == CONTEXT_ALL || context->ContextFlags == (CONTEXT_ALL | CONTEXT_XSTATE)
3472 || context->ContextFlags == (CONTEXT_FULL | CONTEXT_SEGMENTS)
3473 || context->ContextFlags == (CONTEXT_FULL | CONTEXT_SEGMENTS | CONTEXT_XSTATE),
3474 "wrong context flags %lx\n", context->ContextFlags );
3475 }
3476
3477 /* check that pc is fixed up only for EXCEPTION_BREAKPOINT
3478 * even if raised by RtlRaiseException
3479 */
3481 ok( context->Rip == (UINT_PTR)addr - 1,
3482 "%d: Rip at %Ix instead of %Ix\n", test_stage, context->Rip, (UINT_PTR)addr - 1 );
3483 else
3484 ok( context->Rip == (UINT_PTR)addr,
3485 "%d: Rip at %Ix instead of %Ix\n", test_stage, context->Rip, (UINT_PTR)addr );
3486
3487 if (have_vectored_api) ok( context->Rax == 0xf00f00f0, "context->Rax is %Ix, should have been set to 0xf00f00f0 in vectored handler\n", context->Rax );
3488}
3489
3490static LONG CALLBACK rtlraiseexception_unhandled_handler(EXCEPTION_POINTERS *ExceptionInfo)
3491{
3492 PCONTEXT context = ExceptionInfo->ContextRecord;
3493 PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
3494 rtlraiseexception_unhandled_handler_called = 1;
3495 rtlraiseexception_handler_(rec, NULL, context, NULL, TRUE);
3497
3498 /* pc in context is decreased by 1
3499 * Increase it again, else execution will continue in the middle of an instruction */
3500 if (rec->ExceptionCode == EXCEPTION_BREAKPOINT && (context->Rip == (UINT_PTR)rec->ExceptionAddress - 1))
3501 context->Rip++;
3503}
3504
3505static DWORD WINAPI rtlraiseexception_teb_handler( EXCEPTION_RECORD *rec,
3508 EXCEPTION_REGISTRATION_RECORD **dispatcher )
3509{
3510 rtlraiseexception_teb_handler_called = 1;
3511 rtlraiseexception_handler_(rec, frame, context, dispatcher, FALSE);
3513}
3514
3515static DWORD WINAPI rtlraiseexception_handler( EXCEPTION_RECORD *rec, void *frame,
3516 CONTEXT *context, DISPATCHER_CONTEXT *dispatcher )
3517{
3518 if (is_arm64ec)
3519 {
3522 int i;
3523
3524 nonvol_regs = (void *)((DISPATCHER_CONTEXT_ARM64 *)dispatcher)->NonVolatileRegisters;
3525 ok( nonvol_regs->GpNvRegs[0] == ec_ctx->X19,
3526 "wrong non volatile reg x19 %I64x / %I64x\n", nonvol_regs->GpNvRegs[0], ec_ctx->X19 );
3527 ok( nonvol_regs->GpNvRegs[1] == ec_ctx->X20,
3528 "wrong non volatile reg x20 %I64x / %I64x\n", nonvol_regs->GpNvRegs[1], ec_ctx->X20 );
3529 ok( nonvol_regs->GpNvRegs[2] == ec_ctx->X21,
3530 "wrong non volatile reg x21 %I64x / %I64x\n", nonvol_regs->GpNvRegs[2], ec_ctx->X21 );
3531 ok( nonvol_regs->GpNvRegs[3] == ec_ctx->X22,
3532 "wrong non volatile reg x22 %I64x / %I64x\n", nonvol_regs->GpNvRegs[3], ec_ctx->X22 );
3533 ok( nonvol_regs->GpNvRegs[4] == 0, "wrong non volatile reg x23 %I64x\n", nonvol_regs->GpNvRegs[4] );
3534 ok( nonvol_regs->GpNvRegs[5] == 0, "wrong non volatile reg x24 %I64x\n", nonvol_regs->GpNvRegs[5] );
3535 ok( nonvol_regs->GpNvRegs[6] == ec_ctx->X25,
3536 "wrong non volatile reg x25 %I64x / %I64x\n", nonvol_regs->GpNvRegs[6], ec_ctx->X25 );
3537 ok( nonvol_regs->GpNvRegs[7] == ec_ctx->X26,
3538 "wrong non volatile reg x26 %I64x / %I64x\n", nonvol_regs->GpNvRegs[7], ec_ctx->X26 );
3539 ok( nonvol_regs->GpNvRegs[8] == ec_ctx->X27,
3540 "wrong non volatile reg x27 %I64x / %I64x\n", nonvol_regs->GpNvRegs[8], ec_ctx->X27 );
3541 ok( nonvol_regs->GpNvRegs[9] == 0, "wrong non volatile reg x28 %I64x\n", nonvol_regs->GpNvRegs[9] );
3542 ok( nonvol_regs->GpNvRegs[10] > ec_ctx->Fp, /* previous frame */
3543 "wrong non volatile reg x29 %I64x / %I64x\n", nonvol_regs->GpNvRegs[10], ec_ctx->Fp );
3544
3545 for (i = 0; i < NONVOL_FP_NUMREG_ARM64; i++)
3546 ok( nonvol_regs->FpNvRegs[i] == ec_ctx->V[i + 8].D[0],
3547 "wrong non volatile reg d%u %g / %g\n", i + 8,
3548 nonvol_regs->FpNvRegs[i] , ec_ctx->V[i + 8].D[0] );
3549 }
3550 rtlraiseexception_handler_called = 1;
3551 rtlraiseexception_handler_(rec, frame, context, dispatcher, FALSE);
3553}
3554
3555static LONG CALLBACK rtlraiseexception_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
3556{
3557 PCONTEXT context = ExceptionInfo->ContextRecord;
3558 PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
3559 void *addr = rec->ExceptionAddress;
3560
3561 if (is_arm64ec) /* addr points to RtlRaiseException entry thunk */
3562 ok( ((ULONG *)addr)[-1] == 0xd63f0120 /* blr x9 */,
3563 "ExceptionAddress not in entry thunk %p (ntdll+%Ix)\n",
3564 addr, (char *)addr - (char *)hntdll );
3565 else
3566 ok( addr == (char *)code_mem + 0xc || broken(addr == code_mem || !addr ) /* 2008 */,
3567 "ExceptionAddress at %p instead of %p\n", addr, (char *)code_mem + 0xc );
3568
3569 /* check that Rip is fixed up only for EXCEPTION_BREAKPOINT
3570 * even if raised by RtlRaiseException
3571 */
3573 ok( context->Rip == (UINT_PTR)addr - 1,
3574 "%d: Rip at %Ix instead of %Ix\n", test_stage, context->Rip, (UINT_PTR)addr - 1 );
3575 else
3576 ok( context->Rip == (UINT_PTR)addr,
3577 "%d: Rip at %Ix instead of %Ix\n", test_stage, context->Rip, (UINT_PTR)addr );
3578
3579 /* test if context change is preserved from vectored handler to stack handlers */
3580 context->Rax = 0xf00f00f0;
3581
3583}
3584
3585static void run_rtlraiseexception_test(DWORD exceptioncode)
3586{
3587 unsigned char buf[4 + 4 + 4 + 8 + 2 + 8 + 2];
3588 RUNTIME_FUNCTION runtime_func;
3589 UNWIND_INFO *unwind = (UNWIND_INFO *)buf;
3592 PVOID vectored_handler = NULL;
3593
3594 void (CDECL *func)(void* function, EXCEPTION_RECORD* record) = code_mem;
3595
3596 record.ExceptionCode = exceptioncode;
3597 record.ExceptionFlags = 0;
3598 record.ExceptionRecord = NULL;
3599 record.ExceptionAddress = NULL; /* does not matter, copied return address */
3600 record.NumberParameters = 0;
3601
3602 runtime_func.BeginAddress = 0;
3603 runtime_func.EndAddress = sizeof(call_one_arg_code);
3604 runtime_func.UnwindData = 0x1000;
3605
3606 unwind->Version = 1;
3607 unwind->Flags = UNW_FLAG_EHANDLER;
3608 unwind->SizeOfProlog = 4;
3609 unwind->CountOfCodes = 1;
3610 unwind->FrameRegister = 0;
3611 unwind->FrameOffset = 0;
3612 *(WORD *)&buf[4] = 0x4204; /* sub $0x28,%rsp */
3613 *(ULONG *)&buf[8] = 0x1014;
3614 *(const void **)&buf[12] = NULL;
3615 /* movabs $<handler>, %rax */
3616 buf[20] = 0x48;
3617 buf[21] = 0xb8;
3618 *(void **)&buf[22] = rtlraiseexception_handler;
3619 /* jmp *%rax */
3620 buf[30] = 0xff;
3621 buf[31] = 0xe0;
3622
3623 memcpy((unsigned char *)code_mem + 0x1000, buf, sizeof(buf));
3624 pRtlAddFunctionTable( &runtime_func, 1, (ULONG_PTR)code_mem );
3625
3626 frame.Handler = rtlraiseexception_teb_handler;
3627 frame.Prev = NtCurrentTeb()->Tib.ExceptionList;
3628
3629 memcpy(code_mem, call_one_arg_code, sizeof(call_one_arg_code));
3630
3631 NtCurrentTeb()->Tib.ExceptionList = &frame;
3633 {
3634 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, rtlraiseexception_vectored_handler);
3635 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
3636 }
3637 if (pRtlSetUnhandledExceptionFilter) pRtlSetUnhandledExceptionFilter(rtlraiseexception_unhandled_handler);
3638
3639 rtlraiseexception_handler_called = 0;
3640 rtlraiseexception_teb_handler_called = 0;
3641 rtlraiseexception_unhandled_handler_called = 0;
3642 func(pRtlRaiseException, &record);
3643 if (is_arm64ec) /* addr points to RtlRaiseException entry thunk */
3644 ok( ((ULONG *)record.ExceptionAddress)[-1] == 0xd63f0120 /* blr x9 */,
3645 "ExceptionAddress not in entry thunk %p (ntdll+%Ix)\n",
3646 record.ExceptionAddress, (char *)record.ExceptionAddress - (char *)hntdll );
3647 else
3648 ok( record.ExceptionAddress == (char *)code_mem + 0x0c,
3649 "address set to %p instead of %p\n", record.ExceptionAddress, (char *)code_mem + 0x0c );
3650
3651 todo_wine
3652 ok( !rtlraiseexception_teb_handler_called, "Frame TEB handler called\n" );
3653 ok( rtlraiseexception_handler_called, "Frame handler called\n" );
3654 ok( rtlraiseexception_unhandled_handler_called, "UnhandledExceptionFilter wasn't called\n" );
3655
3657 pRtlRemoveVectoredExceptionHandler(vectored_handler);
3658
3659 if (pRtlSetUnhandledExceptionFilter) pRtlSetUnhandledExceptionFilter(NULL);
3660 pRtlDeleteFunctionTable( &runtime_func );
3661 NtCurrentTeb()->Tib.ExceptionList = frame.Prev;
3662}
3663
3664static void test_rtlraiseexception(void)
3665{
3666#if defined(__REACTOS__)
3667 if (is_reactos())
3668 {
3669 skip("Skipping tests that crash\n");
3670 return;
3671 }
3672#endif
3673 if (!pRtlRaiseException)
3674 {
3675 skip("RtlRaiseException not found\n");
3676 return;
3677 }
3678
3679 /* test without debugger */
3680 run_rtlraiseexception_test(0x12345);
3681 run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
3682 run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
3683}
3684
3685static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx)
3686{
3687 char cmdline[MAX_PATH];
3689 STARTUPINFOA si = { 0 };
3690 DEBUG_EVENT de;
3691 DWORD continuestatus;
3692 PVOID code_mem_address = NULL;
3694 SIZE_T size_read;
3695 BOOL ret;
3696 int counter = 0;
3697 si.cb = sizeof(si);
3698
3699 if(!pNtGetContextThread || !pNtSetContextThread || !pNtReadVirtualMemory || !pNtTerminateProcess)
3700 {
3701 skip("NtGetContextThread, NtSetContextThread, NtReadVirtualMemory or NtTerminateProcess not found\n");
3702 return;
3703 }
3704
3705 if (with_WaitForDebugEventEx && !pWaitForDebugEventEx)
3706 {
3707 skip("WaitForDebugEventEx not found, skipping unicode strings in OutputDebugStringW\n");
3708 return;
3709 }
3710
3711 sprintf(cmdline, "%s %s %s %p", my_argv[0], my_argv[1], "debuggee", &test_stage);
3713 ok(ret, "could not create child process error: %lu\n", GetLastError());
3714 if (!ret)
3715 return;
3716
3717 do
3718 {
3719 continuestatus = cont_status;
3720 ret = with_WaitForDebugEventEx ? pWaitForDebugEventEx(&de, INFINITE) : WaitForDebugEvent(&de, INFINITE);
3721 ok(ret, "reading debug event\n");
3722
3723 ret = ContinueDebugEvent(de.dwProcessId, de.dwThreadId, 0xdeadbeef);
3724 ok(!ret, "ContinueDebugEvent unexpectedly succeeded\n");
3725 ok(GetLastError() == ERROR_INVALID_PARAMETER, "Unexpected last error: %lu\n", GetLastError());
3726
3727 if (de.dwThreadId != pi.dwThreadId)
3728 {
3729 trace("event %ld not coming from main thread, ignoring\n", de.dwDebugEventCode);
3730 ContinueDebugEvent(de.dwProcessId, de.dwThreadId, cont_status);
3731 continue;
3732 }
3733
3735 {
3737 {
3738 skip("child process loaded at different address, terminating it\n");
3739 pNtTerminateProcess(pi.hProcess, 0);
3740 }
3741 }
3743 {
3744 CONTEXT ctx;
3745 enum debugger_stages stage;
3746
3747 counter++;
3748 status = pNtReadVirtualMemory(pi.hProcess, &code_mem, &code_mem_address,
3749 sizeof(code_mem_address), &size_read);
3750 ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status);
3751 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
3752 sizeof(stage), &size_read);
3753 ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status);
3754
3756 status = pNtGetContextThread(pi.hThread, &ctx);
3757 ok(!status, "NtGetContextThread failed with 0x%lx\n", status);
3758 ok(ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING, "got %#lx.\n", ctx.ContextFlags);
3759
3760 trace("exception 0x%lx at %p firstchance=%ld Rip=%p, Rax=%p\n",
3763 de.u.Exception.dwFirstChance, (char *)ctx.Rip, (char *)ctx.Rax);
3764
3765 if (counter > 100)
3766 {
3767 ok(FALSE, "got way too many exceptions, probably caught in an infinite loop, terminating child\n");
3768 pNtTerminateProcess(pi.hProcess, 1);
3769 }
3770 else if (counter < 2) /* startup breakpoint */
3771 {
3772 /* breakpoint is inside ntdll */
3774
3775 ok( (char *)ctx.Rip >= (char *)hntdll &&
3776 (char *)ctx.Rip < (char *)hntdll + nt->OptionalHeader.SizeOfImage,
3777 "wrong rip %p ntdll %p-%p\n", (void *)ctx.Rip, hntdll,
3778 (char *)hntdll + nt->OptionalHeader.SizeOfImage );
3779 check_context_exception_request( ctx.ContextFlags, TRUE );
3780 }
3781 else
3782 {
3783 if (stage == STAGE_RTLRAISE_NOT_HANDLED)
3784 {
3785 if (is_arm64ec) /* addr points to RtlRaiseException entry thunk */
3786 ok( ((ULONG *)ctx.Rip)[-1] == 0xd63f0120 /* blr x9 */,
3787 "Rip not in entry thunk %p (ntdll+%Ix)\n",
3788 (char *)ctx.Rip, (char *)ctx.Rip - (char *)hntdll );
3789 else
3790 ok((char *)ctx.Rip == (char *)code_mem_address + 0x0c, "Rip at %p instead of %p\n",
3791 (char *)ctx.Rip, (char *)code_mem_address + 0x0c);
3792 /* setting the context from debugger does not affect the context that the
3793 * exception handler gets, except on w2008 */
3794 ctx.Rip = (UINT_PTR)code_mem_address + 0x0e;
3795 ctx.Rax = 0xf00f00f1;
3796 /* let the debuggee handle the exception */
3797 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
3798 check_context_exception_request( ctx.ContextFlags, FALSE );
3799 }
3800 else if (stage == STAGE_RTLRAISE_HANDLE_LAST_CHANCE)
3801 {
3802 if (de.u.Exception.dwFirstChance)
3803 {
3804 if (is_arm64ec)
3805 ok( ((ULONG *)ctx.Rip)[-1] == 0xd63f0120 /* blr x9 */,
3806 "Rip not in entry thunk %p (ntdll+%Ix)\n",
3807 (char *)ctx.Rip, (char *)ctx.Rip - (char *)hntdll );
3808 else
3809 ok((char *)ctx.Rip == (char *)code_mem_address + 0x0c,
3810 "Rip at %p instead of %p\n",
3811 (char *)ctx.Rip, (char *)code_mem_address + 0x0c);
3812 /* setting the context from debugger does not affect the context that the
3813 * exception handler gets, except on w2008 */
3814 ctx.Rip = (UINT_PTR)code_mem_address + 0x0e;
3815 ctx.Rax = 0xf00f00f1;
3816 /* pass exception to debuggee
3817 * exception will not be handled and a second chance exception will be raised */
3818 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
3819 }
3820 else
3821 {
3822 /* debugger gets context after exception handler has played with it */
3823 if (is_arm64ec)
3824 ok( ((ULONG *)ctx.Rip)[-1] == 0xd63f0120 /* blr x9 */,
3825 "Rip not in entry thunk %p (ntdll+%Ix)\n",
3826 (char *)ctx.Rip, (char *)ctx.Rip - (char *)hntdll );
3828 {
3829 ok((char *)ctx.Rip == (char *)code_mem_address + 0xb, "Rip at %p instead of %p\n",
3830 (char *)ctx.Rip, (char *)code_mem_address + 0xb);
3831 ctx.Rip += 1;
3832 }
3833 else ok((char *)ctx.Rip == (char *)code_mem_address + 0x0c, "Rip at 0x%I64x instead of %p\n",
3834 ctx.Rip, (char *)code_mem_address + 0x0c);
3835 /* here we handle exception */
3836 }
3837 check_context_exception_request( ctx.ContextFlags, FALSE );
3838 }
3839 else if (stage == STAGE_SERVICE_CONTINUE || stage == STAGE_SERVICE_NOT_HANDLED)
3840 {
3842 "expected EXCEPTION_BREAKPOINT, got %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode);
3843 ok((char *)ctx.Rip == (char *)code_mem_address + 0x30,
3844 "expected Rip = %p, got %p\n", (char *)code_mem_address + 0x30, (char *)ctx.Rip);
3845 if (stage == STAGE_SERVICE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
3846 check_context_exception_request( ctx.ContextFlags, TRUE );
3847 }
3848 else if (stage == STAGE_BREAKPOINT_CONTINUE || stage == STAGE_BREAKPOINT_NOT_HANDLED)
3849 {
3851 "expected EXCEPTION_BREAKPOINT, got %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode);
3852 ok((char *)ctx.Rip == (char *)code_mem_address + 2,
3853 "expected Rip = %p, got %p\n", (char *)code_mem_address + 2, (char *)ctx.Rip);
3854 if (stage == STAGE_BREAKPOINT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
3855 check_context_exception_request( ctx.ContextFlags, TRUE );
3856 }
3858 {
3860 "unexpected exception code %08lx, expected %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode,
3863 "unexpected number of parameters %ld, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters);
3864
3866 check_context_exception_request( ctx.ContextFlags, FALSE );
3867 }
3869 {
3870 ok(FALSE || broken(TRUE) /* < Win10 */, "should not throw exception\n");
3871 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
3872 check_context_exception_request( ctx.ContextFlags, FALSE );
3873 }
3874 else if (stage == STAGE_XSTATE || stage == STAGE_XSTATE_LEGACY_SSE)
3875 {
3876 check_context_exception_request( ctx.ContextFlags, TRUE );
3877 test_debugger_xstate(pi.hThread, &ctx, stage);
3878 }
3879 else if (stage == STAGE_SEGMENTS)
3880 {
3881#ifdef __arm64ec__
3882 USHORT ss = 0x2b;
3883#elif defined(__REACTOS__) && defined(_MSC_VER)
3884 USHORT ss = __readsegss();
3885#else
3886 USHORT ss;
3887 __asm__( "movw %%ss,%0" : "=r" (ss) );
3888#endif
3889 ok( ctx.SegSs == ss, "wrong ss %04x / %04x\n", ctx.SegSs, ss );
3890 ok( ctx.SegDs == ctx.SegSs, "wrong ds %04x / %04x\n", ctx.SegDs, ctx.SegSs );
3891 ok( ctx.SegEs == ctx.SegSs, "wrong es %04x / %04x\n", ctx.SegEs, ctx.SegSs );
3892 ok( ctx.SegFs != ctx.SegSs, "wrong fs %04x / %04x\n", ctx.SegFs, ctx.SegSs );
3893 ok( ctx.SegGs == ctx.SegSs, "wrong gs %04x / %04x\n", ctx.SegGs, ctx.SegSs );
3894 ctx.SegSs = 0;
3895 ctx.SegDs = 0;
3896 ctx.SegEs = ctx.SegFs;
3897 ctx.SegGs = 0;
3898 status = pNtSetContextThread( pi.hThread, &ctx );
3899 ok( status == STATUS_SUCCESS, "NtSetContextThread failed with %lx\n", status );
3900 status = pNtGetContextThread( pi.hThread, &ctx );
3901 ok( status == STATUS_SUCCESS, "NtGetContextThread failed with %lx\n", status );
3902 todo_wine ok( ctx.SegSs == ss, "wrong ss %04x / %04x\n", ctx.SegSs, ss );
3903 ok( ctx.SegDs == ctx.SegSs, "wrong ds %04x / %04x\n", ctx.SegDs, ctx.SegSs );
3904 ok( ctx.SegEs == ctx.SegSs, "wrong es %04x / %04x\n", ctx.SegEs, ctx.SegSs );
3905 todo_wine ok( ctx.SegFs != ctx.SegSs, "wrong fs %04x / %04x\n", ctx.SegFs, ctx.SegSs );
3906 ok( ctx.SegGs == ctx.SegSs, "wrong gs %04x / %04x\n", ctx.SegGs, ctx.SegSs );
3907 check_context_exception_request( ctx.ContextFlags, TRUE );
3908 }
3909 else
3910 ok(FALSE, "unexpected stage %u\n", stage);
3911
3912 status = pNtSetContextThread(pi.hThread, &ctx);
3913 ok(!status, "NtSetContextThread failed with 0x%lx\n", status);
3914 }
3915 }
3917 {
3918 enum debugger_stages stage;
3919 char buffer[64 * sizeof(WCHAR)];
3920 unsigned char_size = de.u.DebugString.fUnicode ? sizeof(WCHAR) : sizeof(char);
3921
3922 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
3923 sizeof(stage), &size_read);
3924 ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status);
3925
3926 if (de.u.DebugString.fUnicode)
3927 ok(with_WaitForDebugEventEx &&
3929 "unexpected unicode debug string event\n");
3930 else
3931 ok(!with_WaitForDebugEventEx || stage != STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || cont_status != DBG_CONTINUE,
3932 "unexpected ansi debug string event %u %s %lx\n",
3933 stage, with_WaitForDebugEventEx ? "with" : "without", cont_status);
3934
3935 ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) / char_size - 1,
3936 "buffer not large enough to hold %d bytes\n", de.u.DebugString.nDebugStringLength);
3937
3938 memset(buffer, 0, sizeof(buffer));
3939 status = pNtReadVirtualMemory(pi.hProcess, de.u.DebugString.lpDebugStringData, buffer,
3940 de.u.DebugString.nDebugStringLength * char_size, &size_read);
3941 ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status);
3942
3945 {
3946 if (de.u.DebugString.fUnicode)
3947 ok(!wcscmp((WCHAR*)buffer, L"Hello World"), "got unexpected debug string '%ls'\n", (WCHAR*)buffer);
3948 else
3949 ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer);
3950 }
3951 else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */
3952 ok(strstr(buffer, "SHIMVIEW") != NULL, "unexpected stage %x, got debug string event '%s'\n", stage, buffer);
3953
3955 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
3956 }
3957 else if (de.dwDebugEventCode == RIP_EVENT)
3958 {
3959 enum debugger_stages stage;
3960
3961 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
3962 sizeof(stage), &size_read);
3963 ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status);
3964
3965 if (stage == STAGE_RIPEVENT_CONTINUE || stage == STAGE_RIPEVENT_NOT_HANDLED)
3966 {
3967 ok(de.u.RipInfo.dwError == 0x11223344, "got unexpected rip error code %08lx, expected %08x\n",
3968 de.u.RipInfo.dwError, 0x11223344);
3969 ok(de.u.RipInfo.dwType == 0x55667788, "got unexpected rip type %08lx, expected %08x\n",
3970 de.u.RipInfo.dwType, 0x55667788);
3971 }
3972 else
3973 ok(FALSE, "unexpected stage %x\n", stage);
3974
3975 if (stage == STAGE_RIPEVENT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
3976 }
3977
3978 ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus);
3979
3981
3984 ok(ret, "error %lu\n", GetLastError());
3986 ok(ret, "error %lu\n", GetLastError());
3987}
3988
3989static void test_thread_context(void)
3990{
3993 int i;
3994 struct expected
3995 {
3996 ULONG64 Rax, Rbx, Rcx, Rdx, Rsi, Rdi, R8, R9, R10, R11,
3997 R12, R13, R14, R15, Rbp, Rsp, Rip, prev_frame, EFlags;
3998 ULONG MxCsr;
3999 XMM_SAVE_AREA32 FltSave;
4000 WORD SegCs, SegDs, SegEs, SegFs, SegGs, SegSs;
4001 } expect;
4002 NTSTATUS (*func_ptr)( void *arg1, void *arg2, struct expected *res, void *func ) = code_mem;
4003
4004 static const BYTE call_func[] =
4005 {
4006 0x55, /* push %rbp */
4007 0x48, 0x89, 0xe5, /* mov %rsp,%rbp */
4008 0x48, 0x8d, 0x64, 0x24, 0xd0, /* lea -0x30(%rsp),%rsp */
4009 0x49, 0x89, 0x00, /* mov %rax,(%r8) */
4010 0x49, 0x89, 0x58, 0x08, /* mov %rbx,0x8(%r8) */
4011 0x49, 0x89, 0x48, 0x10, /* mov %rcx,0x10(%r8) */
4012 0x49, 0x89, 0x50, 0x18, /* mov %rdx,0x18(%r8) */
4013 0x49, 0x89, 0x70, 0x20, /* mov %rsi,0x20(%r8) */
4014 0x49, 0x89, 0x78, 0x28, /* mov %rdi,0x28(%r8) */
4015 0x4d, 0x89, 0x40, 0x30, /* mov %r8,0x30(%r8) */
4016 0x4d, 0x89, 0x48, 0x38, /* mov %r9,0x38(%r8) */
4017 0x4d, 0x89, 0x50, 0x40, /* mov %r10,0x40(%r8) */
4018 0x4d, 0x89, 0x58, 0x48, /* mov %r11,0x48(%r8) */
4019 0x4d, 0x89, 0x60, 0x50, /* mov %r12,0x50(%r8) */
4020 0x4d, 0x89, 0x68, 0x58, /* mov %r13,0x58(%r8) */
4021 0x4d, 0x89, 0x70, 0x60, /* mov %r14,0x60(%r8) */
4022 0x4d, 0x89, 0x78, 0x68, /* mov %r15,0x68(%r8) */
4023 0x49, 0x89, 0x68, 0x70, /* mov %rbp,0x70(%r8) */
4024 0x49, 0x89, 0x60, 0x78, /* mov %rsp,0x78(%r8) */
4025 0xff, 0x75, 0x08, /* pushq 0x8(%rbp) */
4026 0x41, 0x8f, 0x80, 0x80, 0x00, 0x00, 0x00, /* popq 0x80(%r8) */
4027 0xff, 0x75, 0x00, /* pushq 0x0(%rbp) */
4028 0x41, 0x8f, 0x80, 0x88, 0x00, 0x00, 0x00, /* popq 0x88(%r8) */
4029 0x9c, /* pushfq */
4030 0x41, 0x8f, 0x80, 0x90, 0x00, 0x00, 0x00, /* popq 0x90(%r8) */
4031 0x41, 0x0f, 0xae, 0x98, 0x98, 0x00, 0x00, 0x00, /* stmxcsr 0x98(%r8) */
4032 0x41, 0x0f, 0xae, 0x80, 0xa0, 0x00, 0x00, 0x00, /* fxsave 0xa0(%r8) */
4033 0x66, 0x41, 0x0f, 0x7f, 0x80, 0x40, 0x01, 0x00, 0x00, /* movdqa %xmm0,0x140(%r8) */
4034 0x66, 0x41, 0x0f, 0x7f, 0x88, 0x50, 0x01, 0x00, 0x00, /* movdqa %xmm1,0x150(%r8) */
4035 0x66, 0x41, 0x0f, 0x7f, 0x90, 0x60, 0x01, 0x00, 0x00, /* movdqa %xmm2,0x160(%r8) */
4036 0x66, 0x41, 0x0f, 0x7f, 0x98, 0x70, 0x01, 0x00, 0x00, /* movdqa %xmm3,0x170(%r8) */
4037 0x66, 0x41, 0x0f, 0x7f, 0xa0, 0x80, 0x01, 0x00, 0x00, /* movdqa %xmm4,0x180(%r8) */
4038 0x66, 0x41, 0x0f, 0x7f, 0xa8, 0x90, 0x01, 0x00, 0x00, /* movdqa %xmm5,0x190(%r8) */
4039 0x66, 0x41, 0x0f, 0x7f, 0xb0, 0xa0, 0x01, 0x00, 0x00, /* movdqa %xmm6,0x1a0(%r8) */
4040 0x66, 0x41, 0x0f, 0x7f, 0xb8, 0xb0, 0x01, 0x00, 0x00, /* movdqa %xmm7,0x1b0(%r8) */
4041 0x66, 0x45, 0x0f, 0x7f, 0x80, 0xc0, 0x01, 0x00, 0x00, /* movdqa %xmm8,0x1c0(%r8) */
4042 0x66, 0x45, 0x0f, 0x7f, 0x88, 0xd0, 0x01, 0x00, 0x00, /* movdqa %xmm9,0x1d0(%r8) */
4043 0x66, 0x45, 0x0f, 0x7f, 0x90, 0xe0, 0x01, 0x00, 0x00, /* movdqa %xmm10,0x1e0(%r8) */
4044 0x66, 0x45, 0x0f, 0x7f, 0x98, 0xf0, 0x01, 0x00, 0x00, /* movdqa %xmm11,0x1f0(%r8) */
4045 0x66, 0x45, 0x0f, 0x7f, 0xa0, 0x00, 0x02, 0x00, 0x00, /* movdqa %xmm12,0x200(%r8) */
4046 0x66, 0x45, 0x0f, 0x7f, 0xa8, 0x10, 0x02, 0x00, 0x00, /* movdqa %xmm13,0x210(%r8) */
4047 0x66, 0x45, 0x0f, 0x7f, 0xb0, 0x20, 0x02, 0x00, 0x00, /* movdqa %xmm14,0x220(%r8) */
4048 0x66, 0x45, 0x0f, 0x7f, 0xb8, 0x30, 0x02, 0x00, 0x00, /* movdqa %xmm15,0x230(%r8) */
4049 0x41, 0x8c, 0x88, 0xa0, 0x02, 0x00, 0x00, /* mov %cs,0x2a0(%r8) */
4050 0x41, 0x8c, 0x98, 0xa2, 0x02, 0x00, 0x00, /* mov %ds,0x2a2(%r8) */
4051 0x41, 0x8c, 0x80, 0xa4, 0x02, 0x00, 0x00, /* mov %es,0x2a4(%r8) */
4052 0x41, 0x8c, 0xa0, 0xa6, 0x02, 0x00, 0x00, /* mov %fs,0x2a6(%r8) */
4053 0x41, 0x8c, 0xa8, 0xa8, 0x02, 0x00, 0x00, /* mov %gs,0x2a8(%r8) */
4054 0x41, 0x8c, 0x90, 0xaa, 0x02, 0x00, 0x00, /* mov %ss,0x2aa(%r8) */
4055 0x41, 0xff, 0xd1, /* callq *%r9 */
4056 0xc9, /* leaveq */
4057 0xc3, /* retq */
4058 };
4059
4060 memcpy( func_ptr, call_func, sizeof(call_func) );
4061
4062#define COMPARE(reg) \
4063 ok( context.reg == expect.reg, "wrong " #reg " %p/%p\n", (void *)(ULONG64)context.reg, (void *)(ULONG64)expect.reg )
4064
4065 memset( &context, 0xcc, sizeof(context) );
4066 memset( &expect, 0xcc, sizeof(expect) );
4067 func_ptr( &context, 0, &expect, pRtlCaptureContext );
4068
4069 if (is_arm64ec)
4070 {
4072 "wrong flags %08lx\n", context.ContextFlags );
4073 ok( (context.EFlags & ~0xd5) == (expect.EFlags & ~0xd5), "wrong EFlags %lx / %I64x\n",
4074 context.EFlags, expect.EFlags );
4075 ok( context.SegCs == 0xcccc, "wrong SegCs %x\n", context.SegCs);
4076 ok( context.SegDs == 0xcccc, "wrong SegDs %x\n", context.SegDs);
4077 ok( context.SegEs == 0xcccc, "wrong SegEs %x\n", context.SegEs);
4078 ok( context.SegFs == 0xcccc, "wrong SegFs %x\n", context.SegFs);
4079 ok( context.SegGs == 0xcccc, "wrong SegGs %x\n", context.SegGs);
4080 ok( context.SegSs == 0xcccc, "wrong SegSs %x\n", context.SegSs);
4081 }
4082 else
4083 {
4085 "wrong flags %08lx\n", context.ContextFlags );
4086 COMPARE( Rax );
4087 COMPARE( Rcx );
4088 COMPARE( Rdx );
4089 COMPARE( R8 );
4090 COMPARE( R9 );
4091 COMPARE( R10 );
4092 COMPARE( R11 );
4093 COMPARE( EFlags );
4094 COMPARE( SegCs );
4095 COMPARE( SegDs );
4096 COMPARE( SegEs );
4097 COMPARE( SegFs );
4098 COMPARE( SegGs );
4099 COMPARE( SegSs );
4100 ok( !memcmp( &context.FltSave, &expect.FltSave, offsetof( XMM_SAVE_AREA32, XmmRegisters )),
4101 "wrong FltSave\n" );
4102 }
4103 COMPARE( Rbx );
4104 COMPARE( Rsi );
4105 COMPARE( Rdi );
4106 COMPARE( R12 );
4107 COMPARE( R13 );
4108 COMPARE( R14 );
4109 COMPARE( R15 );
4110 COMPARE( Rbp );
4111 COMPARE( Rsp );
4112 COMPARE( MxCsr );
4113 COMPARE( FltSave.MxCsr );
4114 COMPARE( FltSave.ControlWord );
4115 COMPARE( FltSave.StatusWord );
4116 for (i = 0; i < 16; i++)
4117 ok( !memcmp( &context.Xmm0 + i, &expect.FltSave.XmmRegisters[i], sizeof(context.Xmm0) ),
4118 "wrong xmm%u\n", i );
4119 /* Rip is return address from RtlCaptureContext */
4120 ok( context.Rip == (ULONG64)func_ptr + sizeof(call_func) - 2,
4121 "wrong Rip %p/%p\n", (void *)context.Rip, (char *)func_ptr + sizeof(call_func) - 2 );
4122
4123 memset( &context, 0xcc, sizeof(context) );
4124 memset( &expect, 0xcc, sizeof(expect) );
4126
4127 status = func_ptr( GetCurrentThread(), &context, &expect, pNtGetContextThread );
4128 ok( status == STATUS_SUCCESS, "NtGetContextThread failed %08lx\n", status );
4129
4130 if (is_arm64ec)
4131 {
4132 /* Rsp is the stack upon entry to the ARM64 NtGetContextThread syscall */
4133 ok( context.Rsp <= expect.Rsp - sizeof(ARM64_NT_CONTEXT) && context.Rsp >= expect.Rsp - 0x1000,
4134 "wrong Rsp %p/%p\n", (void *)context.Rsp, (void *)expect.Rsp );
4135 }
4136 else
4137 {
4138 /* other registers are not preserved */
4139 COMPARE( Rbx );
4140 COMPARE( Rsi );
4141 COMPARE( Rdi );
4142 COMPARE( R12 );
4143 COMPARE( R13 );
4144 COMPARE( R14 );
4145 COMPARE( R15 );
4146 COMPARE( Rbp );
4147 /* Rsp is the stack upon entry to NtGetContextThread */
4148 ok( context.Rsp == expect.Rsp - 8,
4149 "wrong Rsp %p/%p\n", (void *)context.Rsp, (void *)expect.Rsp );
4150 /* Rip is somewhere close to the NtGetContextThread implementation */
4151 ok( (char *)context.Rip >= (char *)pNtGetContextThread - 0x40000 &&
4152 (char *)context.Rip <= (char *)pNtGetContextThread + 0x40000,
4153 "wrong Rip %p/%p\n", (void *)context.Rip, (void *)pNtGetContextThread );
4154 }
4155 COMPARE( MxCsr );
4156 COMPARE( SegCs );
4157 COMPARE( SegDs );
4158 COMPARE( SegEs );
4159 COMPARE( SegFs );
4160 if (expect.SegGs) COMPARE( SegGs );
4161 COMPARE( SegSs );
4162
4163 /* AMD CPUs don't save the opcode or data pointer if no exception is
4164 * pending; see the AMD64 Architecture Programmer's Manual Volume 5 s.v.
4165 * FXSAVE */
4166 memcpy( &expect.FltSave, &context.FltSave, 0x12 );
4167
4168 ok( !memcmp( &context.FltSave, &expect.FltSave, offsetof( XMM_SAVE_AREA32, ErrorOffset )), "wrong FltSave\n" );
4169 for (i = 6; i < 16; i++)
4170 ok( !memcmp( &context.Xmm0 + i, &expect.FltSave.XmmRegisters[i], sizeof(context.Xmm0) ),
4171 "wrong xmm%u\n", i );
4172#undef COMPARE
4173}
4174
4175static void test_continue(void)
4176{
4177 struct context_pair {
4179 CONTEXT after;
4180 } contexts;
4181 NTSTATUS (*func_ptr)( struct context_pair *, void *arg, void *continue_func, void *capture_func ) = code_mem;
4182 KCONTINUE_ARGUMENT args = { .ContinueType = KCONTINUE_UNWIND };
4183 int i;
4184
4185 static const BYTE call_func[] =
4186 {
4187 /* ret at 8*13(rsp) */
4188
4189 /* need to preserve these */
4190 0x53, /* push %rbx; 8*12(rsp) */
4191 0x55, /* push %rbp; 8*11(rsp) */
4192 0x56, /* push %rsi; 8*10(rsp) */
4193 0x57, /* push %rdi; 8*9(rsp) */
4194 0x41, 0x54, /* push %r12; 8*8(rsp) */
4195 0x41, 0x55, /* push %r13; 8*7(rsp) */
4196 0x41, 0x56, /* push %r14; 8*6(rsp) */
4197 0x41, 0x57, /* push %r15; 8*5(rsp) */
4198
4199 0x48, 0x83, 0xec, 0x28, /* sub $0x28, %rsp; reserve space for rsp and outgoing reg params */
4200 0x48, 0x89, 0x64, 0x24, 0x20, /* mov %rsp, 8*4(%rsp); for stack validation */
4201
4202 /* save args */
4203 0x48, 0x89, 0x4c, 0x24, 0x70, /* mov %rcx, 8*14(%rsp) */
4204 0x48, 0x89, 0x54, 0x24, 0x78, /* mov %rdx, 8*15(%rsp) */
4205 0x4c, 0x89, 0x84, 0x24, 0x80, 0x00, 0x00, 0x00, /* mov %r8, 8*16(%rsp) */
4206 0x4c, 0x89, 0x8c, 0x24, 0x88, 0x00, 0x00, 0x00, /* mov %r9, 8*17(%rsp) */
4207
4208 /* invoke capture context */
4209 0x41, 0xff, 0xd1, /* call *%r9 */
4210
4211 /* overwrite general registers */
4212 0x48, 0xb8, 0xef, 0xbe, 0xad, 0xde, 0xef, 0xbe, 0xad, 0xde, /* movabs $0xdeadbeefdeadbeef, %rax */
4213 0x48, 0x89, 0xc1, /* mov %rax, %rcx */
4214 0x48, 0x89, 0xc2, /* mov %rax, %rdx */
4215 0x48, 0x89, 0xc3, /* mov %rax, %rbx */
4216 0x48, 0x89, 0xc5, /* mov %rax, %rbp */
4217 0x48, 0x89, 0xc6, /* mov %rax, %rsi */
4218 0x48, 0x89, 0xc7, /* mov %rax, %rdi */
4219 0x49, 0x89, 0xc0, /* mov %rax, %r8 */
4220 0x49, 0x89, 0xc1, /* mov %rax, %r9 */
4221 0x49, 0x89, 0xc2, /* mov %rax, %r10 */
4222 0x49, 0x89, 0xc3, /* mov %rax, %r11 */
4223 0x49, 0x89, 0xc4, /* mov %rax, %r12 */
4224 0x49, 0x89, 0xc5, /* mov %rax, %r13 */
4225 0x49, 0x89, 0xc6, /* mov %rax, %r14 */
4226 0x49, 0x89, 0xc7, /* mov %rax, %r15 */
4227
4228 /* overwrite SSE registers */
4229 0x66, 0x48, 0x0f, 0x6e, 0xc0, /* movq %rax, %xmm0 */
4230 0x66, 0x0f, 0x6c, 0xc0, /* punpcklqdq %xmm0, %xmm0; extend to high quadword */
4231 0x0f, 0x28, 0xc8, /* movaps %xmm0, %xmm1 */
4232 0x0f, 0x28, 0xd0, /* movaps %xmm0, %xmm2 */
4233 0x0f, 0x28, 0xd8, /* movaps %xmm0, %xmm3 */
4234 0x0f, 0x28, 0xe0, /* movaps %xmm0, %xmm4 */
4235 0x0f, 0x28, 0xe8, /* movaps %xmm0, %xmm5 */
4236 0x0f, 0x28, 0xf0, /* movaps %xmm0, %xmm6 */
4237 0x0f, 0x28, 0xf8, /* movaps %xmm0, %xmm7 */
4238 0x44, 0x0f, 0x28, 0xc0, /* movaps %xmm0, %xmm8 */
4239 0x44, 0x0f, 0x28, 0xc8, /* movaps %xmm0, %xmm9 */
4240 0x44, 0x0f, 0x28, 0xd0, /* movaps %xmm0, %xmm10 */
4241 0x44, 0x0f, 0x28, 0xd8, /* movaps %xmm0, %xmm11 */
4242 0x44, 0x0f, 0x28, 0xe0, /* movaps %xmm0, %xmm12 */
4243 0x44, 0x0f, 0x28, 0xe8, /* movaps %xmm0, %xmm13 */
4244 0x44, 0x0f, 0x28, 0xf0, /* movaps %xmm0, %xmm14 */
4245 0x44, 0x0f, 0x28, 0xf8, /* movaps %xmm0, %xmm15 */
4246
4247 /* FIXME: overwrite debug, x87 FPU and AVX registers to test those */
4248
4249 /* load args */
4250 0x48, 0x8b, 0x4c, 0x24, 0x70, /* mov 8*14(%rsp), %rcx; context */
4251 0x48, 0x8b, 0x54, 0x24, 0x78, /* mov 8*15(%rsp), %rdx; arg */
4252 0x48, 0x83, 0xec, 0x70, /* sub $0x70, %rsp; change stack */
4253
4254 /* setup context to return to label 1 */
4255 0x48, 0x8d, 0x05, 0x18, 0x00, 0x00, 0x00, /* lea 1f(%rip), %rax */
4256 0x48, 0x89, 0x81, 0xf8, 0x00, 0x00, 0x00, /* mov %rax, 0xf8(%rcx); context.Rip */
4257
4258 /* flip some EFLAGS */
4259 0x9c, /* pushf */
4260 /*
4261 0x0001 Carry flag
4262 0x0004 Parity flag
4263 0x0010 Auxiliary Carry flag
4264 0x0040 Zero flag
4265 0x0080 Sign flag
4266 FIXME: 0x0400 Direction flag - not changing as it breaks Wine
4267 0x0800 Overflow flag
4268 ~0x4000~ Nested task flag - not changing - breaks Wine
4269 = 0x8d5
4270 */
4271 0x48, 0x81, 0x34, 0x24, 0xd5, 0x08, 0x00, 0x00, /* xorq $0x8d5, (%rsp) */
4272 0x9d, /* popf */
4273
4274 /* invoke NtContinue... */
4275 0xff, 0x94, 0x24, 0xf0, 0x00, 0x00, 0x00, /* call *8*16+0x70(%rsp) */
4276
4277 /* validate stack pointer */
4278 0x48, 0x3b, 0x64, 0x24, 0x20, /* 1: cmp 0x20(%rsp), %rsp */
4279 0x74, 0x02, /* je 2f; jump over ud2 */
4280 0x0f, 0x0b, /* ud2; stack pointer invalid, let's crash */
4281
4282 /* invoke capture context */
4283 0x48, 0x8b, 0x4c, 0x24, 0x70, /* 2: mov 8*14(%rsp), %rcx; context */
4284 0x48, 0x81, 0xc1, 0xd0, 0x04, 0x00, 0x00, /* add $0x4d0, %rcx; +sizeof(CONTEXT) to get context->after */
4285 0xff, 0x94, 0x24, 0x88, 0x00, 0x00, 0x00, /* call *8*17(%rsp) */
4286
4287 /* free stack */
4288 0x48, 0x83, 0xc4, 0x28, /* add $0x28, %rsp */
4289
4290 /* restore back */
4291 0x41, 0x5f, /* pop %r15 */
4292 0x41, 0x5e, /* pop %r14 */
4293 0x41, 0x5d, /* pop %r13 */
4294 0x41, 0x5c, /* pop %r12 */
4295 0x5f, /* pop %rdi */
4296 0x5e, /* pop %rsi */
4297 0x5d, /* pop %rbp */
4298 0x5b, /* pop %rbx */
4299 0xc3 /* ret */
4300 };
4301
4302 if (!pRtlCaptureContext)
4303 {
4304 win_skip("RtlCaptureContext is not available.\n");
4305 return;
4306 }
4307
4308 memcpy( func_ptr, call_func, sizeof(call_func) );
4309 FlushInstructionCache( GetCurrentProcess(), func_ptr, sizeof(call_func) );
4310
4311 func_ptr( &contexts, 0, NtContinue, pRtlCaptureContext );
4312
4313#define COMPARE(reg) \
4314 ok( contexts.before.reg == contexts.after.reg, "wrong " #reg " %p/%p\n", (void *)(ULONG64)contexts.before.reg, (void *)(ULONG64)contexts.after.reg )
4315
4316 COMPARE( Rax );
4317 COMPARE( Rdx );
4318 COMPARE( Rbx );
4319 COMPARE( Rbp );
4320 COMPARE( Rsi );
4321 COMPARE( Rdi );
4322 COMPARE( R8 );
4323 COMPARE( R9 );
4324 COMPARE( R10 );
4325 COMPARE( R11 );
4326 COMPARE( R12 );
4327 COMPARE( R13 );
4328 COMPARE( R14 );
4329 COMPARE( R15 );
4330
4331 for (i = 0; i < 16; i++)
4332 ok( !memcmp( &contexts.before.Xmm0 + i, &contexts.after.Xmm0 + i, sizeof(contexts.before.Xmm0) ),
4333 "wrong xmm%u %08I64x%08I64x/%08I64x%08I64x\n", i, *(&contexts.before.Xmm0.High + i*2), *(&contexts.before.Xmm0.Low + i*2),
4334 *(&contexts.after.Xmm0.High + i*2), *(&contexts.after.Xmm0.Low + i*2) );
4335
4336 apc_count = 0;
4337 pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef );
4338 func_ptr( &contexts, 0, NtContinue, pRtlCaptureContext );
4339 ok( apc_count == 0, "apc called\n" );
4340 func_ptr( &contexts, (void *)1, NtContinue, pRtlCaptureContext );
4341 ok( apc_count == 1, "apc not called\n" );
4342
4343 if (!pNtContinueEx)
4344 {
4345 win_skip( "NtContinueEx not supported\n" );
4346 return;
4347 }
4348
4349 func_ptr( &contexts, &args, pNtContinueEx, pRtlCaptureContext );
4350
4351#define COMPARE(reg) \
4352 ok( contexts.before.reg == contexts.after.reg, "wrong " #reg " %p/%p\n", (void *)(ULONG64)contexts.before.reg, (void *)(ULONG64)contexts.after.reg )
4353
4354 COMPARE( Rax );
4355 COMPARE( Rdx );
4356 COMPARE( Rbx );
4357 COMPARE( Rbp );
4358 COMPARE( Rsi );
4359 COMPARE( Rdi );
4360 COMPARE( R8 );
4361 COMPARE( R9 );
4362 COMPARE( R10 );
4363 COMPARE( R11 );
4364 COMPARE( R12 );
4365 COMPARE( R13 );
4366 COMPARE( R14 );
4367 COMPARE( R15 );
4368
4369 for (i = 0; i < 16; i++)
4370 ok( !memcmp( &contexts.before.Xmm0 + i, &contexts.after.Xmm0 + i, sizeof(contexts.before.Xmm0) ),
4371 "wrong xmm%u %08I64x%08I64x/%08I64x%08I64x\n", i, *(&contexts.before.Xmm0.High + i*2), *(&contexts.before.Xmm0.Low + i*2),
4372 *(&contexts.after.Xmm0.High + i*2), *(&contexts.after.Xmm0.Low + i*2) );
4373
4374 pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234 + apc_count, 0x5678, 0xdeadbeef );
4375 func_ptr( &contexts, &args, pNtContinueEx, pRtlCaptureContext );
4376 ok( apc_count == 1, "apc called\n" );
4377 args.ContinueFlags = KCONTINUE_FLAG_TEST_ALERT;
4378 func_ptr( &contexts, &args, pNtContinueEx, pRtlCaptureContext );
4379 ok( apc_count == 2, "apc not called\n" );
4380
4381#undef COMPARE
4382}
4383
4384static void test_wow64_context(void)
4385{
4386 const char appname[] = "C:\\windows\\syswow64\\cmd.exe";
4387 char cmdline[256];
4390 STARTUPINFOA si = {0};
4391 WOW64_CONTEXT ctx, *ctx_ptr = NULL;
4393 NTSTATUS ret;
4394 TEB teb;
4395 TEB32 teb32;
4396 SIZE_T res, cpu_size = 0;
4398 WOW64_CPU_AREA_INFO cpu_info;
4399 BOOL r, got32, got64;
4400 unsigned int i, cs32, cs64;
4401 ULONG_PTR ecx, rcx;
4402
4403#ifdef __REACTOS__
4404 if ((pRtlWow64GetThreadContext == NULL) ||
4405 (pRtlWow64SetThreadContext == NULL))
4406 {
4407 skip("RtlWow64Get/SetThreadContext not found\n");
4408 return;
4409 }
4410#endif
4411
4412 memset(&ctx, 0x55, sizeof(ctx));
4413 ctx.ContextFlags = WOW64_CONTEXT_ALL;
4414 ret = pRtlWow64GetThreadContext( GetCurrentThread(), &ctx );
4416
4417 sprintf( cmdline, "\"%s\" /c for /l %%n in () do @echo >nul", appname );
4419 ok( r, "failed to start %s err %lu\n", appname, GetLastError() );
4420
4421 ret = pRtlWow64GetThreadContext( pi.hThread, &ctx );
4422 ok(ret == STATUS_SUCCESS, "got %#lx\n", ret);
4423 ok(ctx.ContextFlags == WOW64_CONTEXT_ALL, "got context flags %#lx\n", ctx.ContextFlags);
4424 ok(!ctx.Ebp, "got ebp %08lx\n", ctx.Ebp);
4425 ok(!ctx.Ecx, "got ecx %08lx\n", ctx.Ecx);
4426 ok(!ctx.Edx, "got edx %08lx\n", ctx.Edx);
4427 ok(!ctx.Esi, "got esi %08lx\n", ctx.Esi);
4428 ok(!ctx.Edi, "got edi %08lx\n", ctx.Edi);
4429 ok((ctx.EFlags & ~2) == 0x200, "got eflags %08lx\n", ctx.EFlags);
4430 ok((WORD) ctx.FloatSave.ControlWord == 0x27f, "got control word %08lx\n",
4431 ctx.FloatSave.ControlWord);
4432 ok(*(WORD *)ctx.ExtendedRegisters == 0x27f, "got SSE control word %04x\n",
4433 *(WORD *)ctx.ExtendedRegisters);
4434
4435 ret = pRtlWow64SetThreadContext( pi.hThread, &ctx );
4436 ok(ret == STATUS_SUCCESS, "got %#lx\n", ret);
4437
4438 pNtQueryInformationThread( pi.hThread, ThreadBasicInformation, &info, sizeof(info), NULL );
4439 if (!ReadProcessMemory( pi.hProcess, info.TebBaseAddress, &teb, sizeof(teb), &res )) res = 0;
4440 ok( res == sizeof(teb), "wrong len %Ix\n", res );
4441
4442 memset( &teb32, 0, sizeof(teb32) );
4443 if (teb.WowTebOffset > 1)
4444 {
4445 if (!ReadProcessMemory( pi.hProcess, (char *)info.TebBaseAddress + teb.WowTebOffset,
4446 &teb32, sizeof(teb32), &res )) res = 0;
4447 ok( res == sizeof(teb32), "wrong len %Ix\n", res );
4448
4449 ok( ((ctx.Esp + 0xfff) & ~0xfff) == teb32.Tib.StackBase,
4450 "esp is not at top of stack: %08lx / %08lx\n", ctx.Esp, teb32.Tib.StackBase );
4451 ok( ULongToPtr( teb32.Tib.StackBase ) <= teb.DeallocationStack ||
4452 ULongToPtr( teb32.DeallocationStack ) >= teb.Tib.StackBase,
4453 "stacks overlap %08lx-%08lx / %p-%p\n", teb32.DeallocationStack, teb32.Tib.StackBase,
4454 teb.DeallocationStack, teb.Tib.StackBase );
4455 }
4456
4457 if (pRtlWow64GetCpuAreaInfo)
4458 {
4459 ok( teb.TlsSlots[WOW64_TLS_CPURESERVED] == teb.Tib.StackBase, "wrong cpu reserved %p / %p\n",
4461 cpu_size = 0x1000 - ((ULONG_PTR)teb.TlsSlots[WOW64_TLS_CPURESERVED] & 0xfff);
4462 cpu = malloc( cpu_size );
4463 if (!ReadProcessMemory( pi.hProcess, teb.TlsSlots[WOW64_TLS_CPURESERVED], cpu, cpu_size, &res )) res = 0;
4464 ok( res == cpu_size, "wrong len %Ix\n", res );
4465 ok( cpu->Machine == IMAGE_FILE_MACHINE_I386, "wrong machine %04x\n", cpu->Machine );
4466 ret = pRtlWow64GetCpuAreaInfo( cpu, 0, &cpu_info );
4467 ok( !ret, "RtlWow64GetCpuAreaInfo failed %lx\n", ret );
4468 /* work around pointer truncation bug on win10 <= 1709 */
4469 if (!((ULONG_PTR)cpu_info.Context >> 32))
4470 {
4471 cpu_info.Context = (char *)cpu + (ULONG)((char *)cpu_info.Context - (char *)cpu);
4472 cpu_info.ContextEx = (char *)cpu + (ULONG)((char *)cpu_info.ContextEx - (char *)cpu);
4473 }
4474 ctx_ptr = (WOW64_CONTEXT *)cpu_info.Context;
4475 ok(!*(void **)cpu_info.ContextEx, "got context_ex %p\n", *(void **)cpu_info.ContextEx);
4476 ok(ctx_ptr->ContextFlags == WOW64_CONTEXT_ALL, "got context flags %#lx\n", ctx_ptr->ContextFlags);
4477 ok(ctx_ptr->Eax == ctx.Eax, "got eax %08lx / %08lx\n", ctx_ptr->Eax, ctx.Eax);
4478 ok(ctx_ptr->Ebx == ctx.Ebx, "got ebx %08lx / %08lx\n", ctx_ptr->Ebx, ctx.Ebx);
4479 ok(ctx_ptr->Ecx == ctx.Ecx, "got ecx %08lx / %08lx\n", ctx_ptr->Ecx, ctx.Ecx);
4480 ok(ctx_ptr->Edx == ctx.Edx, "got edx %08lx / %08lx\n", ctx_ptr->Edx, ctx.Edx);
4481 ok(ctx_ptr->Ebp == ctx.Ebp, "got ebp %08lx / %08lx\n", ctx_ptr->Ebp, ctx.Ebp);
4482 ok(ctx_ptr->Esi == ctx.Esi, "got esi %08lx / %08lx\n", ctx_ptr->Esi, ctx.Esi);
4483 ok(ctx_ptr->Edi == ctx.Edi, "got edi %08lx / %08lx\n", ctx_ptr->Edi, ctx.Edi);
4484 ok(ctx_ptr->SegCs == ctx.SegCs, "got cs %04lx / %04lx\n", ctx_ptr->SegCs, ctx.SegCs);
4485 ok(ctx_ptr->SegDs == ctx.SegDs, "got ds %04lx / %04lx\n", ctx_ptr->SegDs, ctx.SegDs);
4486 ok(ctx_ptr->SegEs == ctx.SegEs, "got es %04lx / %04lx\n", ctx_ptr->SegEs, ctx.SegEs);
4487 ok(ctx_ptr->SegFs == ctx.SegFs, "got fs %04lx / %04lx\n", ctx_ptr->SegFs, ctx.SegFs);
4488 ok(ctx_ptr->SegGs == ctx.SegGs, "got gs %04lx / %04lx\n", ctx_ptr->SegGs, ctx.SegGs);
4489 ok(ctx_ptr->SegSs == ctx.SegSs, "got ss %04lx / %04lx\n", ctx_ptr->SegSs, ctx.SegSs);
4490 ok(ctx_ptr->EFlags == ctx.EFlags, "got eflags %08lx / %08lx\n", ctx_ptr->EFlags, ctx.EFlags);
4491 ok((WORD)ctx_ptr->FloatSave.ControlWord == ctx.FloatSave.ControlWord,
4492 "got control word %08lx / %08lx\n", ctx_ptr->FloatSave.ControlWord, ctx.FloatSave.ControlWord);
4493 ok(*(WORD *)ctx_ptr->ExtendedRegisters == *(WORD *)ctx.ExtendedRegisters,
4494 "got SSE control word %04x / %04x\n", *(WORD *)ctx_ptr->ExtendedRegisters,
4495 *(WORD *)ctx.ExtendedRegisters);
4496
4497 ecx = ctx.Ecx;
4498 ctx.Ecx = 0x12345678;
4499 ret = pRtlWow64SetThreadContext( pi.hThread, &ctx );
4500 ok(ret == STATUS_SUCCESS, "got %#lx\n", ret);
4501 if (!ReadProcessMemory( pi.hProcess, teb.TlsSlots[WOW64_TLS_CPURESERVED], cpu, cpu_size, &res )) res = 0;
4502 ok( res == cpu_size, "wrong len %Ix\n", res );
4503 todo_wine
4504 ok( ctx_ptr->Ecx == 0x12345678, "got ecx %08lx\n", ctx_ptr->Ecx );
4505 ctx.Ecx = ecx;
4506 pRtlWow64SetThreadContext( pi.hThread, &ctx );
4507 }
4508 else win_skip( "RtlWow64GetCpuAreaInfo not supported\n" );
4509
4510 memset( &context, 0x55, sizeof(context) );
4511 context.ContextFlags = CONTEXT_ALL;
4512 ret = pNtGetContextThread( pi.hThread, &context );
4513 ok(ret == STATUS_SUCCESS, "got %#lx\n", ret);
4514 ok( context.ContextFlags == is_arm64ec ? CONTEXT_FULL : CONTEXT_ALL,
4515 "got context flags %#lx\n", context.ContextFlags );
4516 ok( !context.Rsi, "rsi is not zero %Ix\n", context.Rsi );
4517 ok( !context.Rdi, "rdi is not zero %Ix\n", context.Rdi );
4518 ok( !context.Rbp, "rbp is not zero %Ix\n", context.Rbp );
4519 ok( !context.R8, "r8 is not zero %Ix\n", context.R8 );
4520 ok( !context.R9, "r9 is not zero %Ix\n", context.R9 );
4521 ok( !context.R10, "r10 is not zero %Ix\n", context.R10 );
4522 ok( !context.R11, "r11 is not zero %Ix\n", context.R11 );
4523 ok( !context.R12, "r12 is not zero %Ix\n", context.R12 );
4524 ok( !context.R13, "r13 is not zero %Ix\n", context.R13 );
4525 ok( !context.R14, "r14 is not zero %Ix\n", context.R14 );
4526 ok( !context.R15, "r15 is not zero %Ix\n", context.R15 );
4527 ok( context.MxCsr == 0x1f80, "wrong mxcsr %08lx\n", context.MxCsr );
4528 ok( context.FltSave.ControlWord == 0x27f, "wrong control %08x\n", context.FltSave.ControlWord );
4529 if (LOWORD(context.ContextFlags) & CONTEXT_SEGMENTS)
4530 {
4531 ok( context.SegDs == ctx.SegDs, "wrong ds %04x / %04lx\n", context.SegDs, ctx.SegDs );
4532 ok( context.SegEs == ctx.SegEs, "wrong es %04x / %04lx\n", context.SegEs, ctx.SegEs );
4533 ok( context.SegFs == ctx.SegFs, "wrong fs %04x / %04lx\n", context.SegFs, ctx.SegFs );
4534 ok( context.SegGs == ctx.SegGs, "wrong gs %04x / %04lx\n", context.SegGs, ctx.SegGs );
4535 ok( context.SegSs == ctx.SegSs, "wrong ss %04x / %04lx\n", context.SegSs, ctx.SegSs );
4536 }
4537 cs32 = ctx.SegCs;
4538 cs64 = context.SegCs;
4539 if (cs32 == cs64)
4540 {
4541 todo_wine win_skip( "no wow64 support\n" );
4542 goto done;
4543 }
4544
4545 ok( !context.Rax, "rax is not zero %Ix\n", context.Rax );
4546 ok( !context.Rbx, "rbx is not zero %Ix\n", context.Rbx );
4547 ok( ((ULONG_PTR)context.Rsp & ~0xfff) == ((ULONG_PTR)teb.Tib.StackBase & ~0xfff),
4548 "rsp is not at top of stack %p / %p\n", (void *)context.Rsp, teb.Tib.StackBase );
4549 ok( context.EFlags == 0x200 || context.EFlags == 0x202, "wrong flags %08lx\n", context.EFlags );
4550
4551 for (i = 0, got32 = got64 = FALSE; i < 10000 && !(got32 && got64); i++)
4552 {
4554 Sleep( 1 );
4556 memset( &context, 0x55, sizeof(context) );
4557 context.ContextFlags = CONTEXT_ALL;
4558 ret = pNtGetContextThread( pi.hThread, &context );
4559 ok( ret == STATUS_SUCCESS, "got %#lx\n", ret );
4560 if (ret) break;
4561 if (context.SegCs == cs32 && got32) continue;
4562 if (context.SegCs == cs64 && got64) continue;
4563 if (context.SegCs != cs32 && context.SegCs != cs64)
4564 {
4565 ok( 0, "unexpected cs %04x\n", context.SegCs );
4566 break;
4567 }
4568
4569 memset( &ctx, 0x55, sizeof(ctx) );
4570 ctx.ContextFlags = WOW64_CONTEXT_ALL;
4571 ret = pRtlWow64GetThreadContext( pi.hThread, &ctx );
4572 ok(ret == STATUS_SUCCESS, "got %#lx\n", ret);
4573 ok( ctx.ContextFlags == WOW64_CONTEXT_ALL, "got context flags %#lx\n", ctx.ContextFlags );
4574
4576 ret = pRtlWow64GetThreadContext( pi.hThread, &ctx );
4577 ok(ret == STATUS_SUCCESS, "got %#lx\n", ret);
4578#ifdef __REACTOS__
4581 "got context flags %#lx\n", ctx.ContextFlags );
4582 else
4583#endif
4584 ok( (ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING) || broken( ctx.ContextFlags == WOW64_CONTEXT_ALL ) /*Win 7*/,
4585 "got context flags %#lx\n", ctx.ContextFlags );
4586
4587 if (context.SegCs == cs32)
4588 {
4589 trace( "in 32-bit mode %04x\n", context.SegCs );
4590 if (ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING)
4594 "got %#lx.\n", ctx.ContextFlags );
4595 ok( ctx.Eip == context.Rip, "cs32: eip %08lx / %p\n", ctx.Eip, (void *)context.Rip );
4596 ok( ctx.Ebp == context.Rbp, "cs32: ebp %08lx / %p\n", ctx.Ebp, (void *)context.Rbp );
4597 ok( ctx.Esp == context.Rsp, "cs32: esp %08lx / %p\n", ctx.Esp, (void *)context.Rsp );
4598 ok( ctx.Eax == context.Rax, "cs32: eax %08lx / %p\n", ctx.Eax, (void *)context.Rax );
4599 ok( ctx.Ebx == context.Rbx, "cs32: ebx %08lx / %p\n", ctx.Ebx, (void *)context.Rbx );
4600 ok( ctx.Ecx == context.Rcx || broken(ctx.Ecx == (ULONG)context.Rcx),
4601 "cs32: ecx %08lx / %p\n", ctx.Ecx, (void *)context.Rcx );
4602 ok( ctx.Edx == context.Rdx, "cs32: edx %08lx / %p\n", ctx.Edx, (void *)context.Rdx );
4603 ok( ctx.Esi == context.Rsi, "cs32: esi %08lx / %p\n", ctx.Esi, (void *)context.Rsi );
4604 ok( ctx.Edi == context.Rdi, "cs32: edi %08lx / %p\n", ctx.Edi, (void *)context.Rdi );
4605 ok( ctx.SegCs == cs32, "cs32: wrong cs %04lx / %04x\n", ctx.SegCs, cs32 );
4606 ok( ctx.SegDs == context.SegDs, "cs32: wrong ds %04lx / %04x\n", ctx.SegDs, context.SegDs );
4607 ok( ctx.SegEs == context.SegEs, "cs32: wrong es %04lx / %04x\n", ctx.SegEs, context.SegEs );
4608 ok( ctx.SegFs == context.SegFs, "cs32: wrong fs %04lx / %04x\n", ctx.SegFs, context.SegFs );
4609 ok( ctx.SegGs == context.SegGs, "cs32: wrong gs %04lx / %04x\n", ctx.SegGs, context.SegGs );
4610 ok( ctx.SegSs == context.SegSs, "cs32: wrong ss %04lx / %04x\n", ctx.SegSs, context.SegSs );
4611 if (teb32.DeallocationStack)
4612 ok( ctx.Esp >= teb32.DeallocationStack && ctx.Esp <= teb32.Tib.StackBase,
4613 "cs32: esp not inside 32-bit stack %08lx / %08lx-%08lx\n", ctx.Esp,
4614 teb32.DeallocationStack, teb32.Tib.StackBase );
4615 /* r12 points to the TEB */
4616 ok( (void *)context.R12 == info.TebBaseAddress,
4617 "cs32: r12 not pointing to the TEB %p / %p\n", (void *)context.R12, info.TebBaseAddress );
4618 /* r13 points inside the cpu area */
4619 ok( (void *)context.R13 >= teb.TlsSlots[WOW64_TLS_CPURESERVED] &&
4620 context.R13 <= ((ULONG_PTR)teb.TlsSlots[WOW64_TLS_CPURESERVED] | 0xfff),
4621 "cs32: r13 not pointing into cpu area %p / %p\n", (void *)context.R13,
4623 /* r14 stores the 64-bit stack pointer */
4624 ok( (void *)context.R14 >= teb.DeallocationStack && (void *)context.R14 <= teb.Tib.StackBase,
4625 "cs32: r14 not inside 32-bit stack %p / %p-%p\n", (void *)context.R14,
4626 (void *)teb.DeallocationStack, (void *)teb.Tib.StackBase );
4627
4628 if (pRtlWow64GetCpuAreaInfo)
4629 {
4630 /* in 32-bit mode, the 32-bit context is the current cpu context, not the stored one */
4632 cpu, cpu_size, &res )) res = 0;
4633 ok( res == cpu_size, "wrong len %Ix\n", res );
4634 ok(ctx_ptr->ContextFlags == WOW64_CONTEXT_ALL,
4635 "cs32: got context flags %#lx\n", ctx_ptr->ContextFlags);
4636
4637 /* changing either context changes the actual cpu context */
4638 rcx = context.Rcx;
4639 ecx = ctx_ptr->Ecx;
4640 context.Rcx = 0xfedcba987654321ull;
4641 pNtSetContextThread( pi.hThread, &context );
4642 memset( &ctx, 0x55, sizeof(ctx) );
4643 ctx.ContextFlags = WOW64_CONTEXT_ALL;
4644 pRtlWow64GetThreadContext( pi.hThread, &ctx );
4645 todo_wine
4646 ok( ctx.Ecx == 0x87654321, "cs32: ecx set to %08lx\n", ctx.Ecx );
4648 ok( ctx_ptr->Ecx == ecx, "cs32: ecx set to %08lx\n", ctx_ptr->Ecx );
4649 ctx.Ecx = 0x33334444;
4650 pRtlWow64SetThreadContext( pi.hThread, &ctx );
4651 memset( &ctx, 0x55, sizeof(ctx) );
4652 ctx.ContextFlags = WOW64_CONTEXT_ALL;
4653 pRtlWow64GetThreadContext( pi.hThread, &ctx );
4654 ok( ctx.Ecx == 0x33334444, "cs32: ecx set to %08lx\n", ctx.Ecx );
4656 ok( ctx_ptr->Ecx == ecx, "cs32: ecx set to %08lx\n", ctx_ptr->Ecx );
4657 memset( &context, 0x55, sizeof(context) );
4658 context.ContextFlags = CONTEXT_ALL;
4659 pNtGetContextThread( pi.hThread, &context );
4660 todo_wine
4661 ok( context.Rcx == 0x33334444, "cs32: rcx set to %p\n", (void *)context.Rcx );
4662 /* restore everything */
4663 context.Rcx = rcx;
4664 pNtSetContextThread( pi.hThread, &context );
4665 }
4666 got32 = TRUE;
4667 }
4668 else
4669 {
4670 trace( "in 64-bit mode %04x\n", context.SegCs );
4671 if (ctx.ContextFlags & CONTEXT_EXCEPTION_REPORTING)
4674 || ctx.ContextFlags == (WOW64_CONTEXT_ALL | CONTEXT_EXCEPTION_REQUEST
4676 "got %#lx.\n", ctx.ContextFlags );
4677 ok( ctx.Eip != context.Rip, "cs64: eip %08lx / %p\n", ctx.Eip, (void *)context.Rip);
4678 ok( ctx.SegCs == cs32, "cs64: wrong cs %04lx / %04x\n", ctx.SegCs, cs32 );
4679 if (!is_arm64ec)
4680 {
4681 ok( ctx.SegDs == context.SegDs, "cs64: wrong ds %04lx / %04x\n", ctx.SegDs, context.SegDs );
4682 ok( ctx.SegEs == context.SegEs, "cs64: wrong es %04lx / %04x\n", ctx.SegEs, context.SegEs );
4683 ok( ctx.SegFs == context.SegFs, "cs64: wrong fs %04lx / %04x\n", ctx.SegFs, context.SegFs );
4684 ok( ctx.SegGs == context.SegGs, "cs64: wrong gs %04lx / %04x\n", ctx.SegGs, context.SegGs );
4685 ok( ctx.SegSs == context.SegSs, "cs64: wrong ss %04lx / %04x\n", ctx.SegSs, context.SegSs );
4686 }
4687 if (teb32.DeallocationStack)
4688 ok( ctx.Esp >= teb32.DeallocationStack && ctx.Esp <= teb32.Tib.StackBase,
4689 "cs64: esp not inside 32-bit stack %08lx / %08lx-%08lx\n", ctx.Esp,
4690 teb32.DeallocationStack, teb32.Tib.StackBase );
4691 ok( ((void *)context.Rsp >= teb.DeallocationStack && (void *)context.Rsp <= teb.Tib.StackBase) ||
4692 (context.Rsp >= teb32.DeallocationStack && context.Rsp <= teb32.Tib.StackBase),
4693 "cs64: rsp not inside stack %p / 64-bit %p-%p 32-bit %p-%p\n", (void *)context.Rsp,
4696
4697 if (pRtlWow64GetCpuAreaInfo)
4698 {
4699 /* in 64-bit mode, the 32-bit context is stored in the cpu area */
4701 cpu, cpu_size, &res )) res = 0;
4702 ok( res == cpu_size, "wrong len %Ix\n", res );
4703 ok(ctx_ptr->ContextFlags == WOW64_CONTEXT_ALL,
4704 "cs64: got context flags %#lx\n", ctx_ptr->ContextFlags);
4705 ok(ctx_ptr->Eip == ctx.Eip, "cs64: got eip %08lx / %08lx\n", ctx_ptr->Eip, ctx.Eip);
4706 ok(ctx_ptr->Eax == ctx.Eax, "cs64: got eax %08lx / %08lx\n", ctx_ptr->Eax, ctx.Eax);
4707 ok(ctx_ptr->Ebx == ctx.Ebx, "cs64: got ebx %08lx / %08lx\n", ctx_ptr->Ebx, ctx.Ebx);
4708 ok(ctx_ptr->Ecx == ctx.Ecx, "cs64: got ecx %08lx / %08lx\n", ctx_ptr->Ecx, ctx.Ecx);
4709 ok(ctx_ptr->Edx == ctx.Edx, "cs64: got edx %08lx / %08lx\n", ctx_ptr->Edx, ctx.Edx);
4710 ok(ctx_ptr->Ebp == ctx.Ebp, "cs64: got ebp %08lx / %08lx\n", ctx_ptr->Ebp, ctx.Ebp);
4711 ok(ctx_ptr->Esi == ctx.Esi, "cs64: got esi %08lx / %08lx\n", ctx_ptr->Esi, ctx.Esi);
4712 ok(ctx_ptr->Edi == ctx.Edi, "cs64: got edi %08lx / %08lx\n", ctx_ptr->Edi, ctx.Edi);
4713 ok(ctx_ptr->EFlags == ctx.EFlags, "cs64: got eflags %08lx / %08lx\n", ctx_ptr->EFlags, ctx.EFlags);
4714
4715 /* changing one context doesn't change the other one */
4716 rcx = context.Rcx;
4717 ecx = ctx.Ecx;
4718 context.Rcx = 0xfedcba987654321ull;
4719 pNtSetContextThread( pi.hThread, &context );
4720 memset( &ctx, 0x55, sizeof(ctx) );
4721 ctx.ContextFlags = WOW64_CONTEXT_ALL;
4722 pRtlWow64GetThreadContext( pi.hThread, &ctx );
4723 ok( ctx.Ecx == ecx, "cs64: ecx set to %08lx\n", ctx.Ecx );
4725 ok( ctx_ptr->Ecx == ecx, "cs64: ecx set to %08lx\n", ctx_ptr->Ecx );
4726 ctx.Ecx = 0x22223333;
4727 pRtlWow64SetThreadContext( pi.hThread, &ctx );
4728 memset( &ctx, 0x55, sizeof(ctx) );
4729 ctx.ContextFlags = WOW64_CONTEXT_ALL;
4730 pRtlWow64GetThreadContext( pi.hThread, &ctx );
4731 ok( ctx.Ecx == 0x22223333, "cs64: ecx set to %08lx\n", ctx.Ecx );
4733 todo_wine
4734 ok( ctx_ptr->Ecx == 0x22223333, "cs64: ecx set to %08lx\n", ctx_ptr->Ecx );
4735 memset( &context, 0x55, sizeof(context) );
4736 context.ContextFlags = CONTEXT_ALL;
4737 pNtGetContextThread( pi.hThread, &context );
4738 ok( context.Rcx == 0xfedcba987654321ull, "cs64: rcx set to %p\n", (void *)context.Rcx );
4739 /* restore everything */
4740 context.Rcx = rcx;
4741 pNtSetContextThread( pi.hThread, &context );
4742 ctx.Ecx = ecx;
4743 pRtlWow64SetThreadContext( pi.hThread, &ctx );
4744 }
4745 got64 = TRUE;
4746 if (is_arm64ec) break; /* no 32-bit %cs on arm64ec */
4747 }
4748 }
4749 if (!got32) skip( "failed to test 32-bit context\n" );
4750 if (!got64) skip( "failed to test 64-bit context\n" );
4751
4752done:
4753 pNtTerminateProcess(pi.hProcess, 0);
4754 free( cpu );
4755}
4756
4757static BYTE saved_KiUserExceptionDispatcher_bytes[12];
4758static BOOL hook_called;
4759static void *hook_KiUserExceptionDispatcher_rip;
4760static void *dbg_except_continue_handler_rip;
4761static void *hook_exception_address;
4762static struct
4763{
4764 ULONG64 old_rax;
4765 ULONG64 old_rdx;
4766 ULONG64 old_rsi;
4767 ULONG64 old_rdi;
4768 ULONG64 old_rbp;
4769 ULONG64 old_rsp;
4770 ULONG64 new_rax;
4771 ULONG64 new_rdx;
4772 ULONG64 new_rsi;
4773 ULONG64 new_rdi;
4774 ULONG64 new_rbp;
4775 ULONG64 new_rsp;
4776}
4777test_kiuserexceptiondispatcher_regs;
4778
4779static ULONG64 test_kiuserexceptiondispatcher_saved_r12;
4780
4781struct machine_frame
4782{
4783 ULONG64 rip;
4784 ULONG64 cs;
4786 ULONG64 rsp;
4787 ULONG64 ss;
4788};
4789
4790static DWORD dbg_except_continue_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
4792{
4793 trace("handler context->Rip %#Ix, codemem %p.\n", context->Rip, code_mem);
4794 got_exception = 1;
4795 dbg_except_continue_handler_rip = (void *)context->Rip;
4796 ++context->Rip;
4797 memcpy(pKiUserExceptionDispatcher, saved_KiUserExceptionDispatcher_bytes,
4798 sizeof(saved_KiUserExceptionDispatcher_bytes));
4799
4800 RtlUnwind((void *)test_kiuserexceptiondispatcher_regs.old_rsp,
4801 (BYTE *)code_mem + 0x28, rec, (void *)0xdeadbeef);
4803}
4804
4805static LONG WINAPI dbg_except_continue_vectored_handler(struct _EXCEPTION_POINTERS *e)
4806{
4807 EXCEPTION_RECORD *rec = e->ExceptionRecord;
4808 CONTEXT *context = e->ContextRecord;
4809
4810 trace("dbg_except_continue_vectored_handler, code %#lx, Rip %#Ix.\n", rec->ExceptionCode, context->Rip);
4811
4812 if (rec->ExceptionCode == 0xceadbeef)
4813 {
4814#ifndef __REACTOS__ // Broken on Windows Vista and Windows 10
4815 ok(context->P1Home == (ULONG64)0xdeadbeeffeedcafe, "Got unexpected context->P1Home %#Ix.\n", context->P1Home);
4816#endif
4817 context->R12 = test_kiuserexceptiondispatcher_saved_r12;
4819 }
4820
4821 ok(rec->ExceptionCode == 0x80000003, "Got unexpected exception code %#lx.\n", rec->ExceptionCode);
4822
4823 got_exception = 1;
4824 dbg_except_continue_handler_rip = (void *)context->Rip;
4825 if (NtCurrentTeb()->Peb->BeingDebugged && !is_arm64ec)
4826 ++context->Rip;
4827
4828 if (context->Rip >= (ULONG64)code_mem && context->Rip < (ULONG64)code_mem + 0x100)
4829 RtlUnwind((void *)test_kiuserexceptiondispatcher_regs.old_rsp,
4830 (BYTE *)code_mem + 0x28, rec, (void *)0xdeadbeef);
4831
4833}
4834
4835static void * WINAPI hook_KiUserExceptionDispatcher(EXCEPTION_RECORD *rec, CONTEXT *context)
4836{
4837 struct machine_frame *frame = (struct machine_frame *)(((ULONG_PTR)(rec + 1) + 0x0f) & ~0x0f);
4838 CONTEXT_EX *xctx = (CONTEXT_EX *)(context + 1);
4839
4840 trace("rec %p context %p context->Rip %#Ix, context->Rsp %#Ix, ContextFlags %#lx.\n",
4841 rec, context, context->Rip, context->Rsp, context->ContextFlags);
4842
4843 hook_called = TRUE;
4844 /* Broken on Win2008, probably rec offset in stack is different. */
4845 ok(rec->ExceptionCode == 0x80000003 || rec->ExceptionCode == 0xceadbeef || broken(!rec->ExceptionCode),
4846 "Got unexpected ExceptionCode %#lx.\n", rec->ExceptionCode);
4847
4848 ok( !((ULONG_PTR)context & 15), "unaligned context %p\n", context );
4849#ifdef __REACTOS__
4851 {
4852 win_skip("No extended context support.\n");
4853 }
4854 else
4855 {
4856#endif
4857 ok( xctx->All.Offset == -sizeof(CONTEXT), "wrong All.Offset %lx\n", xctx->All.Offset );
4858 ok( xctx->All.Length >= sizeof(CONTEXT) + offsetof(CONTEXT_EX, align), "wrong All.Length %lx\n", xctx->All.Length );
4859 ok( xctx->Legacy.Offset == -sizeof(CONTEXT), "wrong Legacy.Offset %lx\n", xctx->All.Offset );
4860 ok( xctx->Legacy.Length == sizeof(CONTEXT), "wrong Legacy.Length %lx\n", xctx->All.Length );
4861 ok( (void *)(xctx + 1) == (void *)rec, "wrong ptrs %p / %p\n", xctx, rec );
4862 ok( frame->rip == context->Rip, "wrong rip %Ix / %Ix\n", frame->rip, context->Rip );
4863 ok( frame->rsp == context->Rsp, "wrong rsp %Ix / %Ix\n", frame->rsp, context->Rsp );
4864#ifdef __REACTOS__
4865 }
4866#endif
4867
4868 hook_KiUserExceptionDispatcher_rip = (void *)context->Rip;
4869 hook_exception_address = rec->ExceptionAddress;
4870 memcpy(pKiUserExceptionDispatcher, saved_KiUserExceptionDispatcher_bytes,
4871 sizeof(saved_KiUserExceptionDispatcher_bytes));
4873}
4874
4875static void * WINAPI hook_KiUserExceptionDispatcher_arm64ec(EXCEPTION_RECORD *rec, CONTEXT *context)
4876{
4877 ARM64_NT_CONTEXT *arm64_context = (ARM64_NT_CONTEXT *)(context + 1);
4878
4879 trace("rec %p context %p context->Rip %#Ix, context->Rsp %#Ix, ContextFlags %#lx.\n",
4880 rec, context, context->Rip, context->Rsp, context->ContextFlags);
4881 hook_called = TRUE;
4882 ok(rec->ExceptionCode == 0x80000003 || rec->ExceptionCode == 0xceadbeef,
4883 "Got unexpected ExceptionCode %#lx.\n", rec->ExceptionCode);
4884
4885 ok( !((ULONG_PTR)context & 15), "unaligned context %p\n", context );
4886 ok( arm64_context->Pc == context->Rip, "wrong rip %Ix / %Ix\n", arm64_context->Pc, context->Rip );
4887 ok( arm64_context->Sp == context->Rsp, "wrong rsp %Ix / %Ix\n", arm64_context->Sp, context->Rsp );
4888
4889 hook_KiUserExceptionDispatcher_rip = (void *)context->Rip;
4890 hook_exception_address = rec->ExceptionAddress;
4891 memcpy(pKiUserExceptionDispatcher, saved_KiUserExceptionDispatcher_bytes,
4892 sizeof(saved_KiUserExceptionDispatcher_bytes));
4894}
4895
4896static void test_KiUserExceptionDispatcher(void)
4897{
4898 LPVOID vectored_handler;
4899 static BYTE except_code[] =
4900 {
4901 0x48, 0xb9, /* mov imm64, %rcx */
4902 /* offset: 0x2 */
4903 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
4904
4905 0x48, 0x89, 0x01, /* mov %rax, (%rcx) */
4906 0x48, 0x89, 0x51, 0x08, /* mov %rdx, 0x8(%rcx) */
4907 0x48, 0x89, 0x71, 0x10, /* mov %rsi, 0x10(%rcx) */
4908 0x48, 0x89, 0x79, 0x18, /* mov %rdi, 0x18(%rcx) */
4909 0x48, 0x89, 0x69, 0x20, /* mov %rbp, 0x20(%rcx) */
4910 0x48, 0x89, 0x61, 0x28, /* mov %rsp, 0x28(%rcx) */
4911 0x48, 0x83, 0xc1, 0x30, /* add $0x30, %rcx */
4912
4913 /* offset: 0x25 */
4914 0xcc, /* int3 */
4915
4916 0x0f, 0x0b, /* ud2, illegal instruction */
4917
4918 /* offset: 0x28 */
4919 0x48, 0xb9, /* mov imm64, %rcx */
4920 /* offset: 0x2a */
4921 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
4922
4923 0x48, 0x89, 0x01, /* mov %rax, (%rcx) */
4924 0x48, 0x89, 0x51, 0x08, /* mov %rdx, 0x8(%rcx) */
4925 0x48, 0x89, 0x71, 0x10, /* mov %rsi, 0x10(%rcx) */
4926 0x48, 0x89, 0x79, 0x18, /* mov %rdi, 0x18(%rcx) */
4927 0x48, 0x89, 0x69, 0x20, /* mov %rbp, 0x20(%rcx) */
4928 0x48, 0x89, 0x61, 0x28, /* mov %rsp, 0x28(%rcx) */
4929 0xc3, /* ret */
4930 };
4931 static BYTE hook_trampoline[] =
4932 {
4933 0x48, 0x89, 0xe2, /* mov %rsp,%rdx */
4934 0x48, 0x8d, 0x8c, 0x24, 0xf0, 0x04, 0x00, 0x00,
4935 /* lea 0x4f0(%rsp),%rcx */
4936 0x4c, 0x89, 0x22, /* mov %r12,(%rdx) */
4937 0x48, 0xb8, /* movabs hook_KiUserExceptionDispatcher,%rax */
4938 0,0,0,0,0,0,0,0, /* offset 16 */
4939 0xff, 0xd0, /* callq *rax */
4940 0x48, 0x31, 0xc9, /* xor %rcx, %rcx */
4941 0x48, 0x31, 0xd2, /* xor %rdx, %rdx */
4942 0xff, 0xe0, /* jmpq *rax */
4943 };
4944 static BYTE hook_trampoline_arm64ec[] =
4945 {
4946 0x48, 0x8d, 0x54, 0x24, 0x08, /* lea 0x8(%rsp),%rdx */
4947 0x48, 0x8d, 0x8a, 0x60, 0x08, 0x00, 0x00,
4948 /* lea 0x860(%rdx),%rcx */
4949 0x4c, 0x89, 0x22, /* mov %r12,(%rdx) */
4950 0x48, 0xb8, /* movabs hook_KiUserExceptionDispatcher_arm64ec,%rax */
4951 0,0,0,0,0,0,0,0, /* offset 16 */
4952 0xff, 0xd0, /* callq *rax */
4953 0x48, 0x31, 0xc9, /* xor %rcx, %rcx */
4954 0x48, 0x31, 0xd2, /* xor %rdx, %rdx */
4955 0xff, 0xe0, /* jmpq *rax */
4956 };
4957
4958 BYTE patched_KiUserExceptionDispatcher_bytes[12];
4959 void *bpt_address, *trampoline_ptr;
4961 DWORD old_protect;
4962 CONTEXT ctx;
4963 LONG pass;
4964 BYTE *ptr;
4965 BOOL ret;
4966
4967 *(ULONG64 *)(except_code + 2) = (ULONG64)&test_kiuserexceptiondispatcher_regs;
4968 *(ULONG64 *)(except_code + 0x2a) = (ULONG64)&test_kiuserexceptiondispatcher_regs.new_rax;
4969
4970 *(ULONG_PTR *)(hook_trampoline_arm64ec + 17) = (ULONG_PTR)hook_KiUserExceptionDispatcher_arm64ec;
4971 *(ULONG_PTR *)(hook_trampoline + 16) = (ULONG_PTR)hook_KiUserExceptionDispatcher;
4972 trampoline_ptr = (char *)code_mem + 1024;
4973 if (is_arm64ec)
4974 memcpy(trampoline_ptr, hook_trampoline_arm64ec, sizeof(hook_trampoline_arm64ec));
4975 else
4976 memcpy(trampoline_ptr, hook_trampoline, sizeof(hook_trampoline));
4977
4978 ret = VirtualProtect(pKiUserExceptionDispatcher, sizeof(saved_KiUserExceptionDispatcher_bytes),
4979 PAGE_EXECUTE_READWRITE, &old_protect);
4980 ok(ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError());
4981
4982 memcpy(saved_KiUserExceptionDispatcher_bytes, pKiUserExceptionDispatcher,
4983 sizeof(saved_KiUserExceptionDispatcher_bytes));
4984 ptr = (BYTE *)patched_KiUserExceptionDispatcher_bytes;
4985 /* mov hook_trampoline, %rax */
4986 *ptr++ = 0x48;
4987 *ptr++ = 0xb8;
4988 *(void **)ptr = trampoline_ptr;
4989 ptr += sizeof(ULONG64);
4990 /* jmp *rax */
4991 *ptr++ = 0xff;
4992 *ptr++ = 0xe0;
4993
4994 memcpy(pKiUserExceptionDispatcher, patched_KiUserExceptionDispatcher_bytes,
4995 sizeof(patched_KiUserExceptionDispatcher_bytes));
4996 got_exception = 0;
4997 run_exception_test(dbg_except_continue_handler, NULL, except_code, sizeof(except_code), PAGE_EXECUTE_READ);
4998 ok(got_exception, "Handler was not called.\n");
5000 ok(hook_called, "Hook was not called.\n");
5001 if (!hook_called) return;
5002
5003 ok(test_kiuserexceptiondispatcher_regs.new_rax == 0xdeadbeef, "Got unexpected rax %#Ix.\n",
5004 test_kiuserexceptiondispatcher_regs.new_rax);
5005 ok(test_kiuserexceptiondispatcher_regs.old_rsi
5006 == test_kiuserexceptiondispatcher_regs.new_rsi, "rsi does not match.\n");
5007 ok(test_kiuserexceptiondispatcher_regs.old_rdi
5008 == test_kiuserexceptiondispatcher_regs.new_rdi, "rdi does not match.\n");
5009 ok(test_kiuserexceptiondispatcher_regs.old_rbp
5010 == test_kiuserexceptiondispatcher_regs.new_rbp, "rbp does not match.\n");
5011
5012 bpt_address = (BYTE *)code_mem + 0x25;
5013
5014 ok(hook_exception_address == bpt_address || broken(!hook_exception_address) /* Win2008 */,
5015 "Got unexpected exception address %p, expected %p.\n",
5016 hook_exception_address, bpt_address);
5017 ok(hook_KiUserExceptionDispatcher_rip == bpt_address, "Got unexpected exception address %p, expected %p.\n",
5018 hook_KiUserExceptionDispatcher_rip, bpt_address);
5019 ok(dbg_except_continue_handler_rip == bpt_address, "Got unexpected exception address %p, expected %p.\n",
5020 dbg_except_continue_handler_rip, bpt_address);
5021
5022 memset(&record, 0, sizeof(record));
5023 record.ExceptionCode = 0x80000003;
5024 record.ExceptionFlags = 0;
5025 record.ExceptionRecord = NULL;
5026 record.ExceptionAddress = NULL;
5027 record.NumberParameters = 0;
5028
5029 vectored_handler = AddVectoredExceptionHandler(TRUE, dbg_except_continue_vectored_handler);
5030
5031 memcpy(pKiUserExceptionDispatcher, patched_KiUserExceptionDispatcher_bytes,
5032 sizeof(patched_KiUserExceptionDispatcher_bytes));
5033 got_exception = 0;
5034 hook_called = FALSE;
5035
5036 pRtlRaiseException(&record);
5037
5038 ok(got_exception, "Handler was not called.\n");
5039 ok(!hook_called, "Hook was called.\n");
5040
5041 memcpy(pKiUserExceptionDispatcher, patched_KiUserExceptionDispatcher_bytes,
5042 sizeof(patched_KiUserExceptionDispatcher_bytes));
5043 got_exception = 0;
5044 hook_called = FALSE;
5045 NtCurrentTeb()->Peb->BeingDebugged = 1;
5046
5047 pRtlRaiseException(&record);
5048
5049 ok(got_exception, "Handler was not called.\n");
5050 ok(hook_called, "Hook was not called.\n");
5051
5052 ok(hook_exception_address == (BYTE *)hook_KiUserExceptionDispatcher_rip + !is_arm64ec
5053 || broken(!hook_exception_address) /* 2008 */, "Got unexpected addresses %p, %p.\n",
5054 hook_KiUserExceptionDispatcher_rip, hook_exception_address);
5055
5056 RemoveVectoredExceptionHandler(vectored_handler);
5057
5058 memcpy(pKiUserExceptionDispatcher, patched_KiUserExceptionDispatcher_bytes,
5059 sizeof(patched_KiUserExceptionDispatcher_bytes));
5060 got_exception = 0;
5061 hook_called = FALSE;
5062
5063 run_exception_test(dbg_except_continue_handler, NULL, except_code, sizeof(except_code), PAGE_EXECUTE_READ);
5064
5065 ok(got_exception, "Handler was not called.\n");
5066 ok(hook_called, "Hook was not called.\n");
5067 ok(hook_KiUserExceptionDispatcher_rip == bpt_address, "Got unexpected exception address %p, expected %p.\n",
5068 hook_KiUserExceptionDispatcher_rip, bpt_address);
5069 ok(dbg_except_continue_handler_rip == bpt_address, "Got unexpected exception address %p, expected %p.\n",
5070 dbg_except_continue_handler_rip, bpt_address);
5071
5072 ok(test_kiuserexceptiondispatcher_regs.new_rax == 0xdeadbeef, "Got unexpected rax %#Ix.\n",
5073 test_kiuserexceptiondispatcher_regs.new_rax);
5074 ok(test_kiuserexceptiondispatcher_regs.old_rsi
5075 == test_kiuserexceptiondispatcher_regs.new_rsi, "rsi does not match.\n");
5076 ok(test_kiuserexceptiondispatcher_regs.old_rdi
5077 == test_kiuserexceptiondispatcher_regs.new_rdi, "rdi does not match.\n");
5078 ok(test_kiuserexceptiondispatcher_regs.old_rbp
5079 == test_kiuserexceptiondispatcher_regs.new_rbp, "rbp does not match.\n");
5080
5081 NtCurrentTeb()->Peb->BeingDebugged = 0;
5082
5083 vectored_handler = AddVectoredExceptionHandler(TRUE, dbg_except_continue_vectored_handler);
5084 pass = 0;
5086 pRtlCaptureContext(&ctx);
5087 if (InterlockedIncrement(&pass) == 2) /* interlocked to prevent compiler from moving before capture */
5088 {
5089 memcpy(pKiUserExceptionDispatcher, patched_KiUserExceptionDispatcher_bytes,
5090 sizeof(patched_KiUserExceptionDispatcher_bytes));
5091 got_exception = 0;
5092 hook_called = FALSE;
5093
5094 record.ExceptionCode = 0xceadbeef;
5095 test_kiuserexceptiondispatcher_saved_r12 = ctx.R12;
5096 ctx.R12 = (ULONG64)0xdeadbeeffeedcafe;
5097
5098#if defined(__REACTOS__) && defined(_MSC_VER)
5099 Call_NtRaiseException(&record, &ctx, TRUE, pNtRaiseException);
5100#else
5101#ifdef __GNUC__
5102 /* Spoil r12 value to make sure it doesn't come from the current userspace registers. */
5103 __asm__ volatile("movq $0xdeadcafe, %%r12" : : : "%r12");
5104#endif
5105 pNtRaiseException(&record, &ctx, TRUE);
5106#endif
5107 ok(0, "Shouldn't be reached.\n");
5108 }
5109 else
5110 {
5111 ok(pass == 3, "Got unexpected pass %ld.\n", pass);
5112 }
5113 ok(hook_called, "Hook was not called.\n");
5114 RemoveVectoredExceptionHandler(vectored_handler);
5115
5116 ret = VirtualProtect(pKiUserExceptionDispatcher, sizeof(saved_KiUserExceptionDispatcher_bytes),
5117 old_protect, &old_protect);
5118 ok(ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError());
5119}
5120
5121
5122static BYTE saved_KiUserApcDispatcher[12];
5123
5124static void * WINAPI hook_KiUserApcDispatcher(CONTEXT *context)
5125{
5126 struct machine_frame *frame = (struct machine_frame *)(context + 1);
5127 UINT i;
5128
5129 trace( "context %p, context->Rip %#Ix, context->Rsp %#Ix (%#Ix), ContextFlags %#lx.\n",
5130 context, context->Rip, context->Rsp,
5131 (char *)context->Rsp - (char *)context, context->ContextFlags );
5132
5133#ifndef __REACTOS__ // Broken on Windows Vista and Windows 10
5134 ok( context->P1Home == 0x1234, "wrong p1 %#Ix\n", context->P1Home );
5135#endif
5136 ok( context->P2Home == 0x5678, "wrong p2 %#Ix\n", context->P2Home );
5137 ok( context->P3Home == 0xdeadbeef, "wrong p3 %#Ix\n", context->P3Home );
5138 ok( context->P4Home == (ULONG_PTR)apc_func, "wrong p4 %#Ix / %p\n", context->P4Home, apc_func );
5139
5140 /* machine frame offset varies between Windows versions */
5141 for (i = 0; i < 16; i++)
5142 {
5143 if (frame->rip == context->Rip) break;
5144 frame = (struct machine_frame *)((ULONG64 *)frame + 2);
5145 }
5146 ok( frame->rip == context->Rip, "wrong rip %#Ix / %#Ix\n", frame->rip, context->Rip );
5147 ok( frame->rsp == context->Rsp, "wrong rsp %#Ix / %#Ix\n", frame->rsp, context->Rsp );
5148
5149 hook_called = TRUE;
5150 memcpy( pKiUserApcDispatcher, saved_KiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher));
5151 return pKiUserApcDispatcher;
5152}
5153
5154static void test_KiUserApcDispatcher(void)
5155{
5156 BYTE hook_trampoline[] =
5157 {
5158 0x48, 0x89, 0xe1, /* mov %rsp,%rcx */
5159 0x48, 0xb8, /* movabs hook_KiUserApcDispatcher,%rax */
5160 0,0,0,0,0,0,0,0, /* offset 5 */
5161 0xff, 0xd0, /* callq *rax */
5162 0xff, 0xe0, /* jmpq *rax */
5163 };
5164
5165 BYTE patched_KiUserApcDispatcher[12];
5166 DWORD old_protect;
5167 BYTE *ptr;
5168 BOOL ret;
5169
5170 *(ULONG_PTR *)(hook_trampoline + 5) = (ULONG_PTR)hook_KiUserApcDispatcher;
5171 memcpy(code_mem, hook_trampoline, sizeof(hook_trampoline));
5172
5173 ret = VirtualProtect( pKiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher),
5174 PAGE_EXECUTE_READWRITE, &old_protect );
5175 ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() );
5176
5177 memcpy( saved_KiUserApcDispatcher, pKiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher) );
5178 ptr = patched_KiUserApcDispatcher;
5179 /* mov $code_mem, %rax */
5180 *ptr++ = 0x48;
5181 *ptr++ = 0xb8;
5182 *(void **)ptr = code_mem;
5183 ptr += sizeof(ULONG64);
5184 /* jmp *rax */
5185 *ptr++ = 0xff;
5186 *ptr++ = 0xe0;
5187 memcpy( pKiUserApcDispatcher, patched_KiUserApcDispatcher, sizeof(patched_KiUserApcDispatcher) );
5188
5189 hook_called = FALSE;
5190 apc_count = 0;
5191 pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef );
5192 SleepEx( 0, TRUE );
5193 ok( apc_count == 1, "APC was not called\n" );
5194 /* hooking is bypassed on arm64ec */
5195 ok( is_arm64ec ? !hook_called : hook_called, "hook was not called\n" );
5196
5197 VirtualProtect( pKiUserApcDispatcher, sizeof(saved_KiUserApcDispatcher), old_protect, &old_protect );
5198}
5199
5200static void WINAPI hook_KiUserCallbackDispatcher(void *rsp)
5201{
5202 struct
5203 {
5204 ULONG64 padding[4];
5205 void *args;
5206 ULONG len;
5207 ULONG id;
5208 struct machine_frame frame;
5209 BYTE args_data[0];
5210 } *stack = rsp;
5211
5212 KERNEL_CALLBACK_PROC func = NtCurrentTeb()->Peb->KernelCallbackTable[stack->id];
5213
5214 trace( "rsp %p args %p (%#Ix) len %lu id %lu\n", stack, stack->args,
5215 (char *)stack->args - (char *)stack, stack->len, stack->id );
5216
5217 ok( !((ULONG_PTR)stack & 15), "unaligned stack %p\n", stack );
5218 ok( stack->args == stack->args_data, "wrong args %p / %p\n", stack->args, stack->args_data );
5219 ok( (BYTE *)stack->frame.rsp - &stack->args_data[stack->len] <= 16, "wrong rsp %p / %p\n",
5220 (void *)stack->frame.rsp, &stack->args_data[stack->len] );
5221
5222 if (stack->frame.rip && pRtlPcToFileHeader)
5223 {
5224 void *mod, *win32u = GetModuleHandleA("win32u.dll");
5225
5226 pRtlPcToFileHeader( (void *)stack->frame.rip, &mod );
5227 if (win32u) ok( mod == win32u, "ret address %Ix not in win32u %p\n", stack->frame.rip, win32u );
5228 else trace( "ret address %Ix in %p\n", stack->frame.rip, mod );
5229 }
5230 NtCallbackReturn( NULL, 0, func( stack->args, stack->len ));
5231}
5232
5233static void test_KiUserCallbackDispatcher(void)
5234{
5235 BYTE hook_trampoline[] =
5236 {
5237 0x48, 0x89, 0xe1, /* mov %rsp,%rcx */
5238 0x48, 0xb8, /* movabs hook_KiUserCallbackDispatcher,%rax */
5239 0,0,0,0,0,0,0,0, /* offset 5 */
5240 0xff, 0xd0, /* callq *rax */
5241 };
5242
5243 BYTE saved_KiUserCallbackDispatcher[12];
5244 BYTE patched_KiUserCallbackDispatcher[12];
5245 DWORD old_protect;
5246 BYTE *ptr;
5247 BOOL ret;
5248
5249 *(ULONG_PTR *)(hook_trampoline + 5) = (ULONG_PTR)hook_KiUserCallbackDispatcher;
5250 memcpy(code_mem, hook_trampoline, sizeof(hook_trampoline));
5251
5252 ret = VirtualProtect( pKiUserCallbackDispatcher, sizeof(saved_KiUserCallbackDispatcher),
5253 PAGE_EXECUTE_READWRITE, &old_protect );
5254 ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() );
5255
5256 memcpy( saved_KiUserCallbackDispatcher, pKiUserCallbackDispatcher, sizeof(saved_KiUserCallbackDispatcher) );
5257 ptr = patched_KiUserCallbackDispatcher;
5258 /* mov $code_mem, %rax */
5259 *ptr++ = 0x48;
5260 *ptr++ = 0xb8;
5261 *(void **)ptr = code_mem;
5262 ptr += sizeof(ULONG64);
5263 /* jmp *rax */
5264 *ptr++ = 0xff;
5265 *ptr++ = 0xe0;
5266 memcpy( pKiUserCallbackDispatcher, patched_KiUserCallbackDispatcher, sizeof(patched_KiUserCallbackDispatcher) );
5267
5268 DestroyWindow( CreateWindowA( "Static", "test", 0, 0, 0, 0, 0, 0, 0, 0, 0 ));
5269
5270 memcpy( pKiUserCallbackDispatcher, saved_KiUserCallbackDispatcher, sizeof(saved_KiUserCallbackDispatcher));
5271 VirtualProtect( pKiUserCallbackDispatcher, sizeof(saved_KiUserCallbackDispatcher), old_protect, &old_protect );
5272}
5273
5274static BOOL got_nested_exception, got_prev_frame_exception;
5275static void *nested_exception_initial_frame;
5276
5277static DWORD nested_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
5279{
5280 trace("nested_exception_handler Rip %p, Rsp %p, code %#lx, flags %#lx, ExceptionAddress %p.\n",
5281 (void *)context->Rip, (void *)context->Rsp, rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress);
5282
5283 if (rec->ExceptionCode == 0x80000003
5285 {
5286 ok(rec->NumberParameters == 1, "Got unexpected rec->NumberParameters %lu.\n", rec->NumberParameters);
5287 ok((void *)context->Rsp == frame, "Got unexpected frame %p.\n", frame);
5288 ok(*(void **)frame == (char *)code_mem + 5, "Got unexpected *frame %p.\n", *(void **)frame);
5289 ok(context->Rip == (ULONG_PTR)((char *)code_mem + 7), "Got unexpected Rip %#Ix.\n", context->Rip);
5290
5291 nested_exception_initial_frame = frame;
5292 RaiseException(0xdeadbeef, 0, 0, 0);
5293 ++context->Rip;
5295 }
5296
5297 if (rec->ExceptionCode == 0xdeadbeef && (rec->ExceptionFlags == EXCEPTION_NESTED_CALL
5299 {
5300 ok(!rec->NumberParameters, "Got unexpected rec->NumberParameters %lu.\n", rec->NumberParameters);
5301 got_nested_exception = TRUE;
5302 ok(frame == nested_exception_initial_frame, "Got unexpected frame %p.\n", frame);
5304 }
5305
5306 ok(rec->ExceptionCode == 0xdeadbeef && (!rec->ExceptionFlags || rec->ExceptionFlags == EXCEPTION_SOFTWARE_ORIGINATE),
5307 "Got unexpected exception code %#lx, flags %#lx.\n", rec->ExceptionCode, rec->ExceptionFlags);
5308 ok(!rec->NumberParameters, "Got unexpected rec->NumberParameters %lu.\n", rec->NumberParameters);
5309 ok(frame == (void *)((BYTE *)nested_exception_initial_frame + 8),
5310 "Got unexpected frame %p.\n", frame);
5311 got_prev_frame_exception = TRUE;
5313}
5314
5315static const BYTE nested_except_code[] =
5316{
5317 0xe8, 0x02, 0x00, 0x00, 0x00, /* call nest */
5318 0x90, /* nop */
5319 0xc3, /* ret */
5320 /* nest: */
5321 0xcc, /* int3 */
5322 0x90, /* nop */
5323 0xc3, /* ret */
5324};
5325
5326static void test_nested_exception(void)
5327{
5328 got_nested_exception = got_prev_frame_exception = FALSE;
5329 run_exception_test(nested_exception_handler, NULL, nested_except_code, sizeof(nested_except_code), PAGE_EXECUTE_READ);
5330 ok(got_nested_exception, "Did not get nested exception.\n");
5331 ok(got_prev_frame_exception, "Did not get nested exception in the previous frame.\n");
5332}
5333
5334static unsigned int collided_unwind_exception_count;
5335
5336static DWORD collided_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
5338{
5339 CONTEXT ctx;
5340
5341 trace("collided_exception_handler Rip %p, Rsp %p, code %#lx, flags %#lx, ExceptionAddress %p, frame %p.\n",
5342 (void *)context->Rip, (void *)context->Rsp, rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, frame);
5343
5344 switch(collided_unwind_exception_count++)
5345 {
5346 case 0:
5347 /* Initial exception from nested_except_code. */
5348 ok(rec->ExceptionCode == STATUS_BREAKPOINT, "got %#lx.\n", rec->ExceptionCode);
5349 nested_exception_initial_frame = frame;
5350 /* Start unwind. */
5351 pRtlUnwindEx((char *)nested_exception_initial_frame + 8, (char *)code_mem + 5, NULL, NULL, &ctx, NULL);
5352 ok(0, "shouldn't be reached\n");
5353 break;
5354 case 1:
5355 ok(rec->ExceptionCode == STATUS_UNWIND, "got %#lx.\n", rec->ExceptionCode);
5356 ok(rec->ExceptionFlags == EXCEPTION_UNWINDING, "got %#lx.\n", rec->ExceptionFlags);
5357 ok((char *)context->Rip == (char *)code_mem + 7, "got %p.\n", rec->ExceptionAddress);
5358 /* generate exception in unwind handler. */
5359 RaiseException(0xdeadbeef, 0, 0, 0);
5360 ok(0, "shouldn't be reached\n");
5361 break;
5362 case 2:
5363 /* Inner call frame, continue search. */
5364 ok(rec->ExceptionCode == 0xdeadbeef, "got %#lx.\n", rec->ExceptionCode);
5365 ok(!rec->ExceptionFlags || rec->ExceptionFlags == EXCEPTION_SOFTWARE_ORIGINATE, "got %#lx.\n", rec->ExceptionFlags);
5366 ok(frame == nested_exception_initial_frame, "got %p, expected %p.\n", frame, nested_exception_initial_frame);
5367 break;
5368 case 3:
5369 /* Top level call frame, handle exception by unwinding. */
5370 ok(rec->ExceptionCode == 0xdeadbeef, "got %#lx.\n", rec->ExceptionCode);
5371 ok(!rec->ExceptionFlags || rec->ExceptionFlags == EXCEPTION_SOFTWARE_ORIGINATE, "got %#lx.\n", rec->ExceptionFlags);
5372 ok((char *)frame == (char *)nested_exception_initial_frame + 8, "got %p, expected %p.\n", frame, nested_exception_initial_frame);
5373 pRtlUnwindEx((char *)nested_exception_initial_frame + 8, (char *)code_mem + 5, NULL, NULL, &ctx, NULL);
5374 ok(0, "shouldn't be reached\n");
5375 break;
5376 case 4:
5377 /* Collided unwind. */
5378 ok(rec->ExceptionCode == STATUS_UNWIND, "got %#lx.\n", rec->ExceptionCode);
5380 ok(frame == nested_exception_initial_frame, "got %p, expected %p.\n", frame, nested_exception_initial_frame);
5381 break;
5382 case 5:
5383 /* EXCEPTION_COLLIDED_UNWIND cleared for the following frames. */
5384 ok(rec->ExceptionCode == STATUS_UNWIND, "got %#lx.\n", rec->ExceptionCode);
5386 ok((char *)frame == (char *)nested_exception_initial_frame + 8, "got %p, expected %p.\n", frame,
5387 (char *)nested_exception_initial_frame + 8);
5388 break;
5389 }
5391}
5392
5393static void test_collided_unwind(void)
5394{
5395 got_nested_exception = got_prev_frame_exception = FALSE;
5396 collided_unwind_exception_count = 0;
5397 run_exception_test_flags(collided_exception_handler, NULL, nested_except_code, sizeof(nested_except_code),
5398 PAGE_EXECUTE_READ, UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER);
5399 ok(collided_unwind_exception_count == 6, "got %u.\n", collided_unwind_exception_count);
5400}
5401
5402static CONTEXT test_unwind_apc_context;
5403static BOOL test_unwind_apc_called;
5404
5405static void CALLBACK test_unwind_apc(ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3)
5406{
5407 EXCEPTION_RECORD rec;
5408
5409 test_unwind_apc_called = TRUE;
5410 memset(&rec, 0, sizeof(rec));
5411 pRtlUnwind((void *)test_unwind_apc_context.Rsp, (void *)test_unwind_apc_context.Rip, &rec, (void *)0xdeadbeef);
5412 ok(0, "Should not get here.\n");
5413}
5414
5415static void test_unwind_from_apc(void)
5416{
5418 LONG pass;
5419
5420 if (!pNtQueueApcThread)
5421 {
5422 win_skip("NtQueueApcThread is not available.\n");
5423 return;
5424 }
5425
5426 pass = 0;
5428 RtlCaptureContext(&test_unwind_apc_context);
5430
5431 if (pass == 2)
5432 {
5433 test_unwind_apc_called = FALSE;
5434 status = pNtQueueApcThread(GetCurrentThread(), test_unwind_apc, 0, 0, 0);
5435 ok(!status, "Got unexpected status %#lx.\n", status);
5436 SleepEx(0, TRUE);
5437 ok(0, "Should not get here.\n");
5438 }
5439 if (pass == 3)
5440 {
5441 ok(test_unwind_apc_called, "Test user APC was not called.\n");
5442 test_unwind_apc_called = FALSE;
5443 status = pNtQueueApcThread(GetCurrentThread(), test_unwind_apc, 0, 0, 0);
5444 ok(!status, "Got unexpected status %#lx.\n", status);
5445 NtContinue(&test_unwind_apc_context, TRUE );
5446 ok(0, "Should not get here.\n");
5447 }
5448 ok(pass == 4, "Got unexpected pass %ld.\n", pass);
5449 ok(test_unwind_apc_called, "Test user APC was not called.\n");
5450}
5451
5452static void test_syscall_clobbered_regs(void)
5453{
5454 struct regs
5455 {
5456 UINT64 rcx;
5457 UINT64 r10;
5458 UINT64 r11;
5459 UINT32 eflags;
5460 };
5461 static const BYTE code[] =
5462 {
5463 0x48, 0x8d, 0x05, 0x00, 0x10, 0x00, 0x00,
5464 /* leaq 0x1000(%rip),%rax */
5465 0x48, 0x25, 0x00, 0xf0, 0xff, 0xff,
5466 /* andq $~0xfff,%rax */
5467 0x48, 0x83, 0xe8, 0x08, /* subq $8,%rax */
5468 0x48, 0x89, 0x20, /* movq %rsp,0(%rax) */
5469 0x48, 0x89, 0xc4, /* movq %rax,%rsp */
5470 0xfd, /* std */
5471 0x45, 0x31, 0xdb, /* xorl %r11d,%r11d */
5472 0x41, 0x50, /* push %r8 */
5473 0x53, 0x55, 0x57, 0x56, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57,
5474 /* push %rbx, %rbp, %rdi, %rsi, %r12, %r13, %r14, %r15 */
5475 0x49, 0xba, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00,
5476 /* movabs $0xdeadbeef,%r10 */
5477 0x41, 0xff, 0xd1, /* callq *r9 */
5478 0x41, 0x5f, 0x41, 0x5e, 0x41, 0x5d, 0x41, 0x5c, 0x5e, 0x5f, 0x5d, 0x5b,
5479 /* pop %r15, %r14, %r13, %r12, %rsi, %rdi, %rbp, %rbx */
5480 0x41, 0x58, /* pop %r8 */
5481 0x49, 0x89, 0x48, 0x00, /* mov %rcx,(%r8) */
5482 0x4d, 0x89, 0x50, 0x08, /* mov %r10,0x8(%r8) */
5483 0x4d, 0x89, 0x58, 0x10, /* mov %r11,0x10(%r8) */
5484 0x9c, /* pushfq */
5485 0x59, /* pop %rcx */
5486 0xfc, /* cld */
5487 0x41, 0x89, 0x48, 0x18, /* mov %ecx,0x18(%r8) */
5488 0x5c, /* pop %rsp */
5489 0xc3, /* ret */
5490 };
5491
5492 NTSTATUS (WINAPI *func)(void *arg1, void *arg2, struct regs *, void *call_addr);
5493 NTSTATUS (WINAPI *pNtCancelTimer)(HANDLE, BOOLEAN *);
5494 struct regs regs;
5497
5498 if (is_arm64ec) return; /* arm64ec register handling is different */
5499
5500 pNtCancelTimer = (void *)GetProcAddress(hntdll, "NtCancelTimer");
5501 ok(!!pNtCancelTimer, "NtCancelTimer not found.\n");
5502 memcpy(code_mem, code, sizeof(code));
5503 func = code_mem;
5504 memset(&regs, 0, sizeof(regs));
5505 status = func((HANDLE)0xdeadbeef, NULL, &regs, pNtCancelTimer);
5506 ok(status == STATUS_INVALID_HANDLE, "Got unexpected status %#lx.\n", status);
5507 ok(regs.r11 == regs.eflags, "Expected r11 (%#I64x) to equal EFLAGS (%#x).\n", regs.r11, regs.eflags);
5508 ok(regs.r10 != regs.rcx, "got %#I64x.\n", regs.r10);
5509
5510 /* After the syscall instruction rcx contains the address of the instruction next after syscall. */
5511 ok((BYTE *)regs.rcx > (BYTE *)pNtCancelTimer && (BYTE *)regs.rcx < (BYTE *)pNtCancelTimer + 0x20,
5512 "Got unexpected rcx %s, pNtCancelTimer %p.\n", wine_dbgstr_longlong(regs.rcx), pNtCancelTimer);
5513
5514 status = func((HANDLE)0xdeadbeef, (BOOLEAN *)0xdeadbeef, &regs, pNtCancelTimer);
5515 ok(status == STATUS_ACCESS_VIOLATION, "Got unexpected status %#lx.\n", status);
5516 ok((BYTE *)regs.rcx > (BYTE *)pNtCancelTimer && (BYTE *)regs.rcx < (BYTE *)pNtCancelTimer + 0x20,
5517 "Got unexpected rcx %s, pNtCancelTimer %p.\n", wine_dbgstr_longlong(regs.rcx), pNtCancelTimer);
5518 ok(regs.r11 == regs.eflags, "Expected r11 (%#I64x) to equal EFLAGS (%#x).\n", regs.r11, regs.eflags);
5519 ok(regs.r10 != regs.rcx, "got %#I64x.\n", regs.r10);
5520
5521 context.ContextFlags = CONTEXT_CONTROL;
5522 status = func(GetCurrentThread(), &context, &regs, pNtGetContextThread);
5523 ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status);
5524 ok((BYTE *)regs.rcx > (BYTE *)pNtGetContextThread && (BYTE *)regs.rcx < (BYTE *)pNtGetContextThread + 0x20,
5525 "Got unexpected rcx %s, pNtGetContextThread %p.\n", wine_dbgstr_longlong(regs.rcx), pNtGetContextThread);
5526 ok(regs.r11 == regs.eflags, "Expected r11 (%#I64x) to equal EFLAGS (%#x).\n", regs.r11, regs.eflags);
5527 ok(regs.r10 != regs.rcx, "got %#I64x.\n", regs.r10);
5528
5529 status = func(GetCurrentThread(), &context, &regs, pNtSetContextThread);
5530 ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status);
5531 ok((BYTE *)regs.rcx > (BYTE *)pNtGetContextThread && (BYTE *)regs.rcx < (BYTE *)pNtGetContextThread + 0x20,
5532 "Got unexpected rcx %s, pNtGetContextThread %p.\n", wine_dbgstr_longlong(regs.rcx), pNtGetContextThread);
5533 ok((regs.r11 | 0x2) == regs.eflags, "Expected r11 (%#I64x) | 0x2 to equal EFLAGS (%#x).\n", regs.r11, regs.eflags);
5534 ok(regs.r10 != regs.rcx, "got %#I64x.\n", regs.r10);
5535
5536 context.ContextFlags = CONTEXT_INTEGER;
5537 status = func(GetCurrentThread(), &context, &regs, pNtGetContextThread);
5538 ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status);
5539 ok((BYTE *)regs.rcx > (BYTE *)pNtGetContextThread && (BYTE *)regs.rcx < (BYTE *)pNtGetContextThread + 0x20,
5540 "Got unexpected rcx %s, pNtGetContextThread %p.\n", wine_dbgstr_longlong(regs.rcx), pNtGetContextThread);
5541 ok(regs.r11 == regs.eflags, "Expected r11 (%#I64x) to equal EFLAGS (%#x).\n", regs.r11, regs.eflags);
5542 ok(regs.r10 != regs.rcx, "got %#I64x.\n", regs.r10);
5543
5544 status = func(GetCurrentThread(), &context, &regs, pNtSetContextThread);
5545 ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status);
5546 ok((BYTE *)regs.rcx > (BYTE *)pNtSetContextThread && (BYTE *)regs.rcx < (BYTE *)pNtSetContextThread + 0x20,
5547 "Got unexpected rcx %s, pNtSetContextThread %p.\n", wine_dbgstr_longlong(regs.rcx), pNtSetContextThread);
5548 ok(regs.r11 == regs.eflags, "Expected r11 (%#I64x) to equal EFLAGS (%#x).\n", regs.r11, regs.eflags);
5549 ok(regs.r10 != regs.rcx, "got %#I64x.\n", regs.r10);
5550}
5551
5552static CONTEXT test_raiseexception_regs_context;
5553static LONG CALLBACK test_raiseexception_regs_handle(EXCEPTION_POINTERS *exception_info)
5554{
5555 EXCEPTION_RECORD *rec = exception_info->ExceptionRecord;
5556 unsigned int i;
5557
5558 test_raiseexception_regs_context = *exception_info->ContextRecord;
5560 ok(rec->ExceptionCode == 0xdeadbeaf, "got %#lx.\n", rec->ExceptionCode);
5561 ok(!rec->ExceptionRecord, "got %p.\n", rec->ExceptionRecord);
5562 ok(!rec->ExceptionFlags || rec->ExceptionFlags == EXCEPTION_SOFTWARE_ORIGINATE, "got %#lx.\n", rec->ExceptionFlags);
5563 for (i = 0; i < rec->NumberParameters; ++i)
5564 ok(rec->ExceptionInformation[i] == i, "got %Iu, i %u.\n", rec->ExceptionInformation[i], i);
5566}
5567
5568static void test_raiseexception_regs(void)
5569{
5570 static const BYTE code[] =
5571 {
5572 0xb8, 0x00, 0xb0, 0xad, 0xde, /* mov $0xdeadb000,%eax */
5573 0x53, /* push %rbx */
5574 0x48, 0x89, 0xc3, /* mov %rax,%rbx */
5575 0x56, /* push %rsi */
5576 0x48, 0xff, 0xc0, /* inc %rax */
5577 0x48, 0x89, 0xc6, /* mov %rax,%rsi */
5578 0x57, /* push %rdi */
5579 0x48, 0xff, 0xc0, /* inc %rax */
5580 0x48, 0x89, 0xc7, /* mov %rax,%rdi */
5581 0x55, /* push %rbp */
5582 0x48, 0xff, 0xc0, /* inc %rax */
5583 0x48, 0x89, 0xc5, /* mov %rax,%rbp */
5584 0x41, 0x54, /* push %r12 */
5585 0x48, 0xff, 0xc0, /* inc %rax */
5586 0x49, 0x89, 0xc4, /* mov %rax,%r12 */
5587 0x41, 0x55, /* push %r13 */
5588 0x48, 0xff, 0xc0, /* inc %rax */
5589 0x49, 0x89, 0xc5, /* mov %rax,%r13 */
5590 0x41, 0x56, /* push %r14 */
5591 0x48, 0xff, 0xc0, /* inc %rax */
5592 0x49, 0x89, 0xc6, /* mov %rax,%r14 */
5593 0x41, 0x57, /* push %r15 */
5594 0x48, 0xff, 0xc0, /* inc %rax */
5595 0x49, 0x89, 0xc7, /* mov %rax,%r15 */
5596
5597 0x50, /* push %rax */ /* align stack */
5598 0x48, 0x89, 0xc8, /* mov %rcx,%rax */
5599 0xb9, 0xaf, 0xbe, 0xad, 0xde, /* mov $0xdeadbeaf,%ecx */
5600 0xff, 0xd0, /* call *%rax */
5601 0x58, /* pop %rax */
5602
5603 0x41, 0x5f, /* pop %r15 */
5604 0x41, 0x5e, /* pop %r14 */
5605 0x41, 0x5d, /* pop %r13 */
5606 0x41, 0x5c, /* pop %r12 */
5607 0x5d, /* pop %rbp */
5608 0x5f, /* pop %rdi */
5609 0x5e, /* pop %rsi */
5610 0x5b, /* pop %rbx */
5611 0xc3, /* ret */
5612 };
5613 void (WINAPI *pRaiseException)( DWORD code, DWORD flags, DWORD count, const ULONG_PTR *args ) = RaiseException;
5614 void (WINAPI *func)(void *raise_exception, DWORD flags, DWORD count, const ULONG_PTR *args);
5615 void *vectored_handler;
5616 ULONG_PTR args[20];
5618 unsigned int i;
5619
5620 vectored_handler = AddVectoredExceptionHandler(TRUE, test_raiseexception_regs_handle);
5621 ok(!!vectored_handler, "failed.\n");
5622
5623 memcpy(code_mem, code, sizeof(code));
5624 func = code_mem;
5625
5626 for (i = 0; i < ARRAY_SIZE(args); ++i)
5627 args[i] = i;
5628
5629 func(pRaiseException, 0, ARRAY_SIZE(args), args);
5630 expected = 0xdeadb000;
5631 ok(test_raiseexception_regs_context.Rbx == expected, "got %#I64x.\n", test_raiseexception_regs_context.Rbx);
5632 ++expected;
5633 ok(test_raiseexception_regs_context.Rsi == expected, "got %#I64x.\n", test_raiseexception_regs_context.Rsi);
5634 ++expected;
5635 ok(test_raiseexception_regs_context.Rdi == expected, "got %#I64x.\n", test_raiseexception_regs_context.Rdi);
5636 ++expected;
5637 ok(test_raiseexception_regs_context.Rbp == expected || is_arm64ec /* x29 modified by entry thunk */,
5638 "got %#I64x.\n", test_raiseexception_regs_context.Rbp);
5639 ++expected;
5640 ok(test_raiseexception_regs_context.R12 == expected, "got %#I64x.\n", test_raiseexception_regs_context.R12);
5641 ++expected;
5642 ok(test_raiseexception_regs_context.R13 == expected, "got %#I64x.\n", test_raiseexception_regs_context.R13);
5643 ++expected;
5644 ok(test_raiseexception_regs_context.R14 == expected, "got %#I64x.\n", test_raiseexception_regs_context.R14);
5645 ++expected;
5646 ok(test_raiseexception_regs_context.R15 == expected, "got %#I64x.\n", test_raiseexception_regs_context.R15);
5647
5648 RemoveVectoredExceptionHandler(vectored_handler);
5649}
5650
5651static LONG CALLBACK test_instrumentation_callback_handler( EXCEPTION_POINTERS *exception_info )
5652{
5653 EXCEPTION_RECORD *rec = exception_info->ExceptionRecord;
5654 CONTEXT *c = exception_info->ContextRecord;
5655
5656 if (rec->ExceptionCode == EXCEPTION_BREAKPOINT) ++c->Rip;
5658}
5659
5660static HANDLE instrumentation_callback_thread_ready, instrumentation_callback_thread_wait;
5661
5662static DWORD WINAPI test_instrumentation_callback_thread( void *arg )
5663{
5664 SetEvent( instrumentation_callback_thread_ready );
5665 NtWaitForSingleObject( instrumentation_callback_thread_wait, FALSE, NULL );
5666
5667 SetEvent( instrumentation_callback_thread_ready );
5668 NtWaitForSingleObject( instrumentation_callback_thread_wait, FALSE, NULL );
5669 return 0;
5670}
5671
5672struct instrumentation_callback_data
5673{
5674 unsigned int call_count;
5675 struct
5676 {
5677 char *r10;
5678 char *rcx;
5679 }
5680 call_data[256];
5681};
5682
5683static void init_instrumentation_data(struct instrumentation_callback_data *d)
5684{
5685 memset( d, 0xcc, sizeof(*d) );
5686 d->call_count = 0;
5687}
5688
5689static void test_instrumentation_callback(void)
5690{
5691 static const BYTE instrumentation_callback[] =
5692 {
5693 0x50, 0x52, /* push %rax, %rdx */
5694
5695 0x48, 0xba, /* movabs instrumentation_call_count, %rdx */
5696 /* &instrumentation_call_count, offset 4 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
5697 0xb8, 0x01, 0x00, 0x00, 0x00, /* mov $0x1,%eax */
5698 0xf0, 0x0f, 0xc1, 0x02, /* lock xadd %eax,(%rdx) */
5699 0x0f, 0xb6, 0xc0, /* movzx %al,%eax */
5700 0x48, 0xba, /* movabs instrumentation_call_data, %rdx */
5701 /* instrumentation_call_data, offset 26 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
5702 0x48, 0x01, 0xc0, /* add %rax,%rax */
5703 0x48, 0x8d, 0x14, 0xc2, /* lea (%rdx,%rax,8),%rdx */
5704 0x4c, 0x89, 0x12, /* mov %r10,(%rdx) */
5705 0x48, 0x89, 0x4a, 0x08, /* mov %rcx,0x8(%rdx) */
5706
5707 0x5a, 0x58, /* pop %rdx, %rax */
5708 0x41, 0xff, 0xe2, /* jmp *r10 */
5709 };
5710
5711 struct instrumentation_callback_data curr_data, data;
5713 HMODULE ntdll = GetModuleHandleA( "ntdll.dll" );
5714 void *pLdrInitializeThunk;
5716 void *vectored_handler;
5717 unsigned int i, count;
5719 HANDLE thread;
5720 CONTEXT ctx;
5721 HWND hwnd;
5722 LONG pass;
5723
5724 if (is_arm64ec) return;
5725
5726 memcpy( code_mem, instrumentation_callback, sizeof(instrumentation_callback) );
5727 *(void **)((char *)code_mem + 4) = &curr_data.call_count;
5728 *(void **)((char *)code_mem + 26) = curr_data.call_data;
5729
5730 memset(&info, 0, sizeof(info));
5731 info.Callback = code_mem;
5732 init_instrumentation_data( &curr_data );
5734 data = curr_data;
5736 || broken( status == STATUS_PRIVILEGE_NOT_HELD ) /* some versions and machines before Win10 */,
5737 "got %#lx.\n", status );
5738 /* If instrumentation callback is not yet set during syscall entry it won't be called on exit. */
5739 ok( !data.call_count, "got %u.\n", data.call_count );
5740 if (status)
5741 {
5742 win_skip( "Failed setting instrumenation callback.\n" );
5743 return;
5744 }
5745
5746 init_instrumentation_data( &curr_data );
5747 memset( &info, 0xcc, sizeof(info) );
5749 data = curr_data;
5750 ok( status == STATUS_INVALID_INFO_CLASS, "got %#lx.\n", status );
5751 ok( data.call_count == 1, "got %u.\n", data.call_count );
5752 ok( data.call_data[0].r10 >= (char *)NtQueryInformationProcess
5753 && data.call_data[0].r10 < (char *)NtQueryInformationProcess + 0x20,
5754 "got %p, NtQueryInformationProcess %p.\n", data.call_data[0].r10, NtQueryInformationProcess );
5755 ok( data.call_data[0].rcx != data.call_data[0].r10, "got %p.\n", data.call_data[0].rcx );
5756
5757 memset(&info, 0, sizeof(info));
5758 info.Callback = code_mem;
5759 init_instrumentation_data( &curr_data );
5761 data = curr_data;
5762 ok( status == STATUS_SUCCESS, "got %#lx.\n", status );
5763 ok( data.call_count == 1, "got %u.\n", data.call_count );
5764
5765 vectored_handler = AddVectoredExceptionHandler( TRUE, test_instrumentation_callback_handler );
5766 ok( !!vectored_handler, "failed.\n" );
5767 init_instrumentation_data( &curr_data );
5768 DbgBreakPoint();
5769 data = curr_data;
5770 ok( data.call_count == 1 || broken( data.call_count == 2 ) /* before Win10 1809 */, "got %u.\n", data.call_count );
5771 ok( data.call_data[0].r10 == pKiUserExceptionDispatcher, "got %p, expected %p.\n", data.call_data[0].r10,
5773
5774 pass = 0;
5776 pRtlCaptureContext( &ctx );
5777 if (InterlockedIncrement( &pass ) == 2) /* interlocked to prevent compiler from moving before capture */
5778 {
5779 record.ExceptionCode = 0xceadbeef;
5780 record.NumberParameters = 0;
5781 init_instrumentation_data( &curr_data );
5782 status = pNtRaiseException( &record, &ctx, TRUE );
5783 ok( 0, "Shouldn't be reached.\n" );
5784 }
5785 else if (pass == 3)
5786 {
5787 data = curr_data;
5788 ok( data.call_count == 1 || broken( data.call_count == 2 ) /* before Win10 1809 */, "got %u.\n", data.call_count );
5789 ok( data.call_data[0].r10 == pKiUserExceptionDispatcher, "got %p, expected %p.\n", data.call_data[0].r10,
5791 init_instrumentation_data( &curr_data );
5792 NtContinue( &ctx, FALSE );
5793 ok( 0, "Shouldn't be reached.\n" );
5794 }
5795 else if (pass == 4)
5796 {
5797 data = curr_data;
5798 /* Not called for NtContinue. */
5799 ok( !data.call_count, "got %u.\n", data.call_count );
5800 init_instrumentation_data( &curr_data );
5802 ok( 0, "Shouldn't be reached.\n" );
5803 }
5804 else if (pass == 5)
5805 {
5806 data = curr_data;
5807 ok( data.call_count == 1, "got %u.\n", data.call_count );
5808 ok( data.call_data[0].r10 == (void *)ctx.Rip, "got %p, expected %p.\n", data.call_data[0].r10, (void *)ctx.Rip );
5809 init_instrumentation_data( &curr_data );
5810 }
5811 ok( pass == 5, "got %ld.\n", pass );
5812 RemoveVectoredExceptionHandler( vectored_handler );
5813
5814 apc_count = 0;
5815 status = pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef );
5816 ok( !status, "got %#lx.\n", status );
5817 init_instrumentation_data( &curr_data );
5818 SleepEx( 0, TRUE );
5819 data = curr_data;
5820 ok( apc_count == 1, "APC was not called.\n" );
5821 ok( data.call_count == 1, "got %u.\n", data.call_count );
5822 ok( data.call_data[0].r10 == pKiUserApcDispatcher, "got %p, expected %p.\n", data.call_data[0].r10, pKiUserApcDispatcher );
5823
5824 instrumentation_callback_thread_ready = CreateEventW( NULL, FALSE, FALSE, NULL );
5825 instrumentation_callback_thread_wait = CreateEventW( NULL, FALSE, FALSE, NULL );
5826 init_instrumentation_data( &curr_data );
5827 thread = CreateThread( NULL, 0, test_instrumentation_callback_thread, 0, 0, NULL );
5828 NtWaitForSingleObject( instrumentation_callback_thread_ready, FALSE, NULL );
5829 data = curr_data;
5830 ok( data.call_count && data.call_count <= 256, "got %u.\n", data.call_count );
5831 pLdrInitializeThunk = GetProcAddress( ntdll, "LdrInitializeThunk" );
5832 for (i = 0; i < data.call_count; ++i)
5833 {
5834 if (data.call_data[i].r10 == pLdrInitializeThunk) break;
5835 }
5836 ok( i < data.call_count, "LdrInitializeThunk not found.\n" );
5837
5838 init_instrumentation_data( &curr_data );
5839 SetEvent( instrumentation_callback_thread_wait );
5840 NtWaitForSingleObject( instrumentation_callback_thread_ready, FALSE, NULL );
5841 data = curr_data;
5842 ok( data.call_count && data.call_count <= 256, "got %u.\n", data.call_count );
5843 count = 0;
5844 for (i = 0; i < data.call_count; ++i)
5845 {
5846 if (data.call_data[i].r10 >= (char *)NtWaitForSingleObject && data.call_data[i].r10 < (char *)NtWaitForSingleObject + 0x20)
5847 ++count;
5848 }
5849 ok( count == 2, "got %u.\n", count );
5850
5851 SetEvent( instrumentation_callback_thread_wait );
5854 CloseHandle( instrumentation_callback_thread_ready );
5855 CloseHandle( instrumentation_callback_thread_wait );
5856
5857 hwnd = CreateWindowA( "Static", "test", 0, 0, 0, 0, 0, 0, 0, 0, 0 );
5858 init_instrumentation_data( &curr_data );
5860 data = curr_data;
5861 ok( data.call_count && data.call_count <= 256, "got %u.\n", data.call_count );
5862 for (i = 0; i < data.call_count; ++i)
5863 {
5864 if (data.call_data[i].r10 == pKiUserCallbackDispatcher)
5865 break;
5866 }
5867 ok( i < data.call_count, "KiUserCallbackDispatcher not found.\n" );
5868
5869 init_instrumentation_data( &curr_data );
5870 memset(&info, 0, sizeof(info));
5872 data = curr_data;
5873 ok( !status, "got %#lx.\n", status );
5874 ok( !data.call_count, "got %u.\n", data.call_count );
5875}
5876
5877static UINT32 find_syscall_nr(const char *function)
5878{
5879 UINT32 syscall_nr;
5880
5881 char *code = (char *)GetProcAddress(hntdll, function);
5882
5883 /* This assumes that Nt* syscall thunks are all formatted as:
5884 *
5885 * 4c 8b d1 movq %rcx, %r10
5886 * b8 ?? ?? ?? ?? movl $(syscall number), %eax
5887 */
5888 memcpy(&syscall_nr, code + 4, sizeof(UINT32));
5889 return syscall_nr;
5890}
5891
5892static void test_direct_syscalls(void)
5893{
5894 static const BYTE code[] =
5895 {
5896 0x49, 0x89, 0xd2, /* movq %rdx, %r10 */
5897 0x4c, 0x89, 0xc2, /* movq %r8, %rdx */
5898 0x89, 0xc8, /* movl %ecx, %eax */
5899 0x0f, 0x05, /* syscall */
5900 0xc3, /* ret */
5901 };
5902
5903 HANDLE event;
5904 NTSTATUS (WINAPI *func)(UINT32 syscall_nr, HANDLE h, LONG *prev_state);
5905
5906 event = CreateEventW(NULL, FALSE, FALSE, NULL);
5907 memcpy(code_mem, code, sizeof(code));
5908 func = code_mem;
5909 func(find_syscall_nr("NtSetEvent"), event, NULL);
5910
5911 todo_wine
5912 ok(WaitForSingleObject(event, 0) == WAIT_OBJECT_0, "Event not signaled.\n");
5914}
5915
5916#elif defined(__arm__)
5917
5918static void test_thread_context(void)
5919{
5922 struct expected
5923 {
5924 DWORD R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, Sp, Lr, Pc, Cpsr;
5925 } expect;
5926 NTSTATUS (*func_ptr)( void *arg1, void *arg2, struct expected *res, void *func );
5927
5928 static const WORD call_func[] =
5929 {
5930 0xb502, /* push {r1, lr} */
5931 0xe882, 0x1fff, /* stmia.w r2, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, sl, fp, ip} */
5932 0xf8c2, 0xd034, /* str.w sp, [r2, #52] */
5933 0xf8c2, 0xe038, /* str.w lr, [r2, #56] */
5934 0xf3ef, 0x8100, /* mrs r1, CPSR */
5935 0xf041, 0x0120, /* orr.w r1, r1, #32 */
5936 0x6411, /* str r1, [r2, #64] */
5937 0x9900, /* ldr r1, [sp, #0] */
5938 0x4679, /* mov r1, pc */
5939 0xf101, 0x0109, /* add.w r1, r1, #9 */
5940 0x63d1, /* str r1, [r2, #60] */
5941 0x9900, /* ldr r1, [sp, #0] */
5942 0x4798, /* blx r3 */
5943 0xbd02, /* pop {r1, pc} */
5944 };
5945
5946 memcpy( code_mem, call_func, sizeof(call_func) );
5947 func_ptr = (void *)((char *)code_mem + 1); /* thumb */
5948
5949#define COMPARE(reg) \
5950 ok( context.reg == expect.reg, "wrong " #reg " %08lx/%08lx\n", context.reg, expect.reg )
5951
5952 memset( &context, 0xcc, sizeof(context) );
5953 memset( &expect, 0xcc, sizeof(expect) );
5954 func_ptr( &context, 0, &expect, pRtlCaptureContext );
5955 trace( "expect: r0=%08lx r1=%08lx r2=%08lx r3=%08lx r4=%08lx r5=%08lx r6=%08lx r7=%08lx r8=%08lx r9=%08lx "
5956 "r10=%08lx r11=%08lx r12=%08lx sp=%08lx lr=%08lx pc=%08lx cpsr=%08lx\n",
5957 expect.R0, expect.R1, expect.R2, expect.R3, expect.R4, expect.R5, expect.R6, expect.R7,
5958 expect.R8, expect.R9, expect.R10, expect.R11, expect.R12, expect.Sp, expect.Lr, expect.Pc, expect.Cpsr );
5959 trace( "actual: r0=%08lx r1=%08lx r2=%08lx r3=%08lx r4=%08lx r5=%08lx r6=%08lx r7=%08lx r8=%08lx r9=%08lx "
5960 "r10=%08lx r11=%08lx r12=%08lx sp=%08lx lr=%08lx pc=%08lx cpsr=%08lx\n",
5961 context.R0, context.R1, context.R2, context.R3, context.R4, context.R5, context.R6, context.R7,
5962 context.R8, context.R9, context.R10, context.R11, context.R12, context.Sp, context.Lr, context.Pc, context.Cpsr );
5963
5965 "wrong flags %08lx\n", context.ContextFlags );
5966 ok( !context.R0, "wrong R0 %08lx\n", context.R0 );
5967 COMPARE( R1 );
5968 COMPARE( R2 );
5969 COMPARE( R3 );
5970 COMPARE( R4 );
5971 COMPARE( R5 );
5972 COMPARE( R6 );
5973 COMPARE( R7 );
5974 COMPARE( R8 );
5975 COMPARE( R9 );
5976 COMPARE( R10 );
5977 COMPARE( R11 );
5978 COMPARE( R12 );
5979 COMPARE( Sp );
5980 COMPARE( Pc );
5981 COMPARE( Cpsr );
5982 ok( !context.Lr, "wrong Lr %08lx\n", context.Lr );
5983
5984 memset( &context, 0xcc, sizeof(context) );
5985 memset( &expect, 0xcc, sizeof(expect) );
5986 context.ContextFlags = CONTEXT_FULL;
5987
5988 status = func_ptr( GetCurrentThread(), &context, &expect, pNtGetContextThread );
5989 ok( status == STATUS_SUCCESS, "NtGetContextThread failed %08lx\n", status );
5990 trace( "expect: r0=%08lx r1=%08lx r2=%08lx r3=%08lx r4=%08lx r5=%08lx r6=%08lx r7=%08lx r8=%08lx r9=%08lx "
5991 "r10=%08lx r11=%08lx r12=%08lx sp=%08lx lr=%08lx pc=%08lx cpsr=%08lx\n",
5992 expect.R0, expect.R1, expect.R2, expect.R3, expect.R4, expect.R5, expect.R6, expect.R7,
5993 expect.R8, expect.R9, expect.R10, expect.R11, expect.R12, expect.Sp, expect.Lr, expect.Pc, expect.Cpsr );
5994 trace( "actual: r0=%08lx r1=%08lx r2=%08lx r3=%08lx r4=%08lx r5=%08lx r6=%08lx r7=%08lx r8=%08lx r9=%08lx "
5995 "r10=%08lx r11=%08lx r12=%08lx sp=%08lx lr=%08lx pc=%08lx cpsr=%08lx\n",
5996 context.R0, context.R1, context.R2, context.R3, context.R4, context.R5, context.R6, context.R7,
5997 context.R8, context.R9, context.R10, context.R11, context.R12, context.Sp, context.Lr, context.Pc, context.Cpsr );
5998 /* other registers are not preserved */
5999 COMPARE( R4 );
6000 COMPARE( R5 );
6001 COMPARE( R6 );
6002 COMPARE( R7 );
6003 COMPARE( R8 );
6004 COMPARE( R9 );
6005 COMPARE( R10 );
6006 COMPARE( R11 );
6007 ok( (context.Cpsr & 0xff0f0030) == (expect.Cpsr & 0xff0f0030),
6008 "wrong Cpsr %08lx/%08lx\n", context.Cpsr, expect.Cpsr );
6009 ok( context.Sp == expect.Sp - 16,
6010 "wrong Sp %08lx/%08lx\n", context.Sp, expect.Sp - 16 );
6011 /* Pc is somewhere close to the NtGetContextThread implementation */
6012 ok( (char *)context.Pc >= (char *)pNtGetContextThread &&
6013 (char *)context.Pc <= (char *)pNtGetContextThread + 0x10,
6014 "wrong Pc %08lx/%08lx\n", context.Pc, (DWORD)pNtGetContextThread );
6015#undef COMPARE
6016}
6017
6018static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx)
6019{
6020 char cmdline[MAX_PATH];
6022 STARTUPINFOA si = { 0 };
6023 DEBUG_EVENT de;
6024 DWORD continuestatus;
6025 PVOID code_mem_address = NULL;
6027 SIZE_T size_read;
6028 BOOL ret;
6029 int counter = 0;
6030 si.cb = sizeof(si);
6031
6032 if(!pNtGetContextThread || !pNtSetContextThread || !pNtReadVirtualMemory || !pNtTerminateProcess)
6033 {
6034 skip("NtGetContextThread, NtSetContextThread, NtReadVirtualMemory or NtTerminateProcess not found\n");
6035 return;
6036 }
6037
6038 if (with_WaitForDebugEventEx && !pWaitForDebugEventEx)
6039 {
6040 skip("WaitForDebugEventEx not found, skipping unicode strings in OutputDebugStringW\n");
6041 return;
6042 }
6043
6044 sprintf(cmdline, "%s %s %s %p", my_argv[0], my_argv[1], "debuggee", &test_stage);
6046 ok(ret, "could not create child process error: %lu\n", GetLastError());
6047 if (!ret)
6048 return;
6049
6050 do
6051 {
6052 continuestatus = cont_status;
6053 ret = with_WaitForDebugEventEx ? pWaitForDebugEventEx(&de, INFINITE) : WaitForDebugEvent(&de, INFINITE);
6054 ok(ret, "reading debug event\n");
6055
6056 ret = ContinueDebugEvent(de.dwProcessId, de.dwThreadId, 0xdeadbeef);
6057 ok(!ret, "ContinueDebugEvent unexpectedly succeeded\n");
6058 ok(GetLastError() == ERROR_INVALID_PARAMETER, "Unexpected last error: %lu\n", GetLastError());
6059
6060 if (de.dwThreadId != pi.dwThreadId)
6061 {
6062 trace("event %ld not coming from main thread, ignoring\n", de.dwDebugEventCode);
6063 ContinueDebugEvent(de.dwProcessId, de.dwThreadId, cont_status);
6064 continue;
6065 }
6066
6068 {
6070 {
6071 skip("child process loaded at different address, terminating it\n");
6072 pNtTerminateProcess(pi.hProcess, 0);
6073 }
6074 }
6076 {
6077 CONTEXT ctx;
6078 enum debugger_stages stage;
6079
6080 counter++;
6081 status = pNtReadVirtualMemory(pi.hProcess, &code_mem, &code_mem_address,
6082 sizeof(code_mem_address), &size_read);
6083 ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status);
6084 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
6085 sizeof(stage), &size_read);
6086 ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status);
6087
6088 ctx.ContextFlags = CONTEXT_FULL;
6089 status = pNtGetContextThread(pi.hThread, &ctx);
6090 ok(!status, "NtGetContextThread failed with 0x%lx\n", status);
6091
6092 trace("exception 0x%lx at %p firstchance=%ld pc=%08lx, r0=%08lx\n",
6095 de.u.Exception.dwFirstChance, ctx.Pc, ctx.R0);
6096
6097 if (counter > 100)
6098 {
6099 ok(FALSE, "got way too many exceptions, probably caught in an infinite loop, terminating child\n");
6100 pNtTerminateProcess(pi.hProcess, 1);
6101 }
6102 else if (counter < 2) /* startup breakpoint */
6103 {
6104 /* breakpoint is inside ntdll */
6105 void *ntdll = GetModuleHandleA( "ntdll.dll" );
6107
6108 ok( (char *)ctx.Pc >= (char *)ntdll &&
6109 (char *)ctx.Pc < (char *)ntdll + nt->OptionalHeader.SizeOfImage,
6110 "wrong pc %p ntdll %p-%p\n", (void *)ctx.Pc, ntdll,
6111 (char *)ntdll + nt->OptionalHeader.SizeOfImage );
6112 }
6113 else
6114 {
6115 if (stage == STAGE_RTLRAISE_NOT_HANDLED)
6116 {
6117 ok((char *)ctx.Pc == (char *)code_mem_address + 7, "Pc at %lx instead of %p\n",
6118 ctx.Pc, (char *)code_mem_address + 7);
6119 /* setting the context from debugger does not affect the context that the
6120 * exception handler gets, except on w2008 */
6121 ctx.Pc = (UINT_PTR)code_mem_address + 9;
6122 ctx.R0 = 0xf00f00f1;
6123 /* let the debuggee handle the exception */
6124 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
6125 }
6126 else if (stage == STAGE_RTLRAISE_HANDLE_LAST_CHANCE)
6127 {
6128 if (de.u.Exception.dwFirstChance)
6129 {
6130 /* debugger gets first chance exception with unmodified ctx.Pc */
6131 ok((char *)ctx.Pc == (char *)code_mem_address + 7, "Pc at 0x%lx instead of %p\n",
6132 ctx.Pc, (char *)code_mem_address + 7);
6133 ctx.Pc = (UINT_PTR)code_mem_address + 9;
6134 ctx.R0 = 0xf00f00f1;
6135 /* pass exception to debuggee
6136 * exception will not be handled and a second chance exception will be raised */
6137 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
6138 }
6139 else
6140 {
6141 /* debugger gets context after exception handler has played with it */
6142 /* ctx.Pc is the same value the exception handler got */
6144 {
6145 ok((char *)ctx.Pc == (char *)code_mem_address + 7,
6146 "Pc at 0x%lx instead of %p\n", ctx.Pc, (char *)code_mem_address + 7);
6147 /* need to fixup Pc for debuggee */
6148 ctx.Pc += 2;
6149 }
6150 else ok((char *)ctx.Pc == (char *)code_mem_address + 7,
6151 "Pc at 0x%lx instead of %p\n", ctx.Pc, (char *)code_mem_address + 7);
6152 /* here we handle exception */
6153 }
6154 }
6155 else if (stage == STAGE_SERVICE_CONTINUE || stage == STAGE_SERVICE_NOT_HANDLED)
6156 {
6158 "expected EXCEPTION_BREAKPOINT, got %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode);
6159 ok((char *)ctx.Pc == (char *)code_mem_address + 0x1d,
6160 "expected Pc = %p, got 0x%lx\n", (char *)code_mem_address + 0x1d, ctx.Pc);
6161 if (stage == STAGE_SERVICE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
6162 }
6163 else if (stage == STAGE_BREAKPOINT_CONTINUE || stage == STAGE_BREAKPOINT_NOT_HANDLED)
6164 {
6166 "expected EXCEPTION_BREAKPOINT, got %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode);
6167 ok((char *)ctx.Pc == (char *)code_mem_address + 3,
6168 "expected Pc = %p, got 0x%lx\n", (char *)code_mem_address + 3, ctx.Pc);
6169 if (stage == STAGE_BREAKPOINT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
6170 }
6172 {
6174 "unexpected exception code %08lx, expected %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode,
6177 "unexpected number of parameters %ld, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters);
6178
6180 }
6182 {
6183 ok(FALSE || broken(TRUE) /* < Win10 */, "should not throw exception\n");
6184 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
6185 }
6186 else
6187 ok(FALSE, "unexpected stage %x\n", stage);
6188
6189 status = pNtSetContextThread(pi.hThread, &ctx);
6190 ok(!status, "NtSetContextThread failed with 0x%lx\n", status);
6191 }
6192 }
6194 {
6195 enum debugger_stages stage;
6196 char buffer[64 * sizeof(WCHAR)];
6197 unsigned char_size = de.u.DebugString.fUnicode ? sizeof(WCHAR) : sizeof(char);
6198
6199 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
6200 sizeof(stage), &size_read);
6201 ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status);
6202
6203 if (de.u.DebugString.fUnicode)
6204 ok(with_WaitForDebugEventEx &&
6206 "unexpected unicode debug string event\n");
6207 else
6208 ok(!with_WaitForDebugEventEx || stage != STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || cont_status != DBG_CONTINUE,
6209 "unexpected ansi debug string event %u %s %lx\n",
6210 stage, with_WaitForDebugEventEx ? "with" : "without", cont_status);
6211
6212 ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) / char_size - 1,
6213 "buffer not large enough to hold %d bytes\n", de.u.DebugString.nDebugStringLength);
6214
6215 memset(buffer, 0, sizeof(buffer));
6216 status = pNtReadVirtualMemory(pi.hProcess, de.u.DebugString.lpDebugStringData, buffer,
6217 de.u.DebugString.nDebugStringLength * char_size, &size_read);
6218 ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status);
6219
6222 {
6223 if (de.u.DebugString.fUnicode)
6224 ok(!wcscmp((WCHAR*)buffer, L"Hello World"), "got unexpected debug string '%ls'\n", (WCHAR*)buffer);
6225 else
6226 ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer);
6227 }
6228 else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */
6229 ok(strstr(buffer, "SHIMVIEW") != NULL, "unexpected stage %x, got debug string event '%s'\n", stage, buffer);
6230
6232 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
6233 }
6234 else if (de.dwDebugEventCode == RIP_EVENT)
6235 {
6236 enum debugger_stages stage;
6237
6238 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
6239 sizeof(stage), &size_read);
6240 ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status);
6241
6242 if (stage == STAGE_RIPEVENT_CONTINUE || stage == STAGE_RIPEVENT_NOT_HANDLED)
6243 {
6244 ok(de.u.RipInfo.dwError == 0x11223344, "got unexpected rip error code %08lx, expected %08x\n",
6245 de.u.RipInfo.dwError, 0x11223344);
6246 ok(de.u.RipInfo.dwType == 0x55667788, "got unexpected rip type %08lx, expected %08x\n",
6247 de.u.RipInfo.dwType, 0x55667788);
6248 }
6249 else
6250 ok(FALSE, "unexpected stage %x\n", stage);
6251
6252 if (stage == STAGE_RIPEVENT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
6253 }
6254
6255 ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus);
6256
6258
6261 ok(ret, "error %lu\n", GetLastError());
6263 ok(ret, "error %lu\n", GetLastError());
6264}
6265
6266static void test_debug_service(DWORD numexc)
6267{
6268 /* not supported */
6269}
6270
6271
6272static BOOL hook_called;
6273static BOOL got_exception;
6274static void *code_ptr;
6275
6276static WORD patched_code[] =
6277{
6278 0x4668, /* mov r0, sp */
6279 0xf8df, 0xc004, /* ldr.w ip, [pc, #0x4] */
6280 0x4760, /* bx ip */
6281 0, 0, /* 1: hook_trampoline */
6282};
6283static WORD saved_code[ARRAY_SIZE(patched_code)];
6284
6285static LONG WINAPI dbg_except_continue_vectored_handler(struct _EXCEPTION_POINTERS *ptrs)
6286{
6287 EXCEPTION_RECORD *rec = ptrs->ExceptionRecord;
6288 CONTEXT *context = ptrs->ContextRecord;
6289
6290 trace("dbg_except_continue_vectored_handler, code %#lx, pc %#lx.\n", rec->ExceptionCode, context->Pc);
6292
6293 ok(rec->ExceptionCode == 0x80000003, "Got unexpected exception code %#lx.\n", rec->ExceptionCode);
6295}
6296
6297static void * WINAPI hook_KiUserExceptionDispatcher(void *stack)
6298{
6301
6302 trace( "rec %p context %p pc %#lx sp %#lx flags %#lx\n",
6303 rec, context, context->Pc, context->Sp, context->ContextFlags );
6304
6305 ok( !((ULONG_PTR)stack & 7), "unaligned stack %p\n", stack );
6306 ok( rec->ExceptionCode == 0x80000003, "Got unexpected ExceptionCode %#lx.\n", rec->ExceptionCode );
6307
6308 hook_called = TRUE;
6309 memcpy(code_ptr, saved_code, sizeof(saved_code));
6310 FlushInstructionCache( GetCurrentProcess(), code_ptr, sizeof(saved_code));
6312}
6313
6314static void test_KiUserExceptionDispatcher(void)
6315{
6316 WORD hook_trampoline[] =
6317 {
6318 0x4668, /* mov r0, sp */
6319 0xf8df, 0xc006, /* ldr.w r12, [pc, #0x6] */
6320 0x47e0, /* blx r12 */
6321 0x4700, /* bx r0 */
6322 0, 0, /* 1: hook_KiUserExceptionDispatcher */
6323 };
6324
6326 void *trampoline_ptr, *vectored_handler;
6327 DWORD old_protect;
6328 BOOL ret;
6329
6330 code_ptr = (void *)(((ULONG_PTR)pKiUserExceptionDispatcher) & ~1); /* mask thumb bit */
6331 *(void **)&hook_trampoline[5] = hook_KiUserExceptionDispatcher;
6332 trampoline_ptr = (char *)code_mem + 1024;
6333 memcpy( trampoline_ptr, hook_trampoline, sizeof(hook_trampoline));
6334
6335 ret = VirtualProtect( code_ptr, sizeof(saved_code),
6336 PAGE_EXECUTE_READWRITE, &old_protect );
6337 ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() );
6338
6339 memcpy( saved_code, code_ptr, sizeof(saved_code) );
6340 *(void **)&patched_code[4] = (char *)trampoline_ptr + 1; /* thumb */
6341
6342 vectored_handler = AddVectoredExceptionHandler(TRUE, dbg_except_continue_vectored_handler);
6343
6344 memcpy( code_ptr, patched_code, sizeof(patched_code) );
6345 FlushInstructionCache( GetCurrentProcess(), code_ptr, sizeof(patched_code));
6346
6348 hook_called = FALSE;
6349
6350 pRtlRaiseException(&record);
6351
6352 ok(got_exception, "Handler was not called.\n");
6353 ok(!hook_called, "Hook was called.\n");
6354
6355 memcpy( code_ptr, patched_code, sizeof(patched_code) );
6356 FlushInstructionCache( GetCurrentProcess(), code_ptr, sizeof(patched_code));
6357
6358 got_exception = 0;
6359 hook_called = FALSE;
6360 NtCurrentTeb()->Peb->BeingDebugged = 1;
6361
6362 pRtlRaiseException(&record);
6363
6364 ok(got_exception, "Handler was not called.\n");
6365 ok(hook_called, "Hook was not called.\n");
6366 NtCurrentTeb()->Peb->BeingDebugged = 0;
6367
6368 RemoveVectoredExceptionHandler(vectored_handler);
6369 VirtualProtect(code_ptr, sizeof(saved_code), old_protect, &old_protect);
6370}
6371
6372static UINT alertable_supported;
6373
6374static void * WINAPI hook_KiUserApcDispatcher(void *stack)
6375{
6376 struct
6377 {
6378 void *func;
6379 ULONG args[3];
6380 ULONG alertable;
6381 ULONG align;
6383 } *args = stack;
6384 CONTEXT *context = &args->context;
6385
6386 if (args->alertable == 1) alertable_supported = TRUE;
6387 else context = (CONTEXT *)&args->alertable;
6388
6389 trace( "stack=%p func=%p args=%lx,%lx,%lx alertable=%lx context=%p pc=%lx sp=%lx (%lx)\n",
6390 args, args->func, args->args[0], args->args[1], args->args[2],
6391 args->alertable, context, context->Pc, context->Sp,
6392 context->Sp - (ULONG_PTR)stack );
6393
6394 ok( args->func == apc_func, "wrong func %p / %p\n", args->func, apc_func );
6395 ok( args->args[0] == 0x1234 + apc_count, "wrong arg1 %lx\n", args->args[0] );
6396 ok( args->args[1] == 0x5678, "wrong arg2 %lx\n", args->args[1] );
6397 ok( args->args[2] == 0xdeadbeef, "wrong arg3 %lx\n", args->args[2] );
6398
6399 if (apc_count && alertable_supported) args->alertable = FALSE;
6400 pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234 + apc_count + 1, 0x5678, 0xdeadbeef );
6401
6402 hook_called = TRUE;
6403 memcpy( code_ptr, saved_code, sizeof(saved_code));
6404 FlushInstructionCache( GetCurrentProcess(), code_ptr, sizeof(saved_code));
6405 return pKiUserApcDispatcher;
6406}
6407
6408static void test_KiUserApcDispatcher(void)
6409{
6410 WORD hook_trampoline[] =
6411 {
6412 0x4668, /* mov r0, sp */
6413 0xf8df, 0xc006, /* ldr.w r12, [pc, #0x6] */
6414 0x47e0, /* blx r12 */
6415 0x4700, /* bx r0 */
6416 0, 0, /* 1: hook_KiUserApcDispatcher */
6417 };
6418 DWORD old_protect;
6419 BOOL ret;
6420
6421 code_ptr = (void *)(((ULONG_PTR)pKiUserApcDispatcher) & ~1); /* mask thumb bit */
6422 *(void **)&hook_trampoline[5] = hook_KiUserApcDispatcher;
6423 memcpy(code_mem, hook_trampoline, sizeof(hook_trampoline));
6424
6425 ret = VirtualProtect( code_ptr, sizeof(saved_code),
6426 PAGE_EXECUTE_READWRITE, &old_protect );
6427 ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() );
6428
6429 memcpy( saved_code, code_ptr, sizeof(saved_code) );
6430 *(void **)&patched_code[4] = (char *)code_mem + 1; /* thumb */
6431 memcpy( code_ptr, patched_code, sizeof(patched_code) );
6432 FlushInstructionCache( GetCurrentProcess(), code_ptr, sizeof(patched_code));
6433
6434 hook_called = FALSE;
6435 apc_count = 0;
6436 pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef );
6437 SleepEx( 0, TRUE );
6438 ok( apc_count == 2, "APC count %u\n", apc_count );
6439 ok( hook_called, "hook was not called\n" );
6440
6441 memcpy( code_ptr, patched_code, sizeof(patched_code) );
6442 FlushInstructionCache( GetCurrentProcess(), code_ptr, sizeof(patched_code));
6443 pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234 + apc_count, 0x5678, 0xdeadbeef );
6444 SleepEx( 0, TRUE );
6445 if (alertable_supported)
6446 {
6447 ok( apc_count == 3, "APC count %u\n", apc_count );
6448 SleepEx( 0, TRUE );
6449 }
6450 ok( apc_count == 4, "APC count %u\n", apc_count );
6451
6452 VirtualProtect( code_ptr, sizeof(saved_code), old_protect, &old_protect );
6453}
6454
6455static void WINAPI hook_KiUserCallbackDispatcher(void *sp)
6456{
6457 struct
6458 {
6459 void *args;
6460 ULONG len;
6461 ULONG id;
6462 ULONG lr;
6463 ULONG sp;
6464 ULONG pc;
6465 BYTE args_data[0];
6466 } *stack = sp;
6467 ULONG_PTR redzone = (BYTE *)stack->sp - &stack->args_data[stack->len];
6468 KERNEL_CALLBACK_PROC func = NtCurrentTeb()->Peb->KernelCallbackTable[stack->id];
6469
6470 trace( "stack=%p len=%lx id=%lx lr=%lx sp=%lx pc=%lx\n",
6471 stack, stack->len, stack->id, stack->lr, stack->sp, stack->pc );
6472 NtCallbackReturn( NULL, 0, 0 );
6473
6474 ok( stack->args == stack->args_data, "wrong args %p / %p\n", stack->args, stack->args_data );
6475 ok( redzone >= 8 && redzone <= 16, "wrong sp %p / %p (%Iu)\n",
6476 (void *)stack->sp, stack->args_data, redzone );
6477
6479 {
6480 void *mod, *win32u = GetModuleHandleA("win32u.dll");
6481
6482 pRtlPcToFileHeader( (void *)stack->pc, &mod );
6483 ok( mod == win32u, "pc %lx not in win32u %p\n", stack->pc, win32u );
6484 }
6485 NtCallbackReturn( NULL, 0, func( stack->args, stack->len ));
6486}
6487
6488static void test_KiUserCallbackDispatcher(void)
6489{
6490 DWORD old_protect;
6491 BOOL ret;
6492
6493 code_ptr = (void *)(((ULONG_PTR)pKiUserCallbackDispatcher) & ~1); /* mask thumb bit */
6494 ret = VirtualProtect( code_ptr, sizeof(saved_code),
6495 PAGE_EXECUTE_READWRITE, &old_protect );
6496 ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() );
6497
6498 memcpy( saved_code, code_ptr, sizeof(saved_code));
6499 *(void **)&patched_code[4] = hook_KiUserCallbackDispatcher;
6500 memcpy( code_ptr, patched_code, sizeof(patched_code));
6501 FlushInstructionCache(GetCurrentProcess(), code_ptr, sizeof(patched_code));
6502
6503 DestroyWindow( CreateWindowA( "Static", "test", 0, 0, 0, 0, 0, 0, 0, 0, 0 ));
6504
6505 memcpy( code_ptr, saved_code, sizeof(saved_code));
6506 FlushInstructionCache(GetCurrentProcess(), code_ptr, sizeof(saved_code));
6507 VirtualProtect( code_ptr, sizeof(saved_code), old_protect, &old_protect );
6508}
6509
6510struct unwind_info
6511{
6512 DWORD function_length : 18;
6513 DWORD version : 2;
6514 DWORD x : 1;
6515 DWORD e : 1;
6516 DWORD f : 1;
6517 DWORD epilog : 5;
6518 DWORD codes : 4;
6519};
6520
6521static void run_exception_test(void *handler, const void* context,
6522 const void *code, unsigned int code_size,
6523 unsigned int func2_offset, DWORD access, DWORD handler_flags,
6524 void *arg1, void *arg2)
6525{
6526 DWORD buf[11];
6527 RUNTIME_FUNCTION runtime_func[2];
6528 struct unwind_info unwind;
6529 void (*func)(void*, void*) = (void *)((char *)code_mem + 1); /* thumb */
6530 DWORD oldaccess, oldaccess2;
6531
6532 runtime_func[0].BeginAddress = 0;
6533 runtime_func[0].UnwindData = 0x1000;
6534 runtime_func[1].BeginAddress = func2_offset;
6535 runtime_func[1].UnwindData = 0x1010;
6536
6537 unwind.function_length = func2_offset / 2;
6538 unwind.version = 0;
6539 unwind.x = 1;
6540 unwind.e = 1;
6541 unwind.f = 0;
6542 unwind.epilog = 1;
6543 unwind.codes = 1;
6544 buf[0] = *(DWORD *)&unwind;
6545 buf[1] = 0xfbfbffd4; /* push {r4, lr}; end; nop; nop */
6546 buf[2] = 0x1021;
6547 *(const void **)&buf[3] = context;
6548 unwind.function_length = (code_size - func2_offset) / 2;
6549 buf[4] = *(DWORD *)&unwind;
6550 buf[5] = 0xfbfbffd4; /* push {r4, lr}; end; nop; nop */
6551 buf[6] = 0x1021;
6552 *(const void **)&buf[7] = context;
6553 buf[8] = 0xc004f8df; /* ldr ip, 1f */
6554 buf[9] = 0xbf004760; /* bx ip; nop */
6555 *(const void **)&buf[10] = handler;
6556
6557 memcpy((unsigned char *)code_mem + 0x1000, buf, sizeof(buf));
6558 memcpy(code_mem, code, code_size);
6559 if (access) VirtualProtect(code_mem, code_size, access, &oldaccess);
6561
6562 pRtlAddFunctionTable(runtime_func, ARRAY_SIZE(runtime_func), (ULONG_PTR)code_mem);
6563 func( arg1, arg2 );
6564 pRtlDeleteFunctionTable(runtime_func);
6565
6566 if (access) VirtualProtect(code_mem, code_size, oldaccess, &oldaccess2);
6567}
6568
6569static BOOL got_nested_exception, got_prev_frame_exception;
6570static void *nested_exception_initial_frame;
6571
6572static DWORD nested_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
6574{
6575 trace("nested_exception_handler pc %p, sp %p, code %#lx, flags %#lx, ExceptionAddress %p.\n",
6576 (void *)context->Pc, (void *)context->Sp, rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress);
6577
6578 if (rec->ExceptionCode == 0x80000003 && !(rec->ExceptionFlags & EXCEPTION_NESTED_CALL))
6579 {
6580 ok(rec->NumberParameters == 1, "Got unexpected rec->NumberParameters %lu.\n", rec->NumberParameters);
6581 ok((char *)context->Sp == (char *)frame - 8, "Got unexpected frame %p / %p.\n", frame, (void *)context->Sp);
6582 ok((char *)context->Lr == (char *)code_mem + 0x07, "Got unexpected lr %p.\n", (void *)context->Lr);
6583 ok((char *)context->Pc == (char *)code_mem + 0x0b, "Got unexpected pc %p.\n", (void *)context->Pc);
6584
6585 nested_exception_initial_frame = frame;
6586 RaiseException(0xdeadbeef, 0, 0, 0);
6587 context->Pc += 2;
6589 }
6590
6591 if (rec->ExceptionCode == 0xdeadbeef &&
6594 {
6595 ok(!rec->NumberParameters, "Got unexpected rec->NumberParameters %lu.\n", rec->NumberParameters);
6596 got_nested_exception = TRUE;
6597 ok(frame == nested_exception_initial_frame, "Got unexpected frame %p / %p.\n",
6598 frame, nested_exception_initial_frame);
6600 }
6601
6602 ok(rec->ExceptionCode == 0xdeadbeef && (!rec->ExceptionFlags || rec->ExceptionFlags == EXCEPTION_SOFTWARE_ORIGINATE),
6603 "Got unexpected exception code %#lx, flags %#lx.\n", rec->ExceptionCode, rec->ExceptionFlags);
6604 ok(!rec->NumberParameters, "Got unexpected rec->NumberParameters %lu.\n", rec->NumberParameters);
6605 ok((char *)frame == (char *)nested_exception_initial_frame + 8, "Got unexpected frame %p / %p.\n",
6606 frame, nested_exception_initial_frame);
6607 got_prev_frame_exception = TRUE;
6609}
6610
6611static const WORD nested_except_code[] =
6612{
6613 0xb510, /* 00: push {r4, lr} */
6614 0xf000, 0xf801, /* 02: bl 1f */
6615 0xbd10, /* 06: pop {r4, pc} */
6616 0xb510, /* 08: 1: push {r4, lr} */
6617 0xdefe, /* 0a: trap */
6618 0xbf00, /* 0c: nop */
6619 0xbd10, /* 0e: pop {r4, pc} */
6620};
6621
6622static void test_nested_exception(void)
6623{
6624 got_nested_exception = got_prev_frame_exception = FALSE;
6625 run_exception_test(nested_exception_handler, NULL, nested_except_code, sizeof(nested_except_code),
6626 4 * sizeof(WORD), PAGE_EXECUTE_READ, UNW_FLAG_EHANDLER, 0, 0);
6627 ok(got_nested_exception, "Did not get nested exception.\n");
6628 ok(got_prev_frame_exception, "Did not get nested exception in the previous frame.\n");
6629}
6630
6631static unsigned int collided_unwind_exception_count;
6632
6633static DWORD collided_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
6635{
6636 CONTEXT ctx;
6637
6638 trace("collided_exception_handler pc %p, sp %p, code %#lx, flags %#lx, ExceptionAddress %p, frame %p.\n",
6639 (void *)context->Pc, (void *)context->Sp, rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, frame);
6640
6641 switch(collided_unwind_exception_count++)
6642 {
6643 case 0:
6644 /* Initial exception from nested_except_code. */
6645 ok(rec->ExceptionCode == STATUS_BREAKPOINT, "got %#lx.\n", rec->ExceptionCode);
6646 nested_exception_initial_frame = frame;
6647 /* Start unwind. */
6648 pRtlUnwindEx((char *)frame + 8, (char *)code_mem + 0x07, NULL, NULL, &ctx, NULL);
6649 ok(0, "shouldn't be reached\n");
6650 break;
6651 case 1:
6652 ok(rec->ExceptionCode == STATUS_UNWIND, "got %#lx.\n", rec->ExceptionCode);
6653 ok(rec->ExceptionFlags == EXCEPTION_UNWINDING, "got %#lx.\n", rec->ExceptionFlags);
6654 ok((char *)context->Pc == (char *)code_mem + 0x0b, "got %p.\n", (void *)context->Pc);
6655 /* generate exception in unwind handler. */
6656 RaiseException(0xdeadbeef, 0, 0, 0);
6657 ok(0, "shouldn't be reached\n");
6658 break;
6659 case 2:
6660 /* Inner call frame, continue search. */
6661 ok(rec->ExceptionCode == 0xdeadbeef, "got %#lx.\n", rec->ExceptionCode);
6662 ok(!rec->ExceptionFlags || rec->ExceptionFlags == EXCEPTION_SOFTWARE_ORIGINATE, "got %#lx.\n", rec->ExceptionFlags);
6663 ok(frame == nested_exception_initial_frame, "got %p, expected %p.\n", frame, nested_exception_initial_frame);
6664 break;
6665 case 3:
6666 /* Top level call frame, handle exception by unwinding. */
6667 ok(rec->ExceptionCode == 0xdeadbeef, "got %#lx.\n", rec->ExceptionCode);
6668 ok(!rec->ExceptionFlags || rec->ExceptionFlags == EXCEPTION_SOFTWARE_ORIGINATE, "got %#lx.\n", rec->ExceptionFlags);
6669 ok((char *)frame == (char *)nested_exception_initial_frame + 8, "got %p, expected %p.\n", frame, nested_exception_initial_frame);
6670 pRtlUnwindEx((char *)nested_exception_initial_frame + 8, (char *)code_mem + 0x07, NULL, NULL, &ctx, NULL);
6671 ok(0, "shouldn't be reached\n");
6672 break;
6673 case 4:
6674 /* Collided unwind. */
6675 ok(rec->ExceptionCode == STATUS_UNWIND, "got %#lx.\n", rec->ExceptionCode);
6677 ok(frame == nested_exception_initial_frame, "got %p, expected %p.\n", frame, nested_exception_initial_frame);
6678 break;
6679 case 5:
6680 /* EXCEPTION_COLLIDED_UNWIND cleared for the following frames. */
6681 ok(rec->ExceptionCode == STATUS_UNWIND, "got %#lx.\n", rec->ExceptionCode);
6683 ok((char *)frame == (char *)nested_exception_initial_frame + 8, "got %p, expected %p.\n", frame, nested_exception_initial_frame);
6684 break;
6685 }
6687}
6688
6689static void test_collided_unwind(void)
6690{
6691 got_nested_exception = got_prev_frame_exception = FALSE;
6692 collided_unwind_exception_count = 0;
6693 run_exception_test(collided_exception_handler, NULL, nested_except_code, sizeof(nested_except_code),
6694 4 * sizeof(WORD), PAGE_EXECUTE_READ, UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER, 0, 0);
6695 ok(collided_unwind_exception_count == 6, "got %u.\n", collided_unwind_exception_count);
6696}
6697
6698
6699static int rtlraiseexception_unhandled_handler_called;
6700static int rtlraiseexception_teb_handler_called;
6701static int rtlraiseexception_handler_called;
6702
6703static void rtlraiseexception_handler_( EXCEPTION_RECORD *rec, void *frame, CONTEXT *context,
6704 void *dispatcher, BOOL unhandled_handler )
6705{
6706 void *addr = rec->ExceptionAddress;
6707
6708 trace( "exception: %08lx flags:%lx addr:%p context: Pc:%p\n",
6709 rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, (void *)context->Pc );
6710
6711 ok( addr == (char *)code_mem + 7,
6712 "ExceptionAddress at %p instead of %p\n", addr, (char *)code_mem + 7 );
6713 ok( context->ContextFlags == (CONTEXT_FULL | CONTEXT_ARM_FLOATING_POINT | CONTEXT_UNWOUND_TO_CALL) ||
6714 context->ContextFlags == CONTEXT_ALL,
6715 "wrong context flags %lx\n", context->ContextFlags );
6716 ok( context->Pc == (UINT_PTR)addr,
6717 "%d: Pc at %lx instead of %Ix\n", test_stage, context->Pc, (UINT_PTR)addr );
6718
6719 ok( context->R0 == 0xf00f00f0, "context->X0 is %lx, should have been set to 0xf00f00f0 in vectored handler\n", context->R0 );
6720}
6721
6722static LONG CALLBACK rtlraiseexception_unhandled_handler(EXCEPTION_POINTERS *ExceptionInfo)
6723{
6724 PCONTEXT context = ExceptionInfo->ContextRecord;
6725 PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
6726
6727 trace( "exception: %08lx flags:%lx addr:%p context: Pc:%p\n",
6728 rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, (void *)context->Pc );
6729 rtlraiseexception_unhandled_handler_called = 1;
6730 rtlraiseexception_handler_(rec, NULL, context, NULL, TRUE);
6732
6734}
6735
6736static DWORD WINAPI rtlraiseexception_teb_handler( EXCEPTION_RECORD *rec,
6739 EXCEPTION_REGISTRATION_RECORD **dispatcher )
6740{
6741 rtlraiseexception_teb_handler_called = 1;
6742 rtlraiseexception_handler_(rec, frame, context, dispatcher, FALSE);
6744}
6745
6746static DWORD WINAPI rtlraiseexception_handler( EXCEPTION_RECORD *rec, void *frame,
6747 CONTEXT *context, DISPATCHER_CONTEXT *dispatcher )
6748{
6749 ULONG *nonvol_regs = (void *)dispatcher->NonVolatileRegisters;
6750 int i;
6751
6752 for (i = 0; i < 8; i++)
6753 ok( nonvol_regs[i] == ((ULONG *)&context->R4)[i],
6754 "wrong non volatile reg r%u %lx / %lx\n", i + 4,
6755 nonvol_regs[i], ((ULONG *)&context->R4)[i] );
6756 for (i = 0; i < 8; i++)
6757 ok( ((ULONGLONG *)(nonvol_regs + 8))[i] == context->D[i + 8],
6758 "wrong non volatile reg d%u %I64x / %I64x\n", i + 8,
6759 ((ULONGLONG *)(nonvol_regs + 8))[i], context->D[i + 8] );
6760
6761 rtlraiseexception_handler_called = 1;
6762 rtlraiseexception_handler_(rec, frame, context, dispatcher, FALSE);
6764}
6765
6766static LONG CALLBACK rtlraiseexception_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
6767{
6768 PCONTEXT context = ExceptionInfo->ContextRecord;
6769 PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
6770 void *addr = rec->ExceptionAddress;
6771
6772 trace( "exception: %08lx flags:%lx addr:%p context: Pc:%p\n",
6773 rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, (void *)context->Pc );
6774 ok( addr == (char *)code_mem + 7,
6775 "ExceptionAddress at %p instead of %p\n", addr, (char *)code_mem + 7 );
6776 ok( context->Pc == (UINT_PTR)addr,
6777 "%d: Pc at %lx instead of %Ix\n", test_stage, context->Pc, (UINT_PTR)addr );
6778
6779 context->R0 = 0xf00f00f0;
6781}
6782
6783static const DWORD call_one_arg_code[] =
6784{
6785 0xb510, /* 00: push {r4, lr} */
6786 0x4788, /* 02: blx r1 */
6787 0xbf00, /* 04: nop */
6788 0xbd10, /* 06: pop {r4, pc} */
6789};
6790
6791static void run_rtlraiseexception_test(DWORD exceptioncode)
6792{
6795 PVOID vectored_handler = NULL;
6796
6797 record.ExceptionCode = exceptioncode;
6798 record.ExceptionFlags = 0;
6799 record.ExceptionRecord = NULL;
6800 record.ExceptionAddress = NULL; /* does not matter, copied return address */
6801 record.NumberParameters = 0;
6802
6803 frame.Handler = rtlraiseexception_teb_handler;
6804 frame.Prev = NtCurrentTeb()->Tib.ExceptionList;
6805
6806 NtCurrentTeb()->Tib.ExceptionList = &frame;
6807 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, rtlraiseexception_vectored_handler);
6808 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
6809 if (pRtlSetUnhandledExceptionFilter) pRtlSetUnhandledExceptionFilter(rtlraiseexception_unhandled_handler);
6810
6811 rtlraiseexception_handler_called = 0;
6812 rtlraiseexception_teb_handler_called = 0;
6813 rtlraiseexception_unhandled_handler_called = 0;
6814
6815 run_exception_test( rtlraiseexception_handler, NULL, call_one_arg_code,
6816 sizeof(call_one_arg_code), sizeof(call_one_arg_code),
6817 PAGE_EXECUTE_READ, UNW_FLAG_EHANDLER,
6818 &record, pRtlRaiseException);
6819
6820 ok( record.ExceptionAddress == (char *)code_mem + 7,
6821 "address set to %p instead of %p\n", record.ExceptionAddress, (char *)code_mem + 7 );
6822
6823 todo_wine
6824 ok( !rtlraiseexception_teb_handler_called, "Frame TEB handler called\n" );
6825 ok( rtlraiseexception_handler_called, "Frame handler called\n" );
6826 ok( rtlraiseexception_unhandled_handler_called, "UnhandledExceptionFilter wasn't called\n" );
6827
6828 pRtlRemoveVectoredExceptionHandler(vectored_handler);
6829 if (pRtlSetUnhandledExceptionFilter) pRtlSetUnhandledExceptionFilter(NULL);
6830 NtCurrentTeb()->Tib.ExceptionList = frame.Prev;
6831}
6832
6833static void test_rtlraiseexception(void)
6834{
6835 run_rtlraiseexception_test(0x12345);
6836 run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
6837 run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
6838}
6839
6840
6841static LONG consolidate_dummy_called;
6842static LONG pass;
6843
6844static const WORD call_rtlunwind[] =
6845{
6846 0xf8dd, 0xc00c, /* ldr r12, [sp, #0xc] */
6847 0xe8ac, 0x0ff0, /* stm r12!, {r4-r11} */
6848 0xec8c, 0x8b10, /* vstm r12, {d8-d15} */
6849 0xf8dd, 0xc008, /* ldr r12, [sp, #0x8] */
6850 0x4760, /* bx r12 */
6851};
6852
6853static PVOID CALLBACK test_consolidate_dummy(EXCEPTION_RECORD *rec)
6854{
6856 DWORD *saved_regs = (DWORD *)rec->ExceptionInformation[3];
6857 DWORD *regs = (DWORD *)rec->ExceptionInformation[10];
6858 int i;
6859
6860 switch (InterlockedIncrement(&consolidate_dummy_called))
6861 {
6862 case 1: /* RtlRestoreContext */
6863 ok(ctx->Pc == 0xdeadbeef, "RtlRestoreContext wrong Pc, expected: 0xdeadbeef, got: %lx\n", ctx->Pc);
6864 ok( rec->ExceptionInformation[10] == -1, "wrong info %Ix\n", rec->ExceptionInformation[10] );
6865 break;
6866 case 2: /* RtlUnwindEx */
6867 ok(ctx->Pc != 0xdeadbeef, "RtlUnwindEx wrong Pc, got: %lx\n", ctx->Pc );
6868 ok( rec->ExceptionInformation[10] != -1, "wrong info %Ix\n", rec->ExceptionInformation[10] );
6869 for (i = 0; i < 8; i++)
6870 ok( saved_regs[i] == regs[i], "wrong reg R%u, expected: %lx, got: %lx\n",
6871 i + 4, saved_regs[i], regs[i] );
6872 regs += 8;
6873 saved_regs += 8;
6874 for (i = 0; i < 8; i++)
6875 ok( ((DWORD64 *)saved_regs)[i] == ((DWORD64 *)regs)[i],
6876 "wrong reg D%u, expected: %I64x, got: %I64x\n",
6877 i + 8, ((DWORD64 *)saved_regs)[i], ((DWORD64 *)regs)[i] );
6878 break;
6879 }
6880 return (PVOID)rec->ExceptionInformation[2];
6881}
6882
6883static void test_restore_context(void)
6884{
6885 EXCEPTION_RECORD rec;
6886 _JUMP_BUFFER buf;
6887 CONTEXT ctx;
6888 int i;
6889
6890 if (!pRtlUnwindEx || !pRtlRestoreContext || !pRtlCaptureContext)
6891 {
6892 skip("RtlUnwindEx/RtlCaptureContext/RtlRestoreContext not found\n");
6893 return;
6894 }
6895
6896 /* test simple case of capture and restore context */
6897 pass = 0;
6898 InterlockedIncrement(&pass); /* interlocked to prevent compiler from moving after capture */
6899 pRtlCaptureContext(&ctx);
6900 if (InterlockedIncrement(&pass) == 2) /* interlocked to prevent compiler from moving before capture */
6901 {
6902 pRtlRestoreContext(&ctx, NULL);
6903 ok(0, "shouldn't be reached\n");
6904 }
6905 else
6906 ok(pass < 4, "unexpected pass %ld\n", pass);
6907
6908 /* test with jmp using RtlRestoreContext */
6909 pass = 0;
6912 InterlockedIncrement(&pass); /* only called once */
6913 setjmp((_JBTYPE *)&buf);
6915 if (pass == 3)
6916 {
6918 rec.NumberParameters = 1;
6919 rec.ExceptionInformation[0] = (DWORD)&buf;
6920 /* uses buf.Pc instead of ctx.Pc */
6921 pRtlRestoreContext(&ctx, &rec);
6922 ok(0, "shouldn't be reached\n");
6923 }
6924 else if (pass == 4)
6925 {
6926 ok(buf.R4 == ctx.R4 , "longjmp failed for R4, expected: %lx, got: %lx\n", buf.R4, ctx.R4 );
6927 ok(buf.R5 == ctx.R5 , "longjmp failed for R5, expected: %lx, got: %lx\n", buf.R5, ctx.R5 );
6928 ok(buf.R6 == ctx.R6 , "longjmp failed for R6, expected: %lx, got: %lx\n", buf.R6, ctx.R6 );
6929 ok(buf.R7 == ctx.R7 , "longjmp failed for R7, expected: %lx, got: %lx\n", buf.R7, ctx.R7 );
6930 ok(buf.R8 == ctx.R8 , "longjmp failed for R8, expected: %lx, got: %lx\n", buf.R8, ctx.R8 );
6931 ok(buf.R9 == ctx.R9 , "longjmp failed for R9, expected: %lx, got: %lx\n", buf.R9, ctx.R9 );
6932 ok(buf.R10 == ctx.R10, "longjmp failed for R10, expected: %lx, got: %lx\n", buf.R10, ctx.R10 );
6933 ok(buf.R11 == ctx.R11, "longjmp failed for R11, expected: %lx, got: %lx\n", buf.R11, ctx.R11 );
6934 for (i = 0; i < 8; i++)
6935 ok(buf.D[i] == ctx.D[i + 8], "longjmp failed for D%u, expected: %I64x, got: %I64x\n",
6936 i + 8, buf.D[i], ctx.D[i + 8]);
6937 pRtlRestoreContext(&ctx, &rec);
6938 ok(0, "shouldn't be reached\n");
6939 }
6940 else
6941 ok(pass == 5, "unexpected pass %ld\n", pass);
6942
6943 /* test with jmp through RtlUnwindEx */
6944 pass = 0;
6946 pRtlCaptureContext(&ctx);
6947 InterlockedIncrement(&pass); /* only called once */
6948 setjmp((_JBTYPE *)&buf);
6950 if (pass == 3)
6951 {
6953 rec.NumberParameters = 1;
6954 rec.ExceptionInformation[0] = (DWORD)&buf;
6955
6956 /* uses buf.Pc instead of bogus 0xdeadbeef */
6957 pRtlUnwindEx((void*)buf.Sp, (void*)0xdeadbeef, &rec, NULL, &ctx, NULL);
6958 ok(0, "shouldn't be reached\n");
6959 }
6960 else
6961 ok(pass == 4, "unexpected pass %ld\n", pass);
6962
6963
6964 /* test with consolidate */
6965 pass = 0;
6969 if (pass == 2)
6970 {
6972 rec.NumberParameters = 3;
6973 rec.ExceptionInformation[0] = (DWORD)test_consolidate_dummy;
6974 rec.ExceptionInformation[1] = (DWORD)&ctx;
6975 rec.ExceptionInformation[2] = ctx.Pc;
6976 rec.ExceptionInformation[10] = -1;
6977 ctx.Pc = 0xdeadbeef;
6978
6979 pRtlRestoreContext(&ctx, &rec);
6980 ok(0, "shouldn't be reached\n");
6981 }
6982 else if (pass == 3)
6983 ok(consolidate_dummy_called == 1, "test_consolidate_dummy not called\n");
6984 else
6985 ok(0, "unexpected pass %ld\n", pass);
6986
6987 /* test with consolidate through RtlUnwindEx */
6988 pass = 0;
6990 pRtlCaptureContext(&ctx);
6992 if (pass == 2)
6993 {
6994 void (*func)(DWORD,DWORD,EXCEPTION_RECORD*,DWORD,CONTEXT*,void*,void*,void*);
6995 DWORD64 nonvol_regs[12];
6996
6997 func = (void *)((ULONG_PTR)code_mem | 1); /* thumb */
6999 rec.NumberParameters = 4;
7000 rec.ExceptionInformation[0] = (DWORD)test_consolidate_dummy;
7001 rec.ExceptionInformation[1] = (DWORD)&ctx;
7002 rec.ExceptionInformation[2] = ctx.Pc;
7003 rec.ExceptionInformation[3] = (DWORD)&nonvol_regs;
7004 rec.ExceptionInformation[10] = -1; /* otherwise it doesn't get set */
7005 ctx.Pc = 0xdeadbeef;
7006 /* uses consolidate callback Pc instead of bogus 0xdeadbeef */
7007 memcpy( code_mem, call_rtlunwind, sizeof(call_rtlunwind) );
7008 FlushInstructionCache( GetCurrentProcess(), code_mem, sizeof(call_rtlunwind) );
7009 func( buf.Frame, 0xdeadbeef, &rec, 0, &ctx, NULL, pRtlUnwindEx, nonvol_regs );
7010 ok(0, "shouldn't be reached\n");
7011 }
7012 else if (pass == 3)
7013 ok(consolidate_dummy_called == 2, "test_consolidate_dummy not called\n");
7014 else
7015 ok(0, "unexpected pass %ld\n", pass);
7016}
7017
7018#elif defined(__aarch64__)
7019
7020static void test_thread_context(void)
7021{
7024 struct expected
7025 {
7026 ULONG64 X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16,
7027 X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, Fp, Lr, Sp, Pc;
7028 ULONG Cpsr, Fpcr, Fpsr;
7029 } expect;
7030 NTSTATUS (*func_ptr)( void *arg1, void *arg2, struct expected *res, void *func ) = code_mem;
7031
7032 static const DWORD call_func[] =
7033 {
7034 0xa9bf7bfd, /* stp x29, x30, [sp, #-16]! */
7035 0xa9000440, /* stp x0, x1, [x2] */
7036 0xa9010c42, /* stp x2, x3, [x2, #16] */
7037 0xa9021444, /* stp x4, x5, [x2, #32] */
7038 0xa9031c46, /* stp x6, x7, [x2, #48] */
7039 0xa9042448, /* stp x8, x9, [x2, #64] */
7040 0xa9052c4a, /* stp x10, x11, [x2, #80] */
7041 0xa906344c, /* stp x12, x13, [x2, #96] */
7042 0xa9073c4e, /* stp x14, x15, [x2, #112] */
7043 0xa9084450, /* stp x16, x17, [x2, #128] */
7044 0xa9094c52, /* stp x18, x19, [x2, #144] */
7045 0xa90a5454, /* stp x20, x21, [x2, #160] */
7046 0xa90b5c56, /* stp x22, x23, [x2, #176] */
7047 0xa90c6458, /* stp x24, x25, [x2, #192] */
7048 0xa90d6c5a, /* stp x26, x27, [x2, #208] */
7049 0xa90e745c, /* stp x28, x29, [x2, #224] */
7050 0xf900785e, /* str x30, [x2, #240] */
7051 0x910003e1, /* mov x1, sp */
7052 0xf9007c41, /* str x1, [x2, #248] */
7053 0x90000001, /* adrp x1, 1f */
7054 0x9101e021, /* add x1, x1, #:lo12:1f */
7055 0xf9008041, /* str x1, [x2, #256] */
7056 0xd53b4201, /* mrs x1, nzcv */
7057 0xb9010841, /* str w1, [x2, #264] */
7058 0xd53b4401, /* mrs x1, fpcr */
7059 0xb9010c41, /* str w1, [x2, #268] */
7060 0xd53b4421, /* mrs x1, fpsr */
7061 0xb9011041, /* str w1, [x2, #272] */
7062 0xf9400441, /* ldr x1, [x2, #8] */
7063 0xd63f0060, /* blr x3 */
7064 0xa8c17bfd, /* 1: ldp x29, x30, [sp], #16 */
7065 0xd65f03c0, /* ret */
7066 };
7067
7068 memcpy( func_ptr, call_func, sizeof(call_func) );
7069
7070#define COMPARE(reg) \
7071 ok( context.reg == expect.reg, "wrong " #reg " %p/%p\n", (void *)(ULONG64)context.reg, (void *)(ULONG64)expect.reg )
7072
7073 memset( &context, 0xcc, sizeof(context) );
7074 memset( &expect, 0xcc, sizeof(expect) );
7075 func_ptr( &context, 0, &expect, pRtlCaptureContext );
7076 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=%08lx\n",
7077 (void *)expect.X0, (void *)expect.X1, (void *)expect.X2, (void *)expect.X3,
7078 (void *)expect.X4, (void *)expect.X5, (void *)expect.X6, (void *)expect.X7,
7079 (void *)expect.X8, (void *)expect.X9, (void *)expect.X10, (void *)expect.X11,
7080 (void *)expect.X12, (void *)expect.X13, (void *)expect.X14, (void *)expect.X15,
7081 (void *)expect.X16, (void *)expect.X17, (void *)expect.X18, (void *)expect.X19,
7082 (void *)expect.X20, (void *)expect.X21, (void *)expect.X22, (void *)expect.X23,
7083 (void *)expect.X24, (void *)expect.X25, (void *)expect.X26, (void *)expect.X27,
7084 (void *)expect.X28, (void *)expect.Fp, (void *)expect.Lr, (void *)expect.Sp,
7085 (void *)expect.Pc, expect.Cpsr );
7086 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=%08lx\n",
7087 (void *)context.X0, (void *)context.X1, (void *)context.X2, (void *)context.X3,
7088 (void *)context.X4, (void *)context.X5, (void *)context.X6, (void *)context.X7,
7089 (void *)context.X8, (void *)context.X9, (void *)context.X10, (void *)context.X11,
7090 (void *)context.X12, (void *)context.X13, (void *)context.X14, (void *)context.X15,
7091 (void *)context.X16, (void *)context.X17, (void *)context.X18, (void *)context.X19,
7092 (void *)context.X20, (void *)context.X21, (void *)context.X22, (void *)context.X23,
7093 (void *)context.X24, (void *)context.X25, (void *)context.X26, (void *)context.X27,
7094 (void *)context.X28, (void *)context.Fp, (void *)context.Lr, (void *)context.Sp,
7095 (void *)context.Pc, context.Cpsr );
7096
7097 ok( context.ContextFlags == CONTEXT_FULL,
7098 "wrong flags %08lx\n", context.ContextFlags );
7099 ok( !context.X0, "wrong X0 %p\n", (void *)context.X0 );
7100 COMPARE( X1 );
7101 COMPARE( X2 );
7102 COMPARE( X3 );
7103 COMPARE( X4 );
7104 COMPARE( X5 );
7105 COMPARE( X6 );
7106 COMPARE( X7 );
7107 COMPARE( X8 );
7108 COMPARE( X9 );
7109 COMPARE( X10 );
7110 COMPARE( X11 );
7111 COMPARE( X12 );
7112 COMPARE( X13 );
7113 COMPARE( X14 );
7114 COMPARE( X15 );
7115 COMPARE( X16 );
7116 COMPARE( X17 );
7117 COMPARE( X18 );
7118 COMPARE( X19 );
7119 COMPARE( X20 );
7120 COMPARE( X21 );
7121 COMPARE( X22 );
7122 COMPARE( X23 );
7123 COMPARE( X24 );
7124 COMPARE( X25 );
7125 COMPARE( X26 );
7126 COMPARE( X27 );
7127 COMPARE( X28 );
7128 COMPARE( Fp );
7129 COMPARE( Sp );
7130 COMPARE( Pc );
7131 COMPARE( Cpsr );
7132 COMPARE( Fpcr );
7133 COMPARE( Fpsr );
7134 ok( !context.Lr, "wrong Lr %p\n", (void *)context.Lr );
7135
7136 memset( &context, 0xcc, sizeof(context) );
7137 memset( &expect, 0xcc, sizeof(expect) );
7138 context.ContextFlags = CONTEXT_FULL;
7139
7140 status = func_ptr( GetCurrentThread(), &context, &expect, pNtGetContextThread );
7141 ok( status == STATUS_SUCCESS, "NtGetContextThread failed %08lx\n", status );
7142 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=%08lx\n",
7143 (void *)expect.X0, (void *)expect.X1, (void *)expect.X2, (void *)expect.X3,
7144 (void *)expect.X4, (void *)expect.X5, (void *)expect.X6, (void *)expect.X7,
7145 (void *)expect.X8, (void *)expect.X9, (void *)expect.X10, (void *)expect.X11,
7146 (void *)expect.X12, (void *)expect.X13, (void *)expect.X14, (void *)expect.X15,
7147 (void *)expect.X16, (void *)expect.X17, (void *)expect.X18, (void *)expect.X19,
7148 (void *)expect.X20, (void *)expect.X21, (void *)expect.X22, (void *)expect.X23,
7149 (void *)expect.X24, (void *)expect.X25, (void *)expect.X26, (void *)expect.X27,
7150 (void *)expect.X28, (void *)expect.Fp, (void *)expect.Lr, (void *)expect.Sp,
7151 (void *)expect.Pc, expect.Cpsr );
7152 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=%08lx\n",
7153 (void *)context.X0, (void *)context.X1, (void *)context.X2, (void *)context.X3,
7154 (void *)context.X4, (void *)context.X5, (void *)context.X6, (void *)context.X7,
7155 (void *)context.X8, (void *)context.X9, (void *)context.X10, (void *)context.X11,
7156 (void *)context.X12, (void *)context.X13, (void *)context.X14, (void *)context.X15,
7157 (void *)context.X16, (void *)context.X17, (void *)context.X18, (void *)context.X19,
7158 (void *)context.X20, (void *)context.X21, (void *)context.X22, (void *)context.X23,
7159 (void *)context.X24, (void *)context.X25, (void *)context.X26, (void *)context.X27,
7160 (void *)context.X28, (void *)context.Fp, (void *)context.Lr, (void *)context.Sp,
7161 (void *)context.Pc, context.Cpsr );
7162 /* other registers are not preserved */
7163 COMPARE( X19 );
7164 COMPARE( X20 );
7165 COMPARE( X21 );
7166 COMPARE( X22 );
7167 COMPARE( X23 );
7168 COMPARE( X24 );
7169 COMPARE( X25 );
7170 COMPARE( X26 );
7171 COMPARE( X27 );
7172 COMPARE( X28 );
7173 COMPARE( Fp );
7174 COMPARE( Fpcr );
7175 COMPARE( Fpsr );
7176 ok( context.Lr == expect.Pc, "wrong Lr %p/%p\n", (void *)context.Lr, (void *)expect.Pc );
7177 ok( context.Sp == expect.Sp, "wrong Sp %p/%p\n", (void *)context.Sp, (void *)expect.Sp );
7178 ok( (char *)context.Pc >= (char *)pNtGetContextThread &&
7179 (char *)context.Pc <= (char *)pNtGetContextThread + 32,
7180 "wrong Pc %p/%p\n", (void *)context.Pc, pNtGetContextThread );
7181#undef COMPARE
7182}
7183
7184static void test_debugger(DWORD cont_status, BOOL with_WaitForDebugEventEx)
7185{
7186 char cmdline[MAX_PATH];
7188 STARTUPINFOA si = { 0 };
7189 DEBUG_EVENT de;
7190 DWORD continuestatus;
7191 PVOID code_mem_address = NULL;
7193 SIZE_T size_read;
7194 BOOL ret;
7195 int counter = 0;
7196 si.cb = sizeof(si);
7197
7198 if(!pNtGetContextThread || !pNtSetContextThread || !pNtReadVirtualMemory || !pNtTerminateProcess)
7199 {
7200 skip("NtGetContextThread, NtSetContextThread, NtReadVirtualMemory or NtTerminateProcess not found\n");
7201 return;
7202 }
7203
7204 if (with_WaitForDebugEventEx && !pWaitForDebugEventEx)
7205 {
7206 skip("WaitForDebugEventEx not found, skipping unicode strings in OutputDebugStringW\n");
7207 return;
7208 }
7209
7210 sprintf(cmdline, "%s %s %s %p", my_argv[0], my_argv[1], "debuggee", &test_stage);
7212 ok(ret, "could not create child process error: %lu\n", GetLastError());
7213 if (!ret)
7214 return;
7215
7216 do
7217 {
7218 continuestatus = cont_status;
7219 ret = with_WaitForDebugEventEx ? pWaitForDebugEventEx(&de, INFINITE) : WaitForDebugEvent(&de, INFINITE);
7220 ok(ret, "reading debug event\n");
7221
7222 ret = ContinueDebugEvent(de.dwProcessId, de.dwThreadId, 0xdeadbeef);
7223 ok(!ret, "ContinueDebugEvent unexpectedly succeeded\n");
7224 ok(GetLastError() == ERROR_INVALID_PARAMETER, "Unexpected last error: %lu\n", GetLastError());
7225
7226 if (de.dwThreadId != pi.dwThreadId)
7227 {
7228 trace("event %ld not coming from main thread, ignoring\n", de.dwDebugEventCode);
7229 ContinueDebugEvent(de.dwProcessId, de.dwThreadId, cont_status);
7230 continue;
7231 }
7232
7234 {
7236 {
7237 skip("child process loaded at different address, terminating it\n");
7238 pNtTerminateProcess(pi.hProcess, 0);
7239 }
7240 }
7242 {
7243 CONTEXT ctx;
7244 enum debugger_stages stage;
7245
7246 counter++;
7247 status = pNtReadVirtualMemory(pi.hProcess, &code_mem, &code_mem_address,
7248 sizeof(code_mem_address), &size_read);
7249 ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status);
7250 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
7251 sizeof(stage), &size_read);
7252 ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status);
7253
7254 ctx.ContextFlags = CONTEXT_FULL;
7255 status = pNtGetContextThread(pi.hThread, &ctx);
7256 ok(!status, "NtGetContextThread failed with 0x%lx\n", status);
7257
7258 trace("exception 0x%lx at %p firstchance=%ld pc=%p, x0=%p\n",
7261 de.u.Exception.dwFirstChance, (char *)ctx.Pc, (char *)ctx.X0);
7262
7263 if (counter > 100)
7264 {
7265 ok(FALSE, "got way too many exceptions, probably caught in an infinite loop, terminating child\n");
7266 pNtTerminateProcess(pi.hProcess, 1);
7267 }
7268 else if (counter < 2) /* startup breakpoint */
7269 {
7270 /* breakpoint is inside ntdll */
7271 void *ntdll = GetModuleHandleA( "ntdll.dll" );
7273
7274 ok( (char *)ctx.Pc >= (char *)ntdll &&
7275 (char *)ctx.Pc < (char *)ntdll + nt->OptionalHeader.SizeOfImage,
7276 "wrong pc %p ntdll %p-%p\n", (void *)ctx.Pc, ntdll,
7277 (char *)ntdll + nt->OptionalHeader.SizeOfImage );
7278 }
7279 else
7280 {
7281 if (stage == STAGE_RTLRAISE_NOT_HANDLED)
7282 {
7283 ok((char *)ctx.Pc == (char *)code_mem_address + 0xc, "Pc at %p instead of %p\n",
7284 (char *)ctx.Pc, (char *)code_mem_address + 0xc);
7285 /* setting the context from debugger does not affect the context that the
7286 * exception handler gets, except on w2008 */
7287 ctx.Pc = (UINT_PTR)code_mem_address + 0x10;
7288 ctx.X0 = 0xf00f00f1;
7289 /* let the debuggee handle the exception */
7290 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
7291 }
7292 else if (stage == STAGE_RTLRAISE_HANDLE_LAST_CHANCE)
7293 {
7294 if (de.u.Exception.dwFirstChance)
7295 {
7296 /* debugger gets first chance exception with unmodified ctx.Pc */
7297 ok((char *)ctx.Pc == (char *)code_mem_address + 0xc, "Pc at %p instead of %p\n",
7298 (char *)ctx.Pc, (char *)code_mem_address + 0xc);
7299 ctx.Pc = (UINT_PTR)code_mem_address + 0x10;
7300 ctx.X0 = 0xf00f00f1;
7301 /* pass exception to debuggee
7302 * exception will not be handled and a second chance exception will be raised */
7303 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
7304 }
7305 else
7306 {
7307 /* debugger gets context after exception handler has played with it */
7308 /* ctx.Pc is the same value the exception handler got */
7310 {
7311 ok((char *)ctx.Pc == (char *)code_mem_address + 0xc,
7312 "Pc at %p instead of %p\n", (char *)ctx.Pc, (char *)code_mem_address + 0xc);
7313 /* need to fixup Pc for debuggee */
7314 ctx.Pc += 4;
7315 }
7316 else ok((char *)ctx.Pc == (char *)code_mem_address + 0xc,
7317 "Pc at %p instead of %p\n", (void *)ctx.Pc, (char *)code_mem_address + 0xc);
7318 /* here we handle exception */
7319 }
7320 }
7321 else if (stage == STAGE_SERVICE_CONTINUE || stage == STAGE_SERVICE_NOT_HANDLED)
7322 {
7324 "expected EXCEPTION_BREAKPOINT, got %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode);
7325 ok((char *)ctx.Pc == (char *)code_mem_address + 0x1d,
7326 "expected Pc = %p, got %p\n", (char *)code_mem_address + 0x1d, (char *)ctx.Pc);
7327 if (stage == STAGE_SERVICE_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
7328 }
7329 else if (stage == STAGE_BREAKPOINT_CONTINUE || stage == STAGE_BREAKPOINT_NOT_HANDLED)
7330 {
7332 "expected EXCEPTION_BREAKPOINT, got %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode);
7333 ok((char *)ctx.Pc == (char *)code_mem_address + 4,
7334 "expected Pc = %p, got %p\n", (char *)code_mem_address + 4, (char *)ctx.Pc);
7335 if (stage == STAGE_BREAKPOINT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
7336 }
7338 {
7340 "unexpected exception code %08lx, expected %08lx\n", de.u.Exception.ExceptionRecord.ExceptionCode,
7343 "unexpected number of parameters %ld, expected 0\n", de.u.Exception.ExceptionRecord.NumberParameters);
7344
7346 }
7348 {
7349 ok(FALSE || broken(TRUE) /* < Win10 */, "should not throw exception\n");
7350 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
7351 }
7352 else
7353 ok(FALSE, "unexpected stage %x\n", stage);
7354
7355 status = pNtSetContextThread(pi.hThread, &ctx);
7356 ok(!status, "NtSetContextThread failed with 0x%lx\n", status);
7357 }
7358 }
7360 {
7361 enum debugger_stages stage;
7362 char buffer[128 * sizeof(WCHAR)];
7363 unsigned char_size = de.u.DebugString.fUnicode ? sizeof(WCHAR) : sizeof(char);
7364
7365 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
7366 sizeof(stage), &size_read);
7367 ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status);
7368
7369 if (de.u.DebugString.fUnicode)
7370 ok(with_WaitForDebugEventEx &&
7372 "unexpected unicode debug string event\n");
7373 else
7374 ok(!with_WaitForDebugEventEx || stage != STAGE_OUTPUTDEBUGSTRINGW_CONTINUE || cont_status != DBG_CONTINUE,
7375 "unexpected ansi debug string event %u %s %lx\n",
7376 stage, with_WaitForDebugEventEx ? "with" : "without", cont_status);
7377
7378 ok(de.u.DebugString.nDebugStringLength < sizeof(buffer) / char_size - 1,
7379 "buffer not large enough to hold %d bytes\n", de.u.DebugString.nDebugStringLength);
7380
7381 memset(buffer, 0, sizeof(buffer));
7382 status = pNtReadVirtualMemory(pi.hProcess, de.u.DebugString.lpDebugStringData, buffer,
7383 de.u.DebugString.nDebugStringLength * char_size, &size_read);
7384 ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status);
7385
7388 {
7389 if (de.u.DebugString.fUnicode)
7390 ok(!wcscmp((WCHAR*)buffer, L"Hello World"), "got unexpected debug string '%ls'\n", (WCHAR*)buffer);
7391 else
7392 ok(!strcmp(buffer, "Hello World"), "got unexpected debug string '%s'\n", buffer);
7393 }
7394 else /* ignore unrelated debug strings like 'SHIMVIEW: ShimInfo(Complete)' */
7395 ok(strstr(buffer, "SHIMVIEW") || !strncmp(buffer, "RTL:", 4),
7396 "unexpected stage %x, got debug string event '%s'\n", stage, buffer);
7397
7399 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
7400 }
7401 else if (de.dwDebugEventCode == RIP_EVENT)
7402 {
7403 enum debugger_stages stage;
7404
7405 status = pNtReadVirtualMemory(pi.hProcess, &test_stage, &stage,
7406 sizeof(stage), &size_read);
7407 ok(!status,"NtReadVirtualMemory failed with 0x%lx\n", status);
7408
7409 if (stage == STAGE_RIPEVENT_CONTINUE || stage == STAGE_RIPEVENT_NOT_HANDLED)
7410 {
7411 ok(de.u.RipInfo.dwError == 0x11223344, "got unexpected rip error code %08lx, expected %08x\n",
7412 de.u.RipInfo.dwError, 0x11223344);
7413 ok(de.u.RipInfo.dwType == 0x55667788, "got unexpected rip type %08lx, expected %08x\n",
7414 de.u.RipInfo.dwType, 0x55667788);
7415 }
7416 else
7417 ok(FALSE, "unexpected stage %x\n", stage);
7418
7419 if (stage == STAGE_RIPEVENT_NOT_HANDLED) continuestatus = DBG_EXCEPTION_NOT_HANDLED;
7420 }
7421
7422 ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus);
7423
7425
7428 ok(ret, "error %lu\n", GetLastError());
7430 ok(ret, "error %lu\n", GetLastError());
7431}
7432
7433static void test_debug_service(DWORD numexc)
7434{
7435 /* not supported */
7436}
7437
7438static void test_continue(void)
7439{
7440 struct context_pair {
7442 CONTEXT after;
7443 } contexts;
7444 KCONTINUE_ARGUMENT args = { .ContinueType = KCONTINUE_UNWIND };
7445 unsigned int i;
7446 NTSTATUS (*func_ptr)( struct context_pair *, void *arg, void *continue_func, void *capture_func ) = code_mem;
7447
7448 static const DWORD call_func[] =
7449 {
7450 0xa9bd7bfd, /* stp x29, x30, [sp, #-0x30]! */
7451 /* stash volatile registers before calling capture */
7452 0xa90107e0, /* stp x0, x1, [sp, #0x10] */
7453 0xa9020fe2, /* stp x2, x3, [sp, #0x20] */
7454 0xd63f0060, /* blr x3 * capture context from before NtContinue to contexts->before */
7455 0xa9420fe2, /* ldp x2, x3, [sp, #0x20] */
7456 0xa94107e0, /* ldp x0, x1, [sp, #0x10] */
7457 /* overwrite the contents of x4...k28 with a dummy value */
7458 0xd297dde4, /* mov x4, #0xbeef */
7459 0xf2bbd5a4, /* movk x4, #0xdead, lsl #16 */
7460 0xaa048084, /* orr x4, x4, x4, lsl #32 */
7461 0xaa0403e5, /* mov x5, x4 */
7462 0xaa0403e6, /* mov x6, x4 */
7463 0xaa0403e7, /* mov x7, x4 */
7464 0xaa0403e8, /* mov x8, x4 */
7465 0xaa0403e9, /* mov x9, x4 */
7466 0xaa0403ea, /* mov x10, x4 */
7467 0xaa0403eb, /* mov x11, x4 */
7468 0xaa0403ec, /* mov x12, x4 */
7469 0xaa0403ed, /* mov x13, x4 */
7470 0xaa0403ee, /* mov x14, x4 */
7471 0xaa0403ef, /* mov x15, x4 */
7472 0xaa0403f0, /* mov x16, x4 */
7473 0xaa0403f1, /* mov x17, x4 */
7474 /* avoid overwriting the TEB in x18 */
7475 0xaa0403f3, /* mov x19, x4 */
7476 0xaa0403f4, /* mov x20, x4 */
7477 0xaa0403f5, /* mov x21, x4 */
7478 0xaa0403f6, /* mov x22, x4 */
7479 0xaa0403f7, /* mov x23, x4 */
7480 0xaa0403f8, /* mov x24, x4 */
7481 0xaa0403f9, /* mov x25, x4 */
7482 0xaa0403fa, /* mov x26, x4 */
7483 0xaa0403fb, /* mov x27, x4 */
7484 0xaa0403fc, /* mov x28, x4 */
7485 /* overwrite the contents all vector registers a dummy value */
7486 0x4e080c80, /* dup v0.2d, x4 */
7487 0x4ea01c01, /* mov v1.2d, v0.2d */
7488 0x4ea01c02, /* mov v2.2d, v0.2d */
7489 0x4ea01c03, /* mov v3.2d, v0.2d */
7490 0x4ea01c04, /* mov v4.2d, v0.2d */
7491 0x4ea01c05, /* mov v5.2d, v0.2d */
7492 0x4ea01c06, /* mov v6.2d, v0.2d */
7493 0x4ea01c07, /* mov v7.2d, v0.2d */
7494 0x4ea01c08, /* mov v8.2d, v0.2d */
7495 0x4ea01c09, /* mov v9.2d, v0.2d */
7496 0x4ea01c0a, /* mov v10.2d, v0.2d */
7497 0x4ea01c0b, /* mov v11.2d, v0.2d */
7498 0x4ea01c0c, /* mov v12.2d, v0.2d */
7499 0x4ea01c0d, /* mov v13.2d, v0.2d */
7500 0x4ea01c0e, /* mov v14.2d, v0.2d */
7501 0x4ea01c0f, /* mov v15.2d, v0.2d */
7502 0x4ea01c10, /* mov v16.2d, v0.2d */
7503 0x4ea01c11, /* mov v17.2d, v0.2d */
7504 0x4ea01c12, /* mov v18.2d, v0.2d */
7505 0x4ea01c13, /* mov v19.2d, v0.2d */
7506 0x4ea01c14, /* mov v20.2d, v0.2d */
7507 0x4ea01c15, /* mov v21.2d, v0.2d */
7508 0x4ea01c16, /* mov v22.2d, v0.2d */
7509 0x4ea01c17, /* mov v23.2d, v0.2d */
7510 0x4ea01c18, /* mov v24.2d, v0.2d */
7511 0x4ea01c19, /* mov v25.2d, v0.2d */
7512 0x4ea01c1a, /* mov v26.2d, v0.2d */
7513 0x4ea01c1b, /* mov v27.2d, v0.2d */
7514 0x4ea01c1c, /* mov v28.2d, v0.2d */
7515 0x4ea01c1d, /* mov v29.2d, v0.2d */
7516 0x4ea01c1e, /* mov v30.2d, v0.2d */
7517 0x4ea01c1f, /* mov v31.2d, v0.2d */
7518 0xd51b441f, /* msr fpcr, xzr */
7519 0xd51b443f, /* msr fpsr, xzr */
7520 /* setup the control context so execution continues from label 1 */
7521 0x10000064, /* adr x4, #0xc */
7522 0xf9008404, /* str x4, [x0, #0x108] */
7523 0xd63f0040, /* blr x2 * restore the captured integer and floating point context */
7524 0xf94017e3, /* 1: ldr x3, [sp, #0x28] */
7525 0xf9400be0, /* ldr x0, [sp, #0x10] */
7526 0x910e4000, /* add x0, x0, #0x390 * adjust contexts to point to contexts->after */
7527 0xd63f0060, /* blr x3 * capture context from after NtContinue to contexts->after */
7528 0xa8c37bfd, /* ldp x29, x30, [sp], #0x30 */
7529 0xd65f03c0, /* ret */
7530 };
7531
7532 if (!pRtlCaptureContext)
7533 {
7534 win_skip("RtlCaptureContext is not available.\n");
7535 return;
7536 }
7537
7538 memcpy( func_ptr, call_func, sizeof(call_func) );
7539 FlushInstructionCache( GetCurrentProcess(), func_ptr, sizeof(call_func) );
7540
7541#define COMPARE(reg) \
7542 ok( contexts.before.reg == contexts.after.reg, "wrong " #reg " %p/%p\n", (void *)(ULONG64)contexts.before.reg, (void *)(ULONG64)contexts.after.reg )
7543#define COMPARE_INDEXED(reg) \
7544 ok( contexts.before.reg == contexts.after.reg, "wrong " #reg " i: %u, %p/%p\n", i, (void *)(ULONG64)contexts.before.reg, (void *)(ULONG64)contexts.after.reg )
7545
7546 func_ptr( &contexts, 0, NtContinue, pRtlCaptureContext );
7547
7548 for (i = 1; i < 29; i++) COMPARE_INDEXED( X[i] );
7549
7550 COMPARE( Fpcr );
7551 COMPARE( Fpsr );
7552
7553 for (i = 0; i < 32; i++)
7554 {
7555 COMPARE_INDEXED( V[i].Low );
7556 COMPARE_INDEXED( V[i].High );
7557 }
7558
7559 apc_count = 0;
7560 pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef );
7561 func_ptr( &contexts, 0, NtContinue, pRtlCaptureContext );
7562 ok( apc_count == 0, "apc called\n" );
7563 func_ptr( &contexts, (void *)1, NtContinue, pRtlCaptureContext );
7564 ok( apc_count == 1, "apc not called\n" );
7565
7566 if (!pNtContinueEx)
7567 {
7568 win_skip( "NtContinueEx not supported\n" );
7569 return;
7570 }
7571
7572 func_ptr( &contexts, &args, pNtContinueEx, pRtlCaptureContext );
7573
7574 for (i = 1; i < 29; i++) COMPARE_INDEXED( X[i] );
7575
7576 COMPARE( Fpcr );
7577 COMPARE( Fpsr );
7578
7579 for (i = 0; i < 32; i++)
7580 {
7581 COMPARE_INDEXED( V[i].Low );
7582 COMPARE_INDEXED( V[i].High );
7583 }
7584
7585 pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234 + apc_count, 0x5678, 0xdeadbeef );
7586 func_ptr( &contexts, &args, pNtContinueEx, pRtlCaptureContext );
7587 ok( apc_count == 1, "apc called\n" );
7588 args.ContinueFlags = KCONTINUE_FLAG_TEST_ALERT;
7589 func_ptr( &contexts, &args, pNtContinueEx, pRtlCaptureContext );
7590 ok( apc_count == 2, "apc not called\n" );
7591
7592#undef COMPARE
7593}
7594
7595static BOOL hook_called;
7596static BOOL got_exception;
7597
7598static ULONG patched_code[] =
7599{
7600 0x910003e0, /* mov x0, sp */
7601 0x5800004f, /* ldr x15, 1f */
7602 0xd61f01e0, /* br x15 */
7603 0, 0, /* 1: hook_trampoline */
7604};
7605static ULONG saved_code[ARRAY_SIZE(patched_code)];
7606
7607static LONG WINAPI dbg_except_continue_vectored_handler(struct _EXCEPTION_POINTERS *ptrs)
7608{
7609 EXCEPTION_RECORD *rec = ptrs->ExceptionRecord;
7610 CONTEXT *context = ptrs->ContextRecord;
7611
7612 trace("dbg_except_continue_vectored_handler, code %#lx, pc %#Ix.\n", rec->ExceptionCode, context->Pc);
7614
7615 ok(rec->ExceptionCode == 0x80000003, "Got unexpected exception code %#lx.\n", rec->ExceptionCode);
7617}
7618
7619static void * WINAPI hook_KiUserExceptionDispatcher(void *stack)
7620{
7621 struct
7622 {
7623 CONTEXT context; /* 000 */
7624 CONTEXT_EX context_ex; /* 390 */
7625 EXCEPTION_RECORD rec; /* 3b0 */
7626 ULONG64 align; /* 448 */
7627 ULONG64 sp; /* 450 */
7628 ULONG64 pc; /* 458 */
7629 } *args = stack;
7630 EXCEPTION_RECORD *old_rec = (EXCEPTION_RECORD *)&args->context_ex;
7631
7632 trace("stack %p context->Pc %#Ix, context->Sp %#Ix, ContextFlags %#lx.\n",
7633 stack, args->context.Pc, args->context.Sp, args->context.ContextFlags);
7634
7635 hook_called = TRUE;
7636 ok( !((ULONG_PTR)stack & 15), "unaligned stack %p\n", stack );
7637
7638 if (!broken( old_rec->ExceptionCode == 0x80000003 )) /* Windows 11 versions prior to 27686 */
7639 {
7640 ok( args->rec.ExceptionCode == 0x80000003, "Got unexpected ExceptionCode %#lx.\n", args->rec.ExceptionCode );
7641
7642 ok( args->context_ex.All.Offset == -sizeof(CONTEXT), "wrong All.Offset %lx\n", args->context_ex.All.Offset );
7643 ok( args->context_ex.All.Length >= sizeof(CONTEXT) + offsetof(CONTEXT_EX, align), "wrong All.Length %lx\n", args->context_ex.All.Length );
7644 ok( args->context_ex.Legacy.Offset == -sizeof(CONTEXT), "wrong Legacy.Offset %lx\n", args->context_ex.All.Offset );
7645 ok( args->context_ex.Legacy.Length == sizeof(CONTEXT), "wrong Legacy.Length %lx\n", args->context_ex.All.Length );
7646 ok( args->sp == args->context.Sp, "wrong sp %Ix / %Ix\n", args->sp, args->context.Sp );
7647 ok( args->pc == args->context.Pc, "wrong pc %Ix / %Ix\n", args->pc, args->context.Pc );
7648 }
7649
7650 memcpy(pKiUserExceptionDispatcher, saved_code, sizeof(saved_code));
7653}
7654
7655static void test_KiUserExceptionDispatcher(void)
7656{
7657 ULONG hook_trampoline[] =
7658 {
7659 0x910003e0, /* mov x0, sp */
7660 0x5800006f, /* ldr x15, 1f */
7661 0xd63f01e0, /* blr x15 */
7662 0xd61f0000, /* br x0 */
7663 0, 0, /* 1: hook_KiUserExceptionDispatcher */
7664 };
7665
7667 void *trampoline_ptr, *vectored_handler;
7668 DWORD old_protect;
7669 BOOL ret;
7670
7671 *(void **)&hook_trampoline[4] = hook_KiUserExceptionDispatcher;
7672 trampoline_ptr = (char *)code_mem + 1024;
7673 memcpy( trampoline_ptr, hook_trampoline, sizeof(hook_trampoline));
7674
7675 ret = VirtualProtect( pKiUserExceptionDispatcher, sizeof(saved_code),
7676 PAGE_EXECUTE_READWRITE, &old_protect );
7677 ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() );
7678
7679 memcpy( saved_code, pKiUserExceptionDispatcher, sizeof(saved_code) );
7680 *(void **)&patched_code[3] = trampoline_ptr;
7681
7682 vectored_handler = AddVectoredExceptionHandler(TRUE, dbg_except_continue_vectored_handler);
7683
7684 memcpy( pKiUserExceptionDispatcher, patched_code, sizeof(patched_code) );
7686
7688 hook_called = FALSE;
7689
7690 pRtlRaiseException(&record);
7691
7692 ok(got_exception, "Handler was not called.\n");
7693 ok(!hook_called, "Hook was called.\n");
7694
7695 memcpy( pKiUserExceptionDispatcher, patched_code, sizeof(patched_code) );
7697
7698 got_exception = 0;
7699 hook_called = FALSE;
7700 NtCurrentTeb()->Peb->BeingDebugged = 1;
7701
7702 pRtlRaiseException(&record);
7703
7704 ok(got_exception, "Handler was not called.\n");
7705 ok(hook_called, "Hook was not called.\n");
7706 NtCurrentTeb()->Peb->BeingDebugged = 0;
7707
7708 RemoveVectoredExceptionHandler(vectored_handler);
7709 VirtualProtect(pKiUserExceptionDispatcher, sizeof(saved_code), old_protect, &old_protect);
7710}
7711
7712static void * WINAPI hook_KiUserApcDispatcher(void *stack)
7713{
7714 struct
7715 {
7716 void *func;
7717 ULONG64 args[3];
7718 ULONG64 alertable;
7719 ULONG64 align;
7721 } *args = stack;
7722
7723 trace( "stack=%p func=%p args=%Ix,%Ix,%Ix alertable=%Ix context=%p pc=%Ix sp=%Ix (%Ix)\n",
7724 args, args->func, args->args[0], args->args[1], args->args[2],
7725 args->alertable, &args->context, args->context.Pc, args->context.Sp,
7726 args->context.Sp - (ULONG_PTR)stack );
7727
7728 ok( args->func == apc_func, "wrong func %p / %p\n", args->func, apc_func );
7729 ok( args->args[0] == 0x1234 + apc_count, "wrong arg1 %Ix\n", args->args[0] );
7730 ok( args->args[1] == 0x5678, "wrong arg2 %Ix\n", args->args[1] );
7731 ok( args->args[2] == 0xdeadbeef, "wrong arg3 %Ix\n", args->args[2] );
7732
7733 if (apc_count) args->alertable = FALSE;
7734 pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234 + apc_count + 1, 0x5678, 0xdeadbeef );
7735
7736 hook_called = TRUE;
7737 memcpy( pKiUserApcDispatcher, saved_code, sizeof(saved_code));
7739 return pKiUserApcDispatcher;
7740}
7741
7742static void test_KiUserApcDispatcher(void)
7743{
7744 ULONG hook_trampoline[] =
7745 {
7746 0x910003e0, /* mov x0, sp */
7747 0x5800006f, /* ldr x15, 1f */
7748 0xd63f01e0, /* blr x15 */
7749 0xd61f0000, /* br x0 */
7750 0, 0, /* 1: hook_KiUserApcDispatcher */
7751 };
7752 DWORD old_protect;
7753 BOOL ret;
7754
7755 *(void **)&hook_trampoline[4] = hook_KiUserApcDispatcher;
7756 memcpy(code_mem, hook_trampoline, sizeof(hook_trampoline));
7757
7758 ret = VirtualProtect( pKiUserApcDispatcher, sizeof(saved_code),
7759 PAGE_EXECUTE_READWRITE, &old_protect );
7760 ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() );
7761
7762 memcpy( saved_code, pKiUserApcDispatcher, sizeof(saved_code) );
7763 *(void **)&patched_code[3] = code_mem;
7764 memcpy( pKiUserApcDispatcher, patched_code, sizeof(patched_code) );
7766
7767 hook_called = FALSE;
7768 apc_count = 0;
7769 pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef );
7770 SleepEx( 0, TRUE );
7771 ok( apc_count == 2, "APC count %u\n", apc_count );
7772 ok( hook_called, "hook was not called\n" );
7773
7774 memcpy( pKiUserApcDispatcher, patched_code, sizeof(patched_code) );
7776 pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234 + apc_count, 0x5678, 0xdeadbeef );
7777 SleepEx( 0, TRUE );
7778 ok( apc_count == 3, "APC count %u\n", apc_count );
7779 SleepEx( 0, TRUE );
7780 ok( apc_count == 4, "APC count %u\n", apc_count );
7781
7782 VirtualProtect( pKiUserApcDispatcher, sizeof(saved_code), old_protect, &old_protect );
7783}
7784
7785static void WINAPI hook_KiUserCallbackDispatcher(void *sp)
7786{
7787 struct
7788 {
7789 void *args;
7790 ULONG len;
7791 ULONG id;
7793 ULONG64 lr;
7794 ULONG64 sp;
7795 ULONG64 pc;
7796 BYTE args_data[0];
7797 } *stack = sp;
7798 ULONG_PTR redzone = (BYTE *)stack->sp - &stack->args_data[stack->len];
7799 KERNEL_CALLBACK_PROC func = NtCurrentTeb()->Peb->KernelCallbackTable[stack->id];
7800
7801 trace( "stack=%p len=%lx id=%lx unk=%Ix lr=%Ix sp=%Ix pc=%Ix\n",
7802 stack, stack->len, stack->id, stack->unknown, stack->lr, stack->sp, stack->pc );
7803
7804 ok( stack->args == stack->args_data, "wrong args %p / %p\n", stack->args, stack->args_data );
7805 ok( redzone >= 16 && redzone <= 32, "wrong sp %p / %p (%Iu)\n",
7806 (void *)stack->sp, stack->args_data, redzone );
7807
7809 {
7810 void *mod, *win32u = GetModuleHandleA("win32u.dll");
7811
7812 pRtlPcToFileHeader( (void *)stack->pc, &mod );
7813 ok( mod == win32u, "pc %Ix not in win32u %p\n", stack->pc, win32u );
7814 }
7815 NtCallbackReturn( NULL, 0, func( stack->args, stack->len ));
7816}
7817
7818static void test_KiUserCallbackDispatcher(void)
7819{
7820 DWORD old_protect;
7821 BOOL ret;
7822
7823 ret = VirtualProtect( pKiUserCallbackDispatcher, sizeof(saved_code),
7824 PAGE_EXECUTE_READWRITE, &old_protect );
7825 ok( ret, "Got unexpected ret %#x, GetLastError() %lu.\n", ret, GetLastError() );
7826
7827 memcpy( saved_code, pKiUserCallbackDispatcher, sizeof(saved_code));
7828 *(void **)&patched_code[3] = hook_KiUserCallbackDispatcher;
7829 memcpy( pKiUserCallbackDispatcher, patched_code, sizeof(patched_code));
7831
7832 DestroyWindow( CreateWindowA( "Static", "test", 0, 0, 0, 0, 0, 0, 0, 0, 0 ));
7833
7834 memcpy( pKiUserCallbackDispatcher, saved_code, sizeof(saved_code));
7836 VirtualProtect( pKiUserCallbackDispatcher, sizeof(saved_code), old_protect, &old_protect );
7837}
7838
7839static void run_exception_test(void *handler, const void* context,
7840 const void *code, unsigned int code_size,
7841 unsigned int func2_offset, DWORD access, DWORD handler_flags,
7842 void *arg1, void *arg2)
7843{
7844 DWORD buf[14];
7845 RUNTIME_FUNCTION runtime_func[2];
7846 IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA unwind;
7847 void (*func)(void*,void*) = code_mem;
7848 DWORD oldaccess, oldaccess2;
7849
7850 runtime_func[0].BeginAddress = 0;
7851 runtime_func[0].UnwindData = 0x1000;
7852 runtime_func[1].BeginAddress = func2_offset;
7853 runtime_func[1].UnwindData = 0x1014;
7854
7855 unwind.FunctionLength = func2_offset / 4;
7856 unwind.Version = 0;
7857 unwind.ExceptionDataPresent = 1;
7858 unwind.EpilogInHeader = 1;
7859 unwind.EpilogCount = 1;
7860 unwind.CodeWords = 1;
7861 buf[0] = unwind.HeaderData;
7862 buf[1] = 0xe3e481e1; /* mov x29,sp; stp r29,lr,[sp,-#0x10]!; end; nop */
7863 buf[2] = 0x1028;
7864 *(const void **)&buf[3] = context;
7865
7866 unwind.FunctionLength = (code_size - func2_offset) / 4;
7867 buf[5] = unwind.HeaderData;
7868 buf[6] = 0xe3e481e1; /* mov x29,sp; stp r29,lr,[sp,-#0x10]!; end; nop */
7869 buf[7] = 0x1028;
7870 *(const void **)&buf[8] = context;
7871
7872 buf[10] = 0x5800004f; /* ldr x15, 1f */
7873 buf[11] = 0xd61f01e0; /* br x15 */
7874 *(const void **)&buf[12] = handler;
7875
7876 memcpy((unsigned char *)code_mem + 0x1000, buf, sizeof(buf));
7877 memcpy(code_mem, code, code_size);
7878 if (access) VirtualProtect(code_mem, code_size, access, &oldaccess);
7880
7881 pRtlAddFunctionTable(runtime_func, ARRAY_SIZE(runtime_func), (ULONG_PTR)code_mem);
7882 func( arg1, arg2 );
7883 pRtlDeleteFunctionTable(runtime_func);
7884
7885 if (access) VirtualProtect(code_mem, code_size, oldaccess, &oldaccess2);
7886}
7887
7888static BOOL got_nested_exception, got_prev_frame_exception;
7889static void *nested_exception_initial_frame;
7890
7891static DWORD nested_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
7893{
7894 trace("nested_exception_handler pc %p, sp %p, code %#lx, flags %#lx, ExceptionAddress %p.\n",
7895 (void *)context->Pc, (void *)context->Sp, rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress);
7896
7897 if (rec->ExceptionCode == 0x80000003 && !(rec->ExceptionFlags & EXCEPTION_NESTED_CALL))
7898 {
7899 ok(rec->NumberParameters == 1, "Got unexpected rec->NumberParameters %lu.\n", rec->NumberParameters);
7900 ok((char *)context->Sp == (char *)frame - 0x10, "Got unexpected frame %p / %p.\n", frame, (void *)context->Sp);
7901 ok((char *)context->Fp == (char *)frame - 0x10, "Got unexpected frame %p / %p.\n", frame, (void *)context->Fp);
7902 ok((char *)context->Lr == (char *)code_mem + 0x0c, "Got unexpected lr %p.\n", (void *)context->Lr);
7903 ok((char *)context->Pc == (char *)code_mem + 0x1c, "Got unexpected pc %p.\n", (void *)context->Pc);
7904
7905 nested_exception_initial_frame = frame;
7906 RaiseException(0xdeadbeef, 0, 0, 0);
7907 context->Pc += 4;
7909 }
7910
7911 if (rec->ExceptionCode == 0xdeadbeef &&
7914 {
7915 ok(!rec->NumberParameters, "Got unexpected rec->NumberParameters %lu.\n", rec->NumberParameters);
7916 got_nested_exception = TRUE;
7917 ok(frame == nested_exception_initial_frame, "Got unexpected frame %p / %p.\n",
7918 frame, nested_exception_initial_frame);
7920 }
7921
7922 ok(rec->ExceptionCode == 0xdeadbeef && (!rec->ExceptionFlags || rec->ExceptionFlags == EXCEPTION_SOFTWARE_ORIGINATE),
7923 "Got unexpected exception code %#lx, flags %#lx.\n", rec->ExceptionCode, rec->ExceptionFlags);
7924 ok(!rec->NumberParameters, "Got unexpected rec->NumberParameters %lu.\n", rec->NumberParameters);
7925 ok((char *)frame == (char *)nested_exception_initial_frame + 0x10, "Got unexpected frame %p / %p.\n",
7926 frame, nested_exception_initial_frame);
7927 got_prev_frame_exception = TRUE;
7929}
7930
7931static const DWORD nested_except_code[] =
7932{
7933 0xa9bf7bfd, /* 00: stp x29, x30, [sp, #-16]! */
7934 0x910003fd, /* 04: mov x29, sp */
7935 0x94000003, /* 08: bl 1f */
7936 0xa8c17bfd, /* 0c: ldp x29, x30, [sp], #16 */
7937 0xd65f03c0, /* 10: ret */
7938
7939 0xa9bf7bfd, /* 14: stp x29, x30, [sp, #-16]! */
7940 0x910003fd, /* 18: mov x29, sp */
7941 0xd43e0000, /* 1c: brk #0xf000 */
7942 0xd503201f, /* 20: nop */
7943 0xa8c17bfd, /* 24: ldp x29, x30, [sp], #16 */
7944 0xd65f03c0, /* 28: ret */
7945};
7946
7947static void test_nested_exception(void)
7948{
7949 got_nested_exception = got_prev_frame_exception = FALSE;
7950 run_exception_test(nested_exception_handler, NULL, nested_except_code, sizeof(nested_except_code),
7951 5 * sizeof(DWORD), PAGE_EXECUTE_READ, UNW_FLAG_EHANDLER, 0, 0);
7952 ok(got_nested_exception, "Did not get nested exception.\n");
7953 ok(got_prev_frame_exception, "Did not get nested exception in the previous frame.\n");
7954}
7955
7956static unsigned int collided_unwind_exception_count;
7957
7958static DWORD collided_exception_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
7960{
7961 CONTEXT ctx;
7962
7963 trace("collided_exception_handler pc %p, sp %p, code %#lx, flags %#lx, ExceptionAddress %p, frame %p.\n",
7964 (void *)context->Pc, (void *)context->Sp, rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, frame);
7965
7966 switch(collided_unwind_exception_count++)
7967 {
7968 case 0:
7969 /* Initial exception from nested_except_code. */
7970 ok(rec->ExceptionCode == STATUS_BREAKPOINT, "got %#lx.\n", rec->ExceptionCode);
7971 nested_exception_initial_frame = frame;
7972 /* Start unwind. */
7973 pRtlUnwindEx((char *)frame + 0x10, (char *)code_mem + 0x0c, NULL, NULL, &ctx, NULL);
7974 ok(0, "shouldn't be reached\n");
7975 break;
7976 case 1:
7977 ok(rec->ExceptionCode == STATUS_UNWIND, "got %#lx.\n", rec->ExceptionCode);
7978 ok(rec->ExceptionFlags == EXCEPTION_UNWINDING, "got %#lx.\n", rec->ExceptionFlags);
7979 ok((char *)context->Pc == (char *)code_mem + 0x1c, "got %p.\n", (void *)context->Pc);
7980 /* generate exception in unwind handler. */
7981 RaiseException(0xdeadbeef, 0, 0, 0);
7982 ok(0, "shouldn't be reached\n");
7983 break;
7984 case 2:
7985 /* Inner call frame, continue search. */
7986 ok(rec->ExceptionCode == 0xdeadbeef, "got %#lx.\n", rec->ExceptionCode);
7987 ok(!rec->ExceptionFlags || rec->ExceptionFlags == EXCEPTION_SOFTWARE_ORIGINATE, "got %#lx.\n", rec->ExceptionFlags);
7988 ok(frame == nested_exception_initial_frame, "got %p, expected %p.\n", frame, nested_exception_initial_frame);
7989 break;
7990 case 3:
7991 /* Top level call frame, handle exception by unwinding. */
7992 ok(rec->ExceptionCode == 0xdeadbeef, "got %#lx.\n", rec->ExceptionCode);
7993 ok(!rec->ExceptionFlags || rec->ExceptionFlags == EXCEPTION_SOFTWARE_ORIGINATE, "got %#lx.\n", rec->ExceptionFlags);
7994 ok((char *)frame == (char *)nested_exception_initial_frame + 0x10, "got %p, expected %p.\n", frame, nested_exception_initial_frame);
7995 pRtlUnwindEx((char *)nested_exception_initial_frame + 0x10, (char *)code_mem + 0x0c, NULL, NULL, &ctx, NULL);
7996 ok(0, "shouldn't be reached\n");
7997 break;
7998 case 4:
7999 /* Collided unwind. */
8000 ok(rec->ExceptionCode == STATUS_UNWIND, "got %#lx.\n", rec->ExceptionCode);
8002 ok(frame == nested_exception_initial_frame, "got %p, expected %p.\n", frame, nested_exception_initial_frame);
8003 break;
8004 case 5:
8005 /* EXCEPTION_COLLIDED_UNWIND cleared for the following frames. */
8006 ok(rec->ExceptionCode == STATUS_UNWIND, "got %#lx.\n", rec->ExceptionCode);
8008 ok((char *)frame == (char *)nested_exception_initial_frame + 0x10, "got %p, expected %p.\n", frame, nested_exception_initial_frame);
8009 break;
8010 }
8012}
8013
8014static void test_collided_unwind(void)
8015{
8016 got_nested_exception = got_prev_frame_exception = FALSE;
8017 collided_unwind_exception_count = 0;
8018 run_exception_test(collided_exception_handler, NULL, nested_except_code, sizeof(nested_except_code),
8019 5 * sizeof(DWORD), PAGE_EXECUTE_READ, UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER, 0, 0);
8020 ok(collided_unwind_exception_count == 6, "got %u.\n", collided_unwind_exception_count);
8021}
8022
8023
8024static int rtlraiseexception_unhandled_handler_called;
8025static int rtlraiseexception_teb_handler_called;
8026static int rtlraiseexception_handler_called;
8027
8028static void rtlraiseexception_handler_( EXCEPTION_RECORD *rec, void *frame, CONTEXT *context,
8029 void *dispatcher, BOOL unhandled_handler )
8030{
8031 void *addr = rec->ExceptionAddress;
8032
8033 trace( "exception: %08lx flags:%lx addr:%p context: Pc:%p\n",
8034 rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress, (void *)context->Pc );
8035
8036 ok( addr == (char *)code_mem + 0x0c,
8037 "ExceptionAddress at %p instead of %p\n", addr, (char *)code_mem + 0x0c );
8038 ok( context->ContextFlags == (CONTEXT_FULL | CONTEXT_UNWOUND_TO_CALL) ||
8040 "wrong context flags %lx\n", context->ContextFlags );
8041 ok( context->Pc == (UINT_PTR)addr,
8042 "%d: Pc at %Ix instead of %Ix\n", test_stage, context->Pc, (UINT_PTR)addr );
8043
8044 ok( context->X0 == 0xf00f00f0, "context->X0 is %Ix, should have been set to 0xf00f00f0 in vectored handler\n", context->X0 );
8045}
8046
8047static LONG CALLBACK rtlraiseexception_unhandled_handler(EXCEPTION_POINTERS *ExceptionInfo)
8048{
8049 PCONTEXT context = ExceptionInfo->ContextRecord;
8050 PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
8051
8052 rtlraiseexception_unhandled_handler_called = 1;
8053 rtlraiseexception_handler_(rec, NULL, context, NULL, TRUE);
8055
8057}
8058
8059static DWORD WINAPI rtlraiseexception_teb_handler( EXCEPTION_RECORD *rec,
8062 EXCEPTION_REGISTRATION_RECORD **dispatcher )
8063{
8064 rtlraiseexception_teb_handler_called = 1;
8065 rtlraiseexception_handler_(rec, frame, context, dispatcher, FALSE);
8067}
8068
8069static DWORD WINAPI rtlraiseexception_handler( EXCEPTION_RECORD *rec, void *frame,
8070 CONTEXT *context, DISPATCHER_CONTEXT *dispatcher )
8071{
8072 DISPATCHER_CONTEXT_NONVOLREG_ARM64 *nonvol_regs = (void *)dispatcher->NonVolatileRegisters;
8073 int i;
8074
8075 for (i = 0; i < NONVOL_INT_NUMREG_ARM64; i++)
8076 ok( nonvol_regs->GpNvRegs[i] == ((DWORD64 *)&context->X19)[i],
8077 "wrong non volatile reg x%u %I64x / %I64x\n", i + 19,
8078 nonvol_regs->GpNvRegs[i] , ((DWORD64 *)&context->X19)[i] );
8079 for (i = 0; i < NONVOL_FP_NUMREG_ARM64; i++)
8080 ok( nonvol_regs->FpNvRegs[i] == context->V[i + 8].D[0],
8081 "wrong non volatile reg d%u %g / %g\n", i + 8,
8082 nonvol_regs->FpNvRegs[i] , context->V[i + 8].D[0] );
8083
8084 rtlraiseexception_handler_called = 1;
8085 rtlraiseexception_handler_(rec, frame, context, dispatcher, FALSE);
8087}
8088
8089static LONG CALLBACK rtlraiseexception_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
8090{
8091 PCONTEXT context = ExceptionInfo->ContextRecord;
8092 PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
8093 void *addr = rec->ExceptionAddress;
8094
8095 ok( addr == (char *)code_mem + 0xc,
8096 "ExceptionAddress at %p instead of %p\n", addr, (char *)code_mem + 0xc );
8097 ok( context->Pc == (UINT_PTR)addr,
8098 "%d: Pc at %Ix instead of %Ix\n", test_stage, context->Pc, (UINT_PTR)addr );
8099
8100 context->X0 = 0xf00f00f0;
8101
8103}
8104
8105static const DWORD call_one_arg_code[] =
8106{
8107 0xa9bf7bfd, /* 00: stp x29, x30, [sp, #-16]! */
8108 0x910003fd, /* 04: mov x29, sp */
8109 0xd63f0020, /* 08: blr x1 */
8110 0xd503201f, /* 0c: nop */
8111 0xa8c17bfd, /* 10: ldp x29, x30, [sp], #16 */
8112 0xd65f03c0, /* 14: ret */
8113};
8114
8115static void run_rtlraiseexception_test(DWORD exceptioncode)
8116{
8119 PVOID vectored_handler = NULL;
8120
8121 record.ExceptionCode = exceptioncode;
8122 record.ExceptionFlags = 0;
8123 record.ExceptionRecord = NULL;
8124 record.ExceptionAddress = NULL; /* does not matter, copied return address */
8125 record.NumberParameters = 0;
8126
8127 frame.Handler = rtlraiseexception_teb_handler;
8128 frame.Prev = NtCurrentTeb()->Tib.ExceptionList;
8129
8130 NtCurrentTeb()->Tib.ExceptionList = &frame;
8131 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, rtlraiseexception_vectored_handler);
8132 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
8133 if (pRtlSetUnhandledExceptionFilter) pRtlSetUnhandledExceptionFilter(rtlraiseexception_unhandled_handler);
8134
8135 rtlraiseexception_handler_called = 0;
8136 rtlraiseexception_teb_handler_called = 0;
8137 rtlraiseexception_unhandled_handler_called = 0;
8138
8139 run_exception_test( rtlraiseexception_handler, NULL, call_one_arg_code,
8140 sizeof(call_one_arg_code), sizeof(call_one_arg_code),
8141 PAGE_EXECUTE_READ, UNW_FLAG_EHANDLER,
8142 &record, pRtlRaiseException);
8143
8144 ok( record.ExceptionAddress == (char *)code_mem + 0x0c,
8145 "address set to %p instead of %p\n", record.ExceptionAddress, (char *)code_mem + 0x0c );
8146
8147 todo_wine
8148 ok( !rtlraiseexception_teb_handler_called, "Frame TEB handler called\n" );
8149 ok( rtlraiseexception_handler_called, "Frame handler called\n" );
8150 ok( rtlraiseexception_unhandled_handler_called, "UnhandledExceptionFilter wasn't called\n" );
8151
8152 pRtlRemoveVectoredExceptionHandler(vectored_handler);
8153
8154 if (pRtlSetUnhandledExceptionFilter) pRtlSetUnhandledExceptionFilter(NULL);
8155 NtCurrentTeb()->Tib.ExceptionList = frame.Prev;
8156}
8157
8158static void test_rtlraiseexception(void)
8159{
8160 run_rtlraiseexception_test(0x12345);
8161 run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
8162 run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
8163}
8164
8165static DWORD brk_exception_handler_code;
8166
8167static DWORD WINAPI brk_exception_handler( EXCEPTION_RECORD *rec, void *frame,
8168 CONTEXT *context, DISPATCHER_CONTEXT *dispatcher )
8169{
8170 ok( rec->ExceptionCode == brk_exception_handler_code, "got: %08lx\n", rec->ExceptionCode );
8171 ok( rec->NumberParameters == 0, "got: %ld\n", rec->NumberParameters );
8172 ok( rec->ExceptionAddress == (void *)context->Pc, "got addr: %p, pc: %p\n", rec->ExceptionAddress, (void *)context->Pc );
8173 context->Pc += 4;
8175}
8176
8177
8178static void test_brk(void)
8179{
8180 DWORD call_brk[] =
8181 {
8182 0xa9bf7bfd, /* 00: stp x29, x30, [sp, #-16]! */
8183 0x910003fd, /* 04: mov x29, sp */
8184 0x00000000, /* 08: <filled in later> */
8185 0xd503201f, /* 0c: nop */
8186 0xa8c17bfd, /* 10: ldp x29, x30, [sp], #16 */
8187 0xd65f03c0, /* 14: ret */
8188 };
8189
8190 /* brk #0xf000 is tested as part of breakpoint tests */
8191
8192 brk_exception_handler_code = STATUS_ASSERTION_FAILURE;
8193 call_brk[2] = 0xd43e0020; /* 08: brk #0xf001 */
8194 run_exception_test( brk_exception_handler, NULL, call_brk,
8195 sizeof(call_brk), sizeof(call_brk),
8196 PAGE_EXECUTE_READ, UNW_FLAG_EHANDLER,
8197 0, 0 );
8198
8199 /* FIXME: brk #0xf002 needs debug service tests */
8200
8201 /* brk #0xf003 is tested as part of fastfail tests*/
8202
8203 brk_exception_handler_code = EXCEPTION_INT_DIVIDE_BY_ZERO;
8204 call_brk[2] = 0xd43e0080; /* 08: brk #0xf004 */
8205 run_exception_test( brk_exception_handler, NULL, call_brk,
8206 sizeof(call_brk), sizeof(call_brk),
8207 PAGE_EXECUTE_READ, UNW_FLAG_EHANDLER,
8208 0, 0 );
8209
8210 /* Any unknown immediate raises EXCEPTION_ILLEGAL_INSTRUCTION */
8211
8212 brk_exception_handler_code = EXCEPTION_ILLEGAL_INSTRUCTION;
8213 call_brk[2] = 0xd43e00a0; /* 08: brk #0xf005 */
8214 run_exception_test( brk_exception_handler, NULL, call_brk,
8215 sizeof(call_brk), sizeof(call_brk),
8216 PAGE_EXECUTE_READ, UNW_FLAG_EHANDLER,
8217 0, 0 );
8218
8219 brk_exception_handler_code = EXCEPTION_ILLEGAL_INSTRUCTION;
8220 call_brk[2] = 0xd4200000; /* 08: brk #0x0 */
8221 run_exception_test( brk_exception_handler, NULL, call_brk,
8222 sizeof(call_brk), sizeof(call_brk),
8223 PAGE_EXECUTE_READ, UNW_FLAG_EHANDLER,
8224 0, 0 );
8225}
8226
8227static LONG consolidate_dummy_called;
8228static LONG pass;
8229
8230static const DWORD call_rtlunwind[] =
8231{
8232 0xa88150f3, /* stp x19, x20, [x7], #0x10 */
8233 0xa88158f5, /* stp x21, x22, [x7], #0x10 */
8234 0xa88160f7, /* stp x23, x24, [x7], #0x10 */
8235 0xa88168f9, /* stp x25, x26, [x7], #0x10 */
8236 0xa88170fb, /* stp x27, x28, [x7], #0x10 */
8237 0xf80084fd, /* str x29, [x7], #0x8 */
8238 0x6c8124e8, /* stp d8, d9, [x7], #0x10 */
8239 0x6c812cea, /* stp d10, d11, [x7], #0x10 */
8240 0x6c8134ec, /* stp d12, d13, [x7], #0x10 */
8241 0x6c813cee, /* stp d14, d15, [x7], #0x10 */
8242 0xd61f00c0, /* br x6 */
8243};
8244
8245static PVOID CALLBACK test_consolidate_dummy(EXCEPTION_RECORD *rec)
8246{
8248 DWORD64 *saved_regs = (DWORD64 *)rec->ExceptionInformation[3];
8250 int i;
8251
8252 switch (InterlockedIncrement(&consolidate_dummy_called))
8253 {
8254 case 1: /* RtlRestoreContext */
8255 ok(ctx->Pc == 0xdeadbeef, "RtlRestoreContext wrong Pc, expected: 0xdeadbeef, got: %Ix\n", ctx->Pc);
8256 ok( rec->ExceptionInformation[10] == -1, "wrong info %Ix\n", rec->ExceptionInformation[10] );
8257 break;
8258 case 2: /* RtlUnwindEx */
8259 ok(ctx->Pc != 0xdeadbeef, "RtlUnwindEx wrong Pc, got: %Ix\n", ctx->Pc );
8260 ok( rec->ExceptionInformation[10] != -1, "wrong info %Ix\n", rec->ExceptionInformation[10] );
8262 for (i = 0; i < 11; i++)
8263 ok( saved_regs[i] == regs->GpNvRegs[i], "wrong reg X%u, expected: %Ix, got: %Ix\n",
8264 19 + i, saved_regs[i], regs->GpNvRegs[i] );
8265 for (i = 0; i < 8; i++)
8266 ok( saved_regs[i + 11] == *(DWORD64 *)&regs->FpNvRegs[i],
8267 "wrong reg D%u, expected: %Ix, got: %Ix\n",
8268 i + 8, saved_regs[i + 11], *(DWORD64 *)&regs->FpNvRegs[i] );
8269 break;
8270 }
8271 return (PVOID)rec->ExceptionInformation[2];
8272}
8273
8274static void test_restore_context(void)
8275{
8276 EXCEPTION_RECORD rec;
8277 _JUMP_BUFFER buf;
8278 CONTEXT ctx;
8279 int i;
8280
8281 if (!pRtlUnwindEx || !pRtlRestoreContext || !pRtlCaptureContext)
8282 {
8283 skip("RtlUnwindEx/RtlCaptureContext/RtlRestoreContext not found\n");
8284 return;
8285 }
8286
8287 /* test simple case of capture and restore context */
8288 pass = 0;
8289 InterlockedIncrement(&pass); /* interlocked to prevent compiler from moving after capture */
8290 pRtlCaptureContext(&ctx);
8291 if (InterlockedIncrement(&pass) == 2) /* interlocked to prevent compiler from moving before capture */
8292 {
8293 pRtlRestoreContext(&ctx, NULL);
8294 ok(0, "shouldn't be reached\n");
8295 }
8296 else
8297 ok(pass < 4, "unexpected pass %ld\n", pass);
8298
8299 /* test with jmp using RtlRestoreContext */
8300 pass = 0;
8303 InterlockedIncrement(&pass); /* only called once */
8304 setjmp((_JBTYPE *)&buf);
8306 if (pass == 3)
8307 {
8309 rec.NumberParameters = 1;
8310 rec.ExceptionInformation[0] = (DWORD64)&buf;
8311 /* uses buf.Pc instead of ctx.Pc */
8312 pRtlRestoreContext(&ctx, &rec);
8313 ok(0, "shouldn't be reached\n");
8314 }
8315 else if (pass == 4)
8316 {
8317 ok(buf.X19 == ctx.X19, "longjmp failed for X19, expected: %Ix, got: %Ix\n", buf.X19, ctx.X19);
8318 ok(buf.X20 == ctx.X20, "longjmp failed for X20, expected: %Ix, got: %Ix\n", buf.X20, ctx.X20);
8319 ok(buf.X21 == ctx.X21, "longjmp failed for X21, expected: %Ix, got: %Ix\n", buf.X21, ctx.X21);
8320 ok(buf.X22 == ctx.X22, "longjmp failed for X22, expected: %Ix, got: %Ix\n", buf.X22, ctx.X22);
8321 ok(buf.X23 == ctx.X23, "longjmp failed for X23, expected: %Ix, got: %Ix\n", buf.X23, ctx.X23);
8322 ok(buf.X24 == ctx.X24, "longjmp failed for X24, expected: %Ix, got: %Ix\n", buf.X24, ctx.X24);
8323 ok(buf.X25 == ctx.X25, "longjmp failed for X25, expected: %Ix, got: %Ix\n", buf.X25, ctx.X25);
8324 ok(buf.X26 == ctx.X26, "longjmp failed for X26, expected: %Ix, got: %Ix\n", buf.X26, ctx.X26);
8325 ok(buf.X27 == ctx.X27, "longjmp failed for X27, expected: %Ix, got: %Ix\n", buf.X27, ctx.X27);
8326 ok(buf.X28 == ctx.X28, "longjmp failed for X28, expected: %Ix, got: %Ix\n", buf.X28, ctx.X28);
8327 ok(buf.Fp == ctx.Fp, "longjmp failed for Fp, expected: %Ix, got: %Ix\n", buf.Fp, ctx.Fp);
8328 for (i = 0; i < 8; i++)
8329 ok(buf.D[i] == ctx.V[i + 8].D[0], "longjmp failed for D%u, expected: %g, got: %g\n",
8330 i + 8, buf.D[i], ctx.V[i + 8].D[0]);
8331 pRtlRestoreContext(&ctx, &rec);
8332 ok(0, "shouldn't be reached\n");
8333 }
8334 else
8335 ok(pass == 5, "unexpected pass %ld\n", pass);
8336
8337 /* test with jmp through RtlUnwindEx */
8338 pass = 0;
8340 pRtlCaptureContext(&ctx);
8341 InterlockedIncrement(&pass); /* only called once */
8342 setjmp((_JBTYPE *)&buf);
8344 if (pass == 3)
8345 {
8347 rec.NumberParameters = 1;
8348 rec.ExceptionInformation[0] = (DWORD64)&buf;
8349
8350 /* uses buf.Pc instead of bogus 0xdeadbeef */
8351 pRtlUnwindEx((void*)buf.Sp, (void*)0xdeadbeef, &rec, NULL, &ctx, NULL);
8352 ok(0, "shouldn't be reached\n");
8353 }
8354 else
8355 ok(pass == 4, "unexpected pass %ld\n", pass);
8356
8357
8358 /* test with consolidate */
8359 pass = 0;
8363 if (pass == 2)
8364 {
8366 rec.NumberParameters = 3;
8367 rec.ExceptionInformation[0] = (DWORD64)test_consolidate_dummy;
8368 rec.ExceptionInformation[1] = (DWORD64)&ctx;
8369 rec.ExceptionInformation[2] = ctx.Pc;
8370 rec.ExceptionInformation[10] = -1;
8371 ctx.Pc = 0xdeadbeef;
8372
8373 pRtlRestoreContext(&ctx, &rec);
8374 ok(0, "shouldn't be reached\n");
8375 }
8376 else if (pass == 3)
8377 ok(consolidate_dummy_called == 1, "test_consolidate_dummy not called\n");
8378 else
8379 ok(0, "unexpected pass %ld\n", pass);
8380
8381 /* test with consolidate through RtlUnwindEx */
8382 pass = 0;
8384 pRtlCaptureContext(&ctx);
8386 setjmp((_JBTYPE *)&buf);
8387 if (pass == 2)
8388 {
8389 void (*func)(DWORD64,DWORD64,EXCEPTION_RECORD*,DWORD64,CONTEXT*,void*,void*,void*) = code_mem;
8390 DWORD64 nonvol_regs[19];
8391
8393 rec.NumberParameters = 4;
8394 rec.ExceptionInformation[0] = (DWORD64)test_consolidate_dummy;
8395 rec.ExceptionInformation[1] = (DWORD64)&ctx;
8396 rec.ExceptionInformation[2] = ctx.Pc;
8397 rec.ExceptionInformation[3] = (DWORD64)nonvol_regs;
8398 rec.ExceptionInformation[10] = -1; /* otherwise it doesn't get set */
8399 ctx.Pc = 0xdeadbeef;
8400 /* uses consolidate callback Pc instead of bogus 0xdeadbeef */
8401 memcpy( code_mem, call_rtlunwind, sizeof(call_rtlunwind) );
8402 FlushInstructionCache( GetCurrentProcess(), code_mem, sizeof(call_rtlunwind) );
8403 func( buf.Frame, 0xdeadbeef, &rec, 0, &ctx, NULL, pRtlUnwindEx, nonvol_regs );
8404 ok(0, "shouldn't be reached\n");
8405 }
8406 else if (pass == 3)
8407 ok(consolidate_dummy_called == 2, "test_consolidate_dummy not called\n");
8408 else
8409 ok(0, "unexpected pass %ld\n", pass);
8410}
8411
8412static void test_mrs_currentel(void)
8413{
8416
8417 static const DWORD call_func[] =
8418 {
8419 0xd5384240, /* mrs x0, CurrentEL */
8420 0xd538425f, /* mrs xzr, CurrentEL */
8421 0xd65f03c0, /* ret */
8422 };
8423
8424 memcpy( func_ptr, call_func, sizeof(call_func) );
8425 FlushInstructionCache( GetCurrentProcess(), func_ptr, sizeof(call_func) );
8426 result = func_ptr();
8427 ok( result == 0, "expected 0, got %llx\n", result );
8428}
8429
8430
8431#endif /* __aarch64__ */
8432
8433#if defined(__i386__) || defined(__x86_64__)
8434
8435static DWORD WINAPI register_check_thread(void *arg)
8436{
8438 CONTEXT ctx;
8439
8440 memset(&ctx, 0, sizeof(ctx));
8441 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
8442
8443 status = pNtGetContextThread(GetCurrentThread(), &ctx);
8444 ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %lx\n", status);
8445 ok(!ctx.Dr0, "expected 0, got %Ix\n", (DWORD_PTR)ctx.Dr0);
8446 ok(!ctx.Dr1, "expected 0, got %Ix\n", (DWORD_PTR)ctx.Dr1);
8447 ok(!ctx.Dr2, "expected 0, got %Ix\n", (DWORD_PTR)ctx.Dr2);
8448 ok(!ctx.Dr3, "expected 0, got %Ix\n", (DWORD_PTR)ctx.Dr3);
8449 ok(!ctx.Dr6, "expected 0, got %Ix\n", (DWORD_PTR)ctx.Dr6);
8450 ok(!ctx.Dr7, "expected 0, got %Ix\n", (DWORD_PTR)ctx.Dr7);
8451
8452 return 0;
8453}
8454
8455static void test_debug_registers(void)
8456{
8457 static const struct
8458 {
8459 ULONG_PTR dr0, dr1, dr2, dr3, dr6, dr7;
8460 }
8461 tests[] =
8462 {
8463 { 0x42424240, 0, 0x126bb070, 0x0badbad0, 0, 0xffff0115 },
8464 { 0x42424242, 0, 0x100f0fe7, 0x0abebabe, 0, 0x115 },
8465 };
8467 CONTEXT ctx;
8468 HANDLE thread;
8469 int i;
8470
8471 for (i = 0; i < ARRAY_SIZE(tests); i++)
8472 {
8473 memset(&ctx, 0, sizeof(ctx));
8474 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
8475 ctx.Dr0 = tests[i].dr0;
8476 ctx.Dr1 = tests[i].dr1;
8477 ctx.Dr2 = tests[i].dr2;
8478 ctx.Dr3 = tests[i].dr3;
8479 ctx.Dr6 = tests[i].dr6;
8480 ctx.Dr7 = tests[i].dr7;
8481
8482 status = pNtSetContextThread(GetCurrentThread(), &ctx);
8483 ok(status == STATUS_SUCCESS, "NtSetContextThread failed with %08lx\n", status);
8484
8485 memset(&ctx, 0xcc, sizeof(ctx));
8486 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
8487
8488 status = pNtGetContextThread(GetCurrentThread(), &ctx);
8489 ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %08lx\n", status);
8490 if (is_arm64ec) /* setting debug registers is silently ignored */
8491 {
8492 ok(!ctx.Dr0, "test %d: expected 0, got %Ix\n", i, (DWORD_PTR)ctx.Dr0);
8493 ok(!ctx.Dr1, "test %d: expected 0, got %Ix\n", i, (DWORD_PTR)ctx.Dr1);
8494 ok(!ctx.Dr2, "test %d: expected 0, got %Ix\n", i, (DWORD_PTR)ctx.Dr2);
8495 ok(!ctx.Dr3, "test %d: expected 0, got %Ix\n", i, (DWORD_PTR)ctx.Dr3);
8496 ok(!ctx.Dr6, "test %d: expected 0, got %Ix\n", i, (DWORD_PTR)ctx.Dr6);
8497 ok(!ctx.Dr7, "test %d: expected 0, got %Ix\n", i, (DWORD_PTR)ctx.Dr7);
8498 }
8499 else
8500 {
8501 ok(ctx.Dr0 == tests[i].dr0, "test %d: expected %Ix, got %Ix\n", i, tests[i].dr0, (DWORD_PTR)ctx.Dr0);
8502 ok(ctx.Dr1 == tests[i].dr1, "test %d: expected %Ix, got %Ix\n", i, tests[i].dr1, (DWORD_PTR)ctx.Dr1);
8503 ok(ctx.Dr2 == tests[i].dr2, "test %d: expected %Ix, got %Ix\n", i, tests[i].dr2, (DWORD_PTR)ctx.Dr2);
8504 ok(ctx.Dr3 == tests[i].dr3, "test %d: expected %Ix, got %Ix\n", i, tests[i].dr3, (DWORD_PTR)ctx.Dr3);
8505 ok((ctx.Dr6 & 0xf00f) == tests[i].dr6, "test %d: expected %Ix, got %Ix\n", i, tests[i].dr6, (DWORD_PTR)ctx.Dr6);
8506 ok((ctx.Dr7 & ~0xdc00) == tests[i].dr7, "test %d: expected %Ix, got %Ix\n", i, tests[i].dr7, (DWORD_PTR)ctx.Dr7);
8507 }
8508 }
8509
8510 memset(&ctx, 0, sizeof(ctx));
8511 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
8512 ctx.Dr0 = 0xffffffff;
8513 ctx.Dr1 = 0xffffffff;
8514 ctx.Dr2 = 0xffffffff;
8515 ctx.Dr3 = 0xffffffff;
8516 ctx.Dr6 = 0xffffffff;
8517 ctx.Dr7 = 0x00000400;
8518 status = pNtSetContextThread(GetCurrentThread(), &ctx);
8519 ok(status == STATUS_SUCCESS, "NtSetContextThread failed with %lx\n", status);
8520
8521 thread = CreateThread(NULL, 0, register_check_thread, NULL, CREATE_SUSPENDED, NULL);
8522 ok(thread != INVALID_HANDLE_VALUE, "CreateThread failed with %ld\n", GetLastError());
8523
8524 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
8525 status = pNtGetContextThread(thread, &ctx);
8526 ok(status == STATUS_SUCCESS, "NtGetContextThread failed with %lx\n", status);
8527 ok(!ctx.Dr0, "expected 0, got %Ix\n", (DWORD_PTR)ctx.Dr0);
8528 ok(!ctx.Dr1, "expected 0, got %Ix\n", (DWORD_PTR)ctx.Dr1);
8529 ok(!ctx.Dr2, "expected 0, got %Ix\n", (DWORD_PTR)ctx.Dr2);
8530 ok(!ctx.Dr3, "expected 0, got %Ix\n", (DWORD_PTR)ctx.Dr3);
8531 ok(!ctx.Dr6, "expected 0, got %Ix\n", (DWORD_PTR)ctx.Dr6);
8532 ok(!ctx.Dr7, "expected 0, got %Ix\n", (DWORD_PTR)ctx.Dr7);
8533
8537}
8538
8539#if defined(__x86_64__)
8540
8541static void test_debug_registers_wow64(void)
8542{
8543 char cmdline[] = "C:\\windows\\syswow64\\msinfo32.exe";
8545 STARTUPINFOA si = {0};
8546 WOW64_CONTEXT wow64_ctx;
8547 CONTEXT ctx;
8548 BOOL is_wow64;
8549 NTSTATUS ret;
8550 BOOL bret;
8551
8552#ifdef __REACTOS__
8553 if ((pRtlWow64GetThreadContext == NULL) ||
8554 (pRtlWow64SetThreadContext == NULL))
8555 {
8556 skip("RtlWow64Get/SetThreadContext not found\n");
8557 return;
8558 }
8559#endif
8560
8561 si.cb = sizeof(si);
8562 bret = CreateProcessA(cmdline, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
8563 ok(bret, "CreateProcessA failed\n");
8564
8565 bret = pIsWow64Process(pi.hProcess, &is_wow64);
8566 ok(bret && is_wow64, "expected Wow64 process\n");
8567
8569
8570 ZeroMemory(&ctx, sizeof(ctx));
8571 ctx.ContextFlags = CONTEXT_ALL;
8572 bret = GetThreadContext(pi.hThread, &ctx);
8573 ok(bret, "GetThreadContext failed\n");
8574
8575 ctx.Dr0 = 0x12340000;
8576 ctx.Dr1 = 0x12340001;
8577 ctx.Dr2 = 0x12340002;
8578 ctx.Dr3 = 0x12340003;
8579 ctx.Dr7 = 0x155; /* enable all breakpoints (local) */
8580 bret = SetThreadContext(pi.hThread, &ctx);
8581 ok(bret, "SetThreadContext failed\n");
8582
8583 if (bret) {
8584 memset(&ctx, 0xcc, sizeof(ctx));
8585 ctx.ContextFlags = CONTEXT_ALL;
8586 bret = GetThreadContext(pi.hThread, &ctx);
8587 ok(bret, "GetThreadContext failed\n");
8588 if (bret)
8589 {
8590 if (is_arm64ec)
8591 {
8592 ok(!ctx.Dr0, "expected 0, got %Ix\n", ctx.Dr0);
8593 ok(!ctx.Dr1, "expected 0, got %Ix\n", ctx.Dr1);
8594 ok(!ctx.Dr2, "expected 0, got %Ix\n", ctx.Dr2);
8595 ok(!ctx.Dr3, "expected 0, got %Ix\n", ctx.Dr3);
8596 ok(!ctx.Dr7, "expected 0, got %Ix\n", ctx.Dr7);
8597 }
8598 else
8599 {
8600 ok(ctx.Dr0 == 0x12340000, "expected 0x12340000, got %Ix\n", ctx.Dr0);
8601 ok(ctx.Dr1 == 0x12340001, "expected 0x12340001, got %Ix\n", ctx.Dr1);
8602 ok(ctx.Dr2 == 0x12340002, "expected 0x12340002, got %Ix\n", ctx.Dr2);
8603 ok(ctx.Dr3 == 0x12340003, "expected 0x12340003, got %Ix\n", ctx.Dr3);
8604 ok(ctx.Dr7 == 0x155, "expected 0x155, got %Ix\n", ctx.Dr7);
8605 }
8606 }
8607
8608 memset(&wow64_ctx, 0xcc, sizeof(wow64_ctx));
8609 wow64_ctx.ContextFlags = WOW64_CONTEXT_ALL;
8610 ret = pRtlWow64GetThreadContext(pi.hThread, &wow64_ctx);
8611 ok(ret == STATUS_SUCCESS, "Wow64GetThreadContext failed with %lx\n", ret);
8612 if (ret == STATUS_SUCCESS)
8613 {
8614 if (is_arm64ec)
8615 {
8616 ok(!wow64_ctx.Dr0, "expected 0, got %lx\n", wow64_ctx.Dr0);
8617 ok(!wow64_ctx.Dr1, "expected 0, got %lx\n", wow64_ctx.Dr1);
8618 ok(!wow64_ctx.Dr2, "expected 0, got %lx\n", wow64_ctx.Dr2);
8619 ok(!wow64_ctx.Dr3, "expected 0, got %lx\n", wow64_ctx.Dr3);
8620 ok(!wow64_ctx.Dr7, "expected 0, got %lx\n", wow64_ctx.Dr7);
8621 }
8622 else
8623 {
8624 ok(wow64_ctx.Dr0 == 0x12340000, "expected 0x12340000, got %lx\n", wow64_ctx.Dr0);
8625 ok(wow64_ctx.Dr1 == 0x12340001, "expected 0x12340001, got %lx\n", wow64_ctx.Dr1);
8626 ok(wow64_ctx.Dr2 == 0x12340002, "expected 0x12340002, got %lx\n", wow64_ctx.Dr2);
8627 ok(wow64_ctx.Dr3 == 0x12340003, "expected 0x12340003, got %lx\n", wow64_ctx.Dr3);
8628 ok(wow64_ctx.Dr7 == 0x155, "expected 0x155, got %lx\n", wow64_ctx.Dr7);
8629 }
8630 }
8631 }
8632
8633 wow64_ctx.Dr0 = 0x56780000;
8634 wow64_ctx.Dr1 = 0x56780001;
8635 wow64_ctx.Dr2 = 0x56780002;
8636 wow64_ctx.Dr3 = 0x56780003;
8637 wow64_ctx.Dr7 = 0x101; /* enable only the first breakpoint */
8638 ret = pRtlWow64SetThreadContext(pi.hThread, &wow64_ctx);
8639 ok(ret == STATUS_SUCCESS, "Wow64SetThreadContext failed with %lx\n", ret);
8640
8641 memset(&wow64_ctx, 0xcc, sizeof(wow64_ctx));
8642 wow64_ctx.ContextFlags = WOW64_CONTEXT_ALL;
8643 ret = pRtlWow64GetThreadContext(pi.hThread, &wow64_ctx);
8644 ok(ret == STATUS_SUCCESS, "Wow64GetThreadContext failed with %lx\n", ret);
8645 if (ret == STATUS_SUCCESS)
8646 {
8647 ok(wow64_ctx.Dr0 == 0x56780000, "expected 0x56780000, got %lx\n", wow64_ctx.Dr0);
8648 ok(wow64_ctx.Dr1 == 0x56780001, "expected 0x56780001, got %lx\n", wow64_ctx.Dr1);
8649 ok(wow64_ctx.Dr2 == 0x56780002, "expected 0x56780002, got %lx\n", wow64_ctx.Dr2);
8650 ok(wow64_ctx.Dr3 == 0x56780003, "expected 0x56780003, got %lx\n", wow64_ctx.Dr3);
8651 ok(wow64_ctx.Dr7 == 0x101, "expected 0x101, got %lx\n", wow64_ctx.Dr7);
8652 }
8653
8654 memset(&ctx, 0xcc, sizeof(ctx));
8655 ctx.ContextFlags = CONTEXT_ALL;
8656 bret = GetThreadContext(pi.hThread, &ctx);
8657 ok(bret, "GetThreadContext failed\n");
8658 if (bret)
8659 {
8660 if (is_arm64ec)
8661 {
8662 ok(!ctx.Dr0, "expected 0, got %Ix\n", ctx.Dr0);
8663 ok(!ctx.Dr1, "expected 0, got %Ix\n", ctx.Dr1);
8664 ok(!ctx.Dr2, "expected 0, got %Ix\n", ctx.Dr2);
8665 ok(!ctx.Dr3, "expected 0, got %Ix\n", ctx.Dr3);
8666 ok(!ctx.Dr7, "expected 0, got %Ix\n", ctx.Dr7);
8667 }
8668 else
8669 {
8670 ok(ctx.Dr0 == 0x56780000, "expected 0x56780000, got %Ix\n", ctx.Dr0);
8671 ok(ctx.Dr1 == 0x56780001, "expected 0x56780001, got %Ix\n", ctx.Dr1);
8672 ok(ctx.Dr2 == 0x56780002, "expected 0x56780002, got %Ix\n", ctx.Dr2);
8673 ok(ctx.Dr3 == 0x56780003, "expected 0x56780003, got %Ix\n", ctx.Dr3);
8674 ok(ctx.Dr7 == 0x101, "expected 0x101, got %Ix\n", ctx.Dr7);
8675 }
8676 }
8677
8682}
8683
8684#endif
8685
8686static DWORD debug_service_exceptions;
8687
8688static LONG CALLBACK debug_service_handler(EXCEPTION_POINTERS *ExceptionInfo)
8689{
8690 EXCEPTION_RECORD *rec = ExceptionInfo->ExceptionRecord;
8691
8692 ok(rec->ExceptionCode == EXCEPTION_BREAKPOINT, "ExceptionCode is %08lx instead of %08lx\n",
8694
8695#ifdef __i386__
8696 ok(ExceptionInfo->ContextRecord->Eip == (DWORD)code_mem + 0x1c,
8697 "expected Eip = %lx, got %lx\n", (DWORD)code_mem + 0x1c, ExceptionInfo->ContextRecord->Eip);
8698 ok(rec->NumberParameters == (is_wow64 ? 1 : 3),
8699 "ExceptionParameters is %ld instead of %d\n", rec->NumberParameters, is_wow64 ? 1 : 3);
8700 ok(rec->ExceptionInformation[0] == ExceptionInfo->ContextRecord->Eax,
8701 "expected ExceptionInformation[0] = %lx, got %Ix\n",
8702 ExceptionInfo->ContextRecord->Eax, rec->ExceptionInformation[0]);
8703 if (!is_wow64)
8704 {
8705 ok(rec->ExceptionInformation[1] == 0x11111111,
8706 "got ExceptionInformation[1] = %Ix\n", rec->ExceptionInformation[1]);
8707 ok(rec->ExceptionInformation[2] == 0x22222222,
8708 "got ExceptionInformation[2] = %Ix\n", rec->ExceptionInformation[2]);
8709 }
8710#else
8711 ok(ExceptionInfo->ContextRecord->Rip == (DWORD_PTR)code_mem + 0x2f,
8712 "expected Rip = %Ix, got %Ix\n", (DWORD_PTR)code_mem + 0x2f, ExceptionInfo->ContextRecord->Rip);
8713 ok(rec->NumberParameters == 1,
8714 "ExceptionParameters is %ld instead of 1\n", rec->NumberParameters);
8715 ok(rec->ExceptionInformation[0] == ExceptionInfo->ContextRecord->Rax,
8716 "expected ExceptionInformation[0] = %Ix, got %Ix\n",
8717 ExceptionInfo->ContextRecord->Rax, rec->ExceptionInformation[0]);
8718#endif
8719
8720 debug_service_exceptions++;
8722}
8723
8724#ifdef __i386__
8725
8726static const BYTE call_debug_service_code[] = {
8727 0x53, /* pushl %ebx */
8728 0x57, /* pushl %edi */
8729 0x8b, 0x44, 0x24, 0x0c, /* movl 12(%esp),%eax */
8730 0xb9, 0x11, 0x11, 0x11, 0x11, /* movl $0x11111111,%ecx */
8731 0xba, 0x22, 0x22, 0x22, 0x22, /* movl $0x22222222,%edx */
8732 0xbb, 0x33, 0x33, 0x33, 0x33, /* movl $0x33333333,%ebx */
8733 0xbf, 0x44, 0x44, 0x44, 0x44, /* movl $0x44444444,%edi */
8734 0xcd, 0x2d, /* int $0x2d */
8735 0xeb, /* jmp $+17 */
8736 0x0f, 0x1f, 0x00, /* nop */
8737 0x31, 0xc0, /* xorl %eax,%eax */
8738 0xeb, 0x0c, /* jmp $+14 */
8739 0x90, 0x90, 0x90, 0x90, /* nop */
8740 0x90, 0x90, 0x90, 0x90,
8741 0x90,
8742 0x31, 0xc0, /* xorl %eax,%eax */
8743 0x40, /* incl %eax */
8744 0x5f, /* popl %edi */
8745 0x5b, /* popl %ebx */
8746 0xc3, /* ret */
8747};
8748
8749#else
8750
8751static const BYTE call_debug_service_code[] = {
8752 0x53, /* push %rbx */
8753 0x57, /* push %rdi */
8754 0x48, 0x89, 0xc8, /* movl %rcx,%rax */
8755 0x48, 0xb9, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, /* movabs $0x1111111111111111,%rcx */
8756 0x48, 0xba, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* movabs $0x2222222222222222,%rdx */
8757 0x48, 0xbb, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, /* movabs $0x3333333333333333,%rbx */
8758 0x48, 0xbf, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, /* movabs $0x4444444444444444,%rdi */
8759 0xcd, 0x2d, /* int $0x2d */
8760 0xeb, /* jmp $+17 */
8761 0x0f, 0x1f, 0x00, /* nop */
8762 0x48, 0x31, 0xc0, /* xor %rax,%rax */
8763 0xeb, 0x0e, /* jmp $+16 */
8764 0x90, 0x90, 0x90, 0x90, /* nop */
8765 0x90, 0x90, 0x90, 0x90,
8766 0x48, 0x31, 0xc0, /* xor %rax,%rax */
8767 0x48, 0xff, 0xc0, /* inc %rax */
8768 0x5f, /* pop %rdi */
8769 0x5b, /* pop %rbx */
8770 0xc3, /* ret */
8771};
8772
8773#endif
8774
8775static void test_debug_service(DWORD numexc)
8776{
8778 DWORD expected_exc, expected_ret;
8779 void *vectored_handler;
8780 DWORD ret;
8781
8782 /* code will return 0 if execution resumes immediately after "int $0x2d", otherwise 1 */
8784
8785 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &debug_service_handler);
8786 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
8787
8788 expected_exc = numexc;
8789 expected_ret = (numexc != 0);
8790
8791 /* BREAKPOINT_BREAK */
8792 debug_service_exceptions = 0;
8793 ret = func(0);
8794 ok(debug_service_exceptions == expected_exc,
8795 "BREAKPOINT_BREAK generated %lu exceptions, expected %lu\n",
8796 debug_service_exceptions, expected_exc);
8797 ok(ret == expected_ret,
8798 "BREAKPOINT_BREAK returned %lu, expected %lu\n", ret, expected_ret);
8799
8800 /* BREAKPOINT_PROMPT */
8801 debug_service_exceptions = 0;
8802 ret = func(2);
8803 ok(debug_service_exceptions == expected_exc,
8804 "BREAKPOINT_PROMPT generated %lu exceptions, expected %lu\n",
8805 debug_service_exceptions, expected_exc);
8806 ok(ret == expected_ret,
8807 "BREAKPOINT_PROMPT returned %lu, expected %lu\n", ret, expected_ret);
8808
8809 /* invalid debug service */
8810 debug_service_exceptions = 0;
8811 ret = func(6);
8812 ok(debug_service_exceptions == expected_exc,
8813 "invalid debug service generated %lu exceptions, expected %lu\n",
8814 debug_service_exceptions, expected_exc);
8815 ok(ret == expected_ret,
8816 "invalid debug service returned %lu, expected %lu\n", ret, expected_ret);
8817
8818 expected_exc = (is_wow64 ? numexc : 0);
8819 expected_ret = (is_wow64 && numexc);
8820
8821 /* BREAKPOINT_PRINT */
8822 debug_service_exceptions = 0;
8823 ret = func(1);
8824 ok(debug_service_exceptions == expected_exc,
8825 "BREAKPOINT_PRINT generated %lu exceptions, expected %lu\n",
8826 debug_service_exceptions, expected_exc);
8827 ok(ret == expected_ret,
8828 "BREAKPOINT_PRINT returned %lu, expected %lu\n", ret, expected_ret);
8829
8830 /* BREAKPOINT_LOAD_SYMBOLS */
8831 debug_service_exceptions = 0;
8832 ret = func(3);
8833 ok(debug_service_exceptions == expected_exc,
8834 "BREAKPOINT_LOAD_SYMBOLS generated %lu exceptions, expected %lu\n",
8835 debug_service_exceptions, expected_exc);
8836 ok(ret == expected_ret,
8837 "BREAKPOINT_LOAD_SYMBOLS returned %lu, expected %lu\n", ret, expected_ret);
8838
8839 /* BREAKPOINT_UNLOAD_SYMBOLS */
8840 debug_service_exceptions = 0;
8841 ret = func(4);
8842 ok(debug_service_exceptions == expected_exc,
8843 "BREAKPOINT_UNLOAD_SYMBOLS generated %lu exceptions, expected %lu\n",
8844 debug_service_exceptions, expected_exc);
8845 ok(ret == expected_ret,
8846 "BREAKPOINT_UNLOAD_SYMBOLS returned %lu, expected %lu\n", ret, expected_ret);
8847
8848 /* BREAKPOINT_COMMAND_STRING */
8849 debug_service_exceptions = 0;
8850 ret = func(5);
8851 ok(debug_service_exceptions == expected_exc || broken(debug_service_exceptions == numexc),
8852 "BREAKPOINT_COMMAND_STRING generated %lu exceptions, expected %lu\n",
8853 debug_service_exceptions, expected_exc);
8854 ok(ret == expected_ret || broken(ret == (numexc != 0)),
8855 "BREAKPOINT_COMMAND_STRING returned %lu, expected %lu\n", ret, expected_ret);
8856
8857 pRtlRemoveVectoredExceptionHandler(vectored_handler);
8858}
8859#endif /* defined(__i386__) || defined(__x86_64__) */
8860
8863
8865{
8866 PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
8867
8868 switch (rec->ExceptionCode)
8869 {
8871 ok(rec->NumberParameters == 2, "ExceptionParameters is %ld instead of 2\n", rec->NumberParameters);
8872 ok(rec->ExceptionInformation[0] == 12, "ExceptionInformation[0] = %ld instead of 12\n", (DWORD)rec->ExceptionInformation[0]);
8873 ok(!strcmp((char *)rec->ExceptionInformation[1], "Hello World"),
8874 "ExceptionInformation[1] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[1]);
8876 break;
8878 ok(outputdebugstring_exceptions_ansi == 0, "Unicode exception should come first\n");
8879 ok(rec->NumberParameters == 4, "ExceptionParameters is %ld instead of 4\n", rec->NumberParameters);
8880 ok(rec->ExceptionInformation[0] == 12, "ExceptionInformation[0] = %ld instead of 12\n", (DWORD)rec->ExceptionInformation[0]);
8881 ok(!wcscmp((WCHAR *)rec->ExceptionInformation[1], L"Hello World"),
8882 "ExceptionInformation[1] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[1]);
8883 ok(rec->ExceptionInformation[2] == 12, "ExceptionInformation[2] = %ld instead of 12\n", (DWORD)rec->ExceptionInformation[2]);
8884 ok(!strcmp((char *)rec->ExceptionInformation[3], "Hello World"),
8885 "ExceptionInformation[3] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[3]);
8887 break;
8888 default:
8889 ok(0, "ExceptionCode is %08lx unexpected\n", rec->ExceptionCode);
8890 break;
8891 }
8892
8894}
8895
8896static void test_outputdebugstring(BOOL unicode, DWORD numexc_ansi, BOOL todo_ansi,
8897 DWORD numexc_unicode_low, DWORD numexc_unicode_high)
8898{
8899 PVOID vectored_handler;
8900
8901 if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler)
8902 {
8903 skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n");
8904 return;
8905 }
8906
8907 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &outputdebugstring_vectored_handler);
8908 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
8909
8911
8912 if (unicode)
8913 OutputDebugStringW(L"Hello World");
8914 else
8915 OutputDebugStringA("Hello World");
8916
8917 todo_wine_if(todo_ansi)
8919 "OutputDebugString%c generated %ld ansi exceptions, expected %ld\n",
8920 unicode ? 'W' : 'A', outputdebugstring_exceptions_ansi, numexc_ansi);
8921 ok(outputdebugstring_exceptions_unicode >= numexc_unicode_low &&
8922 outputdebugstring_exceptions_unicode <= numexc_unicode_high,
8923 "OutputDebugString%c generated %lu unicode exceptions, expected %ld-%ld\n",
8924 unicode ? 'W' : 'A', outputdebugstring_exceptions_unicode, numexc_unicode_low, numexc_unicode_high);
8925
8926 pRtlRemoveVectoredExceptionHandler(vectored_handler);
8927}
8928
8931
8933{
8934 PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
8935
8936 switch (rec->ExceptionCode)
8937 {
8939 ok(rec->NumberParameters == 2, "ExceptionParameters is %ld instead of 2\n", rec->NumberParameters);
8940 ok(rec->ExceptionInformation[0] == 12, "ExceptionInformation[0] = %ld instead of 12\n", (DWORD)rec->ExceptionInformation[0]);
8941 ok(!strcmp((char *)rec->ExceptionInformation[1], "Hello World"),
8942 "ExceptionInformation[1] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[1]);
8945 break;
8947 ok(rec->NumberParameters == 4, "ExceptionParameters is %ld instead of 4\n", rec->NumberParameters);
8948 ok(rec->ExceptionInformation[0] == 12, "ExceptionInformation[0] = %ld instead of 12\n", (DWORD)rec->ExceptionInformation[0]);
8949 ok(!wcscmp((WCHAR *)rec->ExceptionInformation[1], L"Hello World"),
8950 "ExceptionInformation[1] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[1]);
8951 ok(rec->ExceptionInformation[2] == 12, "ExceptionInformation[2] = %ld instead of 12\n", (DWORD)rec->ExceptionInformation[2]);
8952 ok(!strcmp((char *)rec->ExceptionInformation[3], "Hello World"),
8953 "ExceptionInformation[3] = '%s' instead of 'Hello World'\n", (char *)rec->ExceptionInformation[3]);
8956 break;
8957 default:
8958 ok(0, "ExceptionCode is %08lx unexpected\n", rec->ExceptionCode);
8959 break;
8960 }
8961
8963}
8964
8966{
8967 PVOID vectored_handler;
8968 struct
8969 {
8970 /* input */
8971 BOOL unicode;
8972 DWORD ret_code;
8973 /* expected output */
8974 DWORD exceptions_order;
8975 }
8976 tests[] =
8977 {
8981 {TRUE, EXCEPTION_CONTINUE_SEARCH, ('W' << 8) | 'A'},
8982 };
8983 int i;
8984
8985 if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler)
8986 {
8987 skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n");
8988 return;
8989 }
8990
8991 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &outputdebugstring_new_model_vectored_handler);
8992 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
8993
8994 for (i = 0; i < ARRAY_SIZE(tests); i++)
8995 {
8998
8999 if (tests[i].unicode)
9000 OutputDebugStringW(L"Hello World");
9001 else
9002 OutputDebugStringA("Hello World");
9003
9005 "OutputDebugString%c/%u generated exceptions %04lxs, expected %04lx\n",
9006 tests[i].unicode ? 'W' : 'A', i,
9008 }
9009
9010 pRtlRemoveVectoredExceptionHandler(vectored_handler);
9011}
9012
9014
9016{
9017 PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
9018
9019 ok(rec->ExceptionCode == DBG_RIPEXCEPTION, "ExceptionCode is %08lx instead of %08lx\n",
9021 ok(rec->NumberParameters == 2, "ExceptionParameters is %ld instead of 2\n", rec->NumberParameters);
9022 ok(rec->ExceptionInformation[0] == 0x11223344, "ExceptionInformation[0] = %08lx instead of %08x\n",
9023 (NTSTATUS)rec->ExceptionInformation[0], 0x11223344);
9024 ok(rec->ExceptionInformation[1] == 0x55667788, "ExceptionInformation[1] = %08lx instead of %08x\n",
9025 (NTSTATUS)rec->ExceptionInformation[1], 0x55667788);
9026
9029}
9030
9031static void test_ripevent(DWORD numexc)
9032{
9034 PVOID vectored_handler;
9035
9036 if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler || !pRtlRaiseException)
9037 {
9038 skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler or RtlRaiseException not found\n");
9039 return;
9040 }
9041
9042 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &ripevent_vectored_handler);
9043 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
9044
9045 record.ExceptionCode = DBG_RIPEXCEPTION;
9046 record.ExceptionFlags = 0;
9047 record.ExceptionRecord = NULL;
9048 record.ExceptionAddress = NULL;
9049 record.NumberParameters = 2;
9050 record.ExceptionInformation[0] = 0x11223344;
9051 record.ExceptionInformation[1] = 0x55667788;
9052
9054 pRtlRaiseException(&record);
9055 ok(ripevent_exceptions == numexc, "RtlRaiseException generated %ld exceptions, expected %ld\n",
9056 ripevent_exceptions, numexc);
9057
9058 pRtlRemoveVectoredExceptionHandler(vectored_handler);
9059}
9060
9061static void subtest_fastfail(unsigned int code)
9062{
9063 char cmdline[MAX_PATH];
9065 STARTUPINFOA si = { 0 };
9066 DEBUG_EVENT de;
9067 DWORD continuestatus;
9068 BOOL ret;
9069 BOOL had_ff = FALSE, had_se = FALSE;
9070
9071 sprintf(cmdline, "%s %s %s %u", my_argv[0], my_argv[1], "fastfail", code);
9072 si.cb = sizeof(si);
9074 ok(ret, "could not create child process error: %lu\n", GetLastError());
9075 if (!ret)
9076 return;
9077
9078 do
9079 {
9080 continuestatus = DBG_CONTINUE;
9081 ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n");
9082
9084 {
9086 {
9087 ok(!de.u.Exception.dwFirstChance, "must be a second chance exception\n");
9089 "expected exactly one parameter, got %lu\n",
9093 "expected %u for code, got %Iu\n",
9095 had_ff = TRUE;
9096 }
9097
9098 if (de.u.Exception.dwFirstChance)
9099 {
9100 continuestatus = DBG_EXCEPTION_NOT_HANDLED;
9101 }
9102 else
9103 {
9104 had_se = TRUE;
9105 pNtTerminateProcess(pi.hProcess, 0);
9106 }
9107 }
9108
9109 ContinueDebugEvent(de.dwProcessId, de.dwThreadId, continuestatus);
9110
9112
9113 ok(had_ff || broken(had_se) /* Win7 */, "fast fail did not occur\n");
9114
9117 ok(ret, "error %lu\n", GetLastError());
9119 ok(ret, "error %lu\n", GetLastError());
9120
9121 return;
9122}
9123
9124static void test_fastfail(void)
9125{
9126 unsigned int codes[] = {
9136 0xdeadbeefUL,
9137 };
9138 unsigned int i;
9139
9140 for (i = 0; i < ARRAY_SIZE(codes); i++)
9141 {
9142 winetest_push_context("__fastfail(%#x)", codes[i]);
9143 subtest_fastfail(codes[i]);
9145 }
9146}
9147
9149
9151{
9152 EXCEPTION_RECORD *rec = ExceptionInfo->ExceptionRecord;
9153
9154 ok(rec->ExceptionCode == EXCEPTION_BREAKPOINT, "ExceptionCode is %08lx instead of %08lx\n",
9156
9157#ifdef __i386__
9158 ok(ExceptionInfo->ContextRecord->Eip == (DWORD)code_mem + 1,
9159 "expected Eip = %lx, got %lx\n", (DWORD)code_mem + 1, ExceptionInfo->ContextRecord->Eip);
9160 ok(rec->NumberParameters == (is_wow64 ? 1 : 3),
9161 "ExceptionParameters is %ld instead of %d\n", rec->NumberParameters, is_wow64 ? 1 : 3);
9162 ok(rec->ExceptionInformation[0] == 0,
9163 "got ExceptionInformation[0] = %Ix\n", rec->ExceptionInformation[0]);
9164 ExceptionInfo->ContextRecord->Eip = (DWORD)code_mem + 2;
9165#elif defined(__x86_64__)
9166 ok(ExceptionInfo->ContextRecord->Rip == (DWORD_PTR)code_mem + 1,
9167 "expected Rip = %Ix, got %Ix\n", (DWORD_PTR)code_mem + 1, ExceptionInfo->ContextRecord->Rip);
9168 ok(rec->NumberParameters == 1,
9169 "ExceptionParameters is %ld instead of 1\n", rec->NumberParameters);
9170 ok(rec->ExceptionInformation[0] == 0,
9171 "got ExceptionInformation[0] = %Ix\n", rec->ExceptionInformation[0]);
9172 ExceptionInfo->ContextRecord->Rip = (DWORD_PTR)code_mem + 2;
9173#elif defined(__arm__)
9174 ok(ExceptionInfo->ContextRecord->Pc == (DWORD)code_mem + 1,
9175 "expected pc = %lx, got %lx\n", (DWORD)code_mem + 1, ExceptionInfo->ContextRecord->Pc);
9176 ok(rec->NumberParameters == 1,
9177 "ExceptionParameters is %ld instead of 1\n", rec->NumberParameters);
9178 ok(rec->ExceptionInformation[0] == 0,
9179 "got ExceptionInformation[0] = %Ix\n", rec->ExceptionInformation[0]);
9180 ExceptionInfo->ContextRecord->Pc += 2;
9181#elif defined(__aarch64__)
9182 ok(ExceptionInfo->ContextRecord->Pc == (DWORD_PTR)code_mem,
9183 "expected pc = %p, got %p\n", code_mem, (void *)ExceptionInfo->ContextRecord->Pc);
9184 ok(rec->NumberParameters == 1,
9185 "ExceptionParameters is %ld instead of 1\n", rec->NumberParameters);
9186 ok(rec->ExceptionInformation[0] == 0,
9187 "got ExceptionInformation[0] = %p\n", (void *)rec->ExceptionInformation[0]);
9188 ExceptionInfo->ContextRecord->Pc += 4;
9189#endif
9190
9193}
9194
9195#if defined(__i386__) || defined(__x86_64__)
9196static const BYTE breakpoint_code[] = { 0xcd, 0x03, 0xc3 }; /* int $0x3; ret */
9197#elif defined(__arm__)
9198static const DWORD breakpoint_code[] = { 0xdefe, 0x4770 }; /* udf #0xfe; bx lr */
9199#elif defined(__aarch64__)
9200static const DWORD breakpoint_code[] = { 0xd43e0000, 0xd65f03c0 }; /* brk #0xf000; ret */
9201#endif
9202
9203static void test_breakpoint(DWORD numexc)
9204{
9206 void *vectored_handler;
9207
9208#if defined(__REACTOS__)
9209 if (is_reactos())
9210 {
9211 skip("Skipping tests that crash\n");
9212 return;
9213 }
9214#endif
9215
9216 memcpy(code_mem, breakpoint_code, sizeof(breakpoint_code));
9217#ifdef __arm__
9218 func = (void *)((char *)code_mem + 1); /* thumb */
9219#endif
9220 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &breakpoint_handler);
9221 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
9222
9224 func();
9225 ok(breakpoint_exceptions == numexc, "int $0x3 generated %lu exceptions, expected %lu\n",
9226 breakpoint_exceptions, numexc);
9227
9228 pRtlRemoveVectoredExceptionHandler(vectored_handler);
9229}
9230
9231#if defined(__i386__) || defined(__x86_64__)
9232static BYTE except_code_set_ymm0[] =
9233{
9234#ifdef __x86_64__
9235 0x48,
9236#endif
9237 0xb8, /* mov imm,%ax */
9238 0x00, 0x00, 0x00, 0x00,
9239#ifdef __x86_64__
9240 0x00, 0x00, 0x00, 0x00,
9241#endif
9242
9243 0xc5, 0xfc, 0x10, 0x00, /* vmovups (%ax),%ymm0 */
9244 0xcc, /* int3 */
9245 0xc5, 0xfc, 0x11, 0x00, /* vmovups %ymm0,(%ax) */
9246 0xc3, /* ret */
9247};
9248
9249static void test_debuggee_xstate(void)
9250{
9251 void (CDECL *func)(void) = code_mem;
9252 unsigned int address_offset, i;
9253 unsigned int data[8];
9254
9255 if (!pRtlGetEnabledExtendedFeatures || !pRtlGetEnabledExtendedFeatures(1 << XSTATE_AVX))
9256 {
9257 memcpy(code_mem, breakpoint_code, sizeof(breakpoint_code));
9258 func();
9259 return;
9260 }
9261
9262 memcpy(code_mem, except_code_set_ymm0, sizeof(except_code_set_ymm0));
9263 address_offset = sizeof(void *) == 8 ? 2 : 1;
9264 *(void **)((BYTE *)code_mem + address_offset) = data;
9265
9266 for (i = 0; i < ARRAY_SIZE(data); ++i)
9267 data[i] = i + 1;
9268
9269 func();
9270
9271 for (i = 0; i < 4; ++i)
9272 ok(data[i] == (test_stage == STAGE_XSTATE ? i + 1 : 0x28282828),
9273 "Got unexpected data %#x, test_stage %u, i %u.\n", data[i], test_stage, i);
9274
9275 for ( ; i < ARRAY_SIZE(data); ++i)
9276 ok(data[i] == (test_stage == STAGE_XSTATE ? i + 1 : 0x48484848)
9277 || broken(test_stage == STAGE_XSTATE_LEGACY_SSE && data[i] == i + 1) /* Win7 */,
9278 "Got unexpected data %#x, test_stage %u, i %u.\n", data[i], test_stage, i);
9279}
9280
9281static BYTE except_code_segments[] =
9282{
9283 0x8c, 0xc0, /* mov %es,%eax */
9284 0x50, /* push %rax */
9285 0x8c, 0xd8, /* mov %ds,%eax */
9286 0x50, /* push %rax */
9287 0x8c, 0xe0, /* mov %fs,%eax */
9288 0x50, /* push %rax */
9289 0x8c, 0xe8, /* mov %gs,%eax */
9290 0x50, /* push %rax */
9291 0x31, 0xc0, /* xor %eax,%eax */
9292 0x8e, 0xc0, /* mov %eax,%es */
9293 0x8e, 0xd8, /* mov %eax,%ds */
9294 0x8e, 0xe0, /* mov %eax,%fs */
9295 0x8e, 0xe8, /* mov %eax,%gs */
9296 0xcc, /* int3 */
9297 0x58, /* pop %rax */
9298 0x8e, 0xe8, /* mov %eax,%gs */
9299 0x58, /* pop %rax */
9300 0x8e, 0xe0, /* mov %eax,%fs */
9301 0x58, /* pop %rax */
9302 0x8e, 0xd8, /* mov %eax,%ds */
9303 0x58, /* pop %rax */
9304 0x8e, 0xc0, /* mov %eax,%es */
9305 0xc3, /* retq */
9306};
9307
9308static void test_debuggee_segments(void)
9309{
9310 void (CDECL *func)(void) = code_mem;
9311
9312 memcpy( code_mem, except_code_segments, sizeof(except_code_segments));
9313 func();
9314}
9315#endif
9316
9318
9320{
9321 PEXCEPTION_RECORD rec = ExceptionInfo->ExceptionRecord;
9322
9323 ok(rec->ExceptionCode == EXCEPTION_INVALID_HANDLE, "ExceptionCode is %08lx instead of %08lx\n",
9325 ok(rec->NumberParameters == 0, "ExceptionParameters is %ld instead of 0\n", rec->NumberParameters);
9326
9329}
9330
9332{
9333 return HandleToLong(handle) >= ~5 && HandleToLong(handle) <= ~0;
9334}
9335
9337{
9339 PVOID vectored_handler;
9340 BOOL ret, expectret;
9341
9342 if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler || !pRtlRaiseException)
9343 {
9344 skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler or RtlRaiseException not found\n");
9345 return;
9346 }
9347
9348 vectored_handler = pRtlAddVectoredExceptionHandler(TRUE, &invalid_handle_vectored_handler);
9349 ok(vectored_handler != 0, "RtlAddVectoredExceptionHandler failed\n");
9350
9352 expectret = is_magic_handle(handle) || broken(numexc && sizeof(handle) == 4); /* < Win10 */
9355 "CloseHandle had wrong GetLastError(), got %lu for %p\n", GetLastError(), handle);
9356 ok(ret == expectret || broken(HandleToLong(handle) < 0) /* < Win10 */,
9357 "CloseHandle expected %d, got %d for %p\n", expectret, ret, handle);
9358 ok(invalid_handle_exceptions == numexc || broken(!numexc && is_magic_handle(handle)), /* < Win10 */
9359 "CloseHandle generated %ld exceptions, expected %ld for %p\n",
9361
9364 status = pNtClose(handle);
9365 ok(status == expect || broken(HandleToLong(handle) < 0), /* < Win10 */
9366 "NtClose returned unexpected status %#lx, expected %#lx for %p\n", status, expect, handle);
9367 ok(invalid_handle_exceptions == numexc || broken(!numexc && is_magic_handle(handle)), /* < Win10 */
9368 "CloseHandle generated %ld exceptions, expected %ld for %p\n",
9370
9371 pRtlRemoveVectoredExceptionHandler(vectored_handler);
9372}
9373
9375{
9376 PVOID handler1, handler2;
9377 ULONG ret;
9378
9379 if (!pRtlAddVectoredContinueHandler || !pRtlRemoveVectoredContinueHandler)
9380 {
9381 skip("RtlAddVectoredContinueHandler or RtlRemoveVectoredContinueHandler not found\n");
9382 return;
9383 }
9384
9385 handler1 = pRtlAddVectoredContinueHandler(TRUE, (void *)0xdeadbeef);
9386 ok(handler1 != 0, "RtlAddVectoredContinueHandler failed\n");
9387
9388 handler2 = pRtlAddVectoredContinueHandler(TRUE, (void *)0xdeadbeef);
9389 ok(handler2 != 0, "RtlAddVectoredContinueHandler failed\n");
9390 ok(handler1 != handler2, "RtlAddVectoredContinueHandler returned same handler\n");
9391
9392 if (pRtlRemoveVectoredExceptionHandler)
9393 {
9394 ret = pRtlRemoveVectoredExceptionHandler(handler1);
9395 ok(!ret, "RtlRemoveVectoredExceptionHandler succeeded\n");
9396 }
9397
9398 ret = pRtlRemoveVectoredContinueHandler(handler1);
9399 ok(ret, "RtlRemoveVectoredContinueHandler failed\n");
9400
9401 ret = pRtlRemoveVectoredContinueHandler(handler2);
9402 ok(ret, "RtlRemoveVectoredContinueHandler failed\n");
9403
9404 ret = pRtlRemoveVectoredContinueHandler(handler1);
9405 ok(!ret, "RtlRemoveVectoredContinueHandler succeeded\n");
9406
9407 ret = pRtlRemoveVectoredContinueHandler((void *)0x11223344);
9408 ok(!ret, "RtlRemoveVectoredContinueHandler succeeded\n");
9409}
9410
9411static void test_user_apc(void)
9412{
9415 LONG pass;
9416 int ret;
9417
9418 if (!pNtQueueApcThread)
9419 {
9420 win_skip("NtQueueApcThread is not available.\n");
9421 return;
9422 }
9423
9424 pass = 0;
9426#ifdef __i386__
9427 {
9428 /* RtlCaptureContext puts the return address of the caller's stack
9429 * frame into %eip, so we need a thunk to get it to return here */
9430 static const BYTE code[] =
9431 {
9432 0x55, /* pushl %ebp */
9433 0x89, 0xe5, /* movl %esp, %ebp */
9434 0xff, 0x75, 0x0c, /* pushl 0xc(%ebp) */
9435 0xff, 0x55, 0x08, /* call *0x8(%ebp) */
9436 0xc9, /* leave */
9437 0xc3, /* ret */
9438 };
9439 int (__cdecl *func)(void *capture, CONTEXT *context) = code_mem;
9440
9441 memcpy(code_mem, code, sizeof(code));
9443 /* work around broken RtlCaptureContext on Windows < 7 which doesn't set
9444 * ContextFlags */
9445 context.ContextFlags = CONTEXT_FULL;
9446 }
9447#else
9448 {
9450
9451 ret = func(&context);
9452 }
9453#endif
9455
9456 if (pass == 2)
9457 {
9458 /* Try to make sure context data is far enough below context.Esp. */
9459 CONTEXT c[4];
9460
9461#ifdef __i386__
9462 context.Eax = 0xabacab;
9463#elif defined(__x86_64__)
9464 context.Rax = 0xabacab;
9465#elif defined(__arm__)
9466 context.R0 = 0xabacab;
9467#elif defined(__aarch64__)
9468 context.X0 = 0xabacab;
9469#endif
9470
9471 c[0] = context;
9472
9473 apc_count = 0;
9474 status = pNtQueueApcThread(GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef);
9475 ok(!status, "Got unexpected status %#lx.\n", status);
9476 SleepEx(0, TRUE);
9477 ok(apc_count == 1, "Test user APC was not called.\n");
9478 apc_count = 0;
9479 status = pNtQueueApcThread(GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef);
9480 ok(!status, "Got unexpected status %#lx.\n", status);
9481 status = NtContinue(&c[0], TRUE );
9482
9483 ok(0, "Should not get here, status %#lx.\n", status);
9484 return;
9485 }
9486 ok(ret == 0xabacab, "Got return value %#x.\n", ret);
9487 ok(pass == 3, "Got unexpected pass %ld.\n", pass);
9488 ok(apc_count > 0, "Test user APC was not called.\n");
9489}
9490
9491static void test_user_callback(void)
9492{
9494 ok( status == STATUS_NO_CALLBACK_ACTIVE, "failed %lx\n", status );
9495}
9496
9498{
9499 HANDLE event = arg;
9501 return 0;
9502}
9503
9505{
9506 static BOOL supported = TRUE;
9508 ULONG count;
9509
9510 if (!supported)
9511 return;
9512
9513 count = ~0u;
9514 status = pNtQueryInformationThread(hthread, ThreadSuspendCount, &count, sizeof(count), NULL);
9515 if (status)
9516 {
9517 win_skip("ThreadSuspendCount is not supported.\n");
9518 supported = FALSE;
9519 return;
9520 }
9521
9522 ok_(__FILE__, line)(!status, "Failed to get suspend count, status %#lx.\n", status);
9523 ok_(__FILE__, line)(count == expected_count, "Unexpected suspend count %lu.\n", count);
9524}
9525
9526static void test_suspend_thread(void)
9527{
9528#define TEST_SUSPEND_COUNT(thread, count) test_suspend_count((thread), (count), __LINE__)
9530 ULONG count, len;
9532 DWORD ret;
9533
9535 ok(status == STATUS_INVALID_HANDLE, "Unexpected return value %#lx.\n", status);
9536
9538 ok(status == STATUS_INVALID_HANDLE, "Unexpected return value %#lx.\n", status);
9539
9540 event = CreateEventW(NULL, FALSE, FALSE, NULL);
9541
9543 ok(thread != NULL, "Failed to create a thread.\n");
9544
9546 ok(ret == WAIT_TIMEOUT, "Unexpected status %ld.\n", ret);
9547
9548 status = pNtQueryInformationThread(thread, ThreadSuspendCount, &count, sizeof(count), NULL);
9549 if (!status)
9550 {
9551 status = pNtQueryInformationThread(thread, ThreadSuspendCount, NULL, sizeof(count), NULL);
9552 ok(status == STATUS_ACCESS_VIOLATION, "Unexpected status %#lx.\n", status);
9553
9554 status = pNtQueryInformationThread(thread, ThreadSuspendCount, &count, sizeof(count) / 2, NULL);
9555 ok(status == STATUS_INFO_LENGTH_MISMATCH, "Unexpected status %#lx.\n", status);
9556
9557 len = 123;
9558 status = pNtQueryInformationThread(thread, ThreadSuspendCount, &count, sizeof(count) / 2, &len);
9559 ok(status == STATUS_INFO_LENGTH_MISMATCH, "Unexpected status %#lx.\n", status);
9560 ok(len == 123, "Unexpected info length %lu.\n", len);
9561
9562 len = 123;
9563 status = pNtQueryInformationThread(thread, ThreadSuspendCount, NULL, 0, &len);
9564 ok(status == STATUS_INFO_LENGTH_MISMATCH, "Unexpected status %#lx.\n", status);
9565 ok(len == 123, "Unexpected info length %lu.\n", len);
9566
9567 count = 10;
9568 status = pNtQueryInformationThread(0, ThreadSuspendCount, &count, sizeof(count), NULL);
9569 ok(status, "Unexpected status %#lx.\n", status);
9570 ok(count == 10, "Unexpected suspend count %lu.\n", count);
9571 }
9572
9574 ok(!status, "Unexpected status %#lx.\n", status);
9575
9577 ok(!status, "Unexpected status %#lx.\n", status);
9578 ok(count == 0, "Unexpected suspended count %lu.\n", count);
9579
9581
9583 ok(!status, "Failed to suspend a thread, status %#lx.\n", status);
9584
9586
9588 ok(!status, "Failed to suspend a thread, status %#lx.\n", status);
9589 ok(count == 1, "Unexpected suspended count %lu.\n", count);
9590
9592
9594 ok(!status, "Failed to resume a thread, status %#lx.\n", status);
9595 ok(count == 2, "Unexpected suspended count %lu.\n", count);
9596
9598
9600 ok(!status, "Failed to resume a thread, status %#lx.\n", status);
9601
9603
9604 SetEvent(event);
9606
9608#undef TEST_SUSPEND_COUNT
9609}
9610
9611static const char *suspend_process_event_name = "suspend_process_event";
9612static const char *suspend_process_event2_name = "suspend_process_event2";
9613
9615{
9616 return 0;
9617}
9618
9619static void suspend_process_proc(void)
9620{
9623 DWORD count;
9625 HANDLE thread;
9626
9627 ok(event != NULL, "Failed to open event handle.\n");
9628 ok(event2 != NULL, "Failed to open event handle.\n");
9629
9631 ok(thread != NULL, "Failed to create auxiliary thread.\n");
9632
9633 /* Suspend up to limit. */
9634 while (!(status = NtSuspendThread(thread, NULL)))
9635 ;
9636 ok(status == STATUS_SUSPEND_COUNT_EXCEEDED, "Unexpected status %#lx.\n", status);
9637
9638 for (;;)
9639 {
9640 SetEvent(event2);
9642 break;
9643 }
9644
9646 ok(!status, "Failed to suspend a thread, status %#lx.\n", status);
9647 ok(count == 125, "Unexpected suspend count %lu.\n", count);
9648
9650 ok(!status, "Failed to resume a thread, status %#lx.\n", status);
9651
9653 CloseHandle(event2);
9654}
9655
9656static void test_suspend_process(void)
9657{
9659 char path_name[MAX_PATH];
9661 HANDLE event, event2;
9663 char **argv;
9664 DWORD ret;
9665
9667 ok(event != NULL, "Failed to create event.\n");
9668
9670 ok(event2 != NULL, "Failed to create event.\n");
9671
9673 memset(&startup, 0, sizeof(startup));
9674 startup.cb = sizeof(startup);
9675 sprintf(path_name, "%s exception suspend_process", argv[0]);
9676
9678 ok(ret, "Failed to create target process.\n");
9679
9680 /* New process signals this event. */
9681 ResetEvent(event2);
9682 ret = WaitForSingleObject(event2, INFINITE);
9683 ok(ret == WAIT_OBJECT_0, "Wait failed, %#lx.\n", ret);
9684
9685 /* Suspend main thread */
9686 status = NtSuspendThread(info.hThread, &ret);
9687 ok(!status && !ret, "Failed to suspend main thread, status %#lx.\n", status);
9688
9689 /* Process wasn't suspended yet. */
9690 status = pNtResumeProcess(info.hProcess);
9691 ok(!status, "Failed to resume a process, status %#lx.\n", status);
9692
9693 status = pNtSuspendProcess(0);
9694 ok(status == STATUS_INVALID_HANDLE, "Unexpected status %#lx.\n", status);
9695
9696 status = pNtResumeProcess(info.hProcess);
9697 ok(!status, "Failed to resume a process, status %#lx.\n", status);
9698
9699 ResetEvent(event2);
9700 ret = WaitForSingleObject(event2, 200);
9701 ok(ret == WAIT_OBJECT_0, "Wait failed.\n");
9702
9703 status = pNtSuspendProcess(info.hProcess);
9704 ok(!status, "Failed to suspend a process, status %#lx.\n", status);
9705
9706 status = NtSuspendThread(info.hThread, &ret);
9707 ok(!status && ret == 1, "Failed to suspend main thread, status %#lx.\n", status);
9708 status = NtResumeThread(info.hThread, &ret);
9709 ok(!status && ret == 2, "Failed to resume main thread, status %#lx.\n", status);
9710
9711 ResetEvent(event2);
9712 ret = WaitForSingleObject(event2, 200);
9713 ok(ret == WAIT_TIMEOUT, "Wait failed.\n");
9714
9715 status = pNtSuspendProcess(info.hProcess);
9716 ok(!status, "Failed to suspend a process, status %#lx.\n", status);
9717
9718 status = pNtResumeProcess(info.hProcess);
9719 ok(!status, "Failed to resume a process, status %#lx.\n", status);
9720
9721 ResetEvent(event2);
9722 ret = WaitForSingleObject(event2, 200);
9723 ok(ret == WAIT_TIMEOUT, "Wait failed.\n");
9724
9725 status = pNtResumeProcess(info.hProcess);
9726 ok(!status, "Failed to resume a process, status %#lx.\n", status);
9727
9728 ResetEvent(event2);
9729 ret = WaitForSingleObject(event2, 1000);
9730 ok(ret == WAIT_OBJECT_0, "Wait failed.\n");
9731
9732 SetEvent(event);
9733
9734 wait_child_process(info.hProcess);
9735
9736 CloseHandle(info.hProcess);
9737 CloseHandle(info.hThread);
9738
9740 CloseHandle(event2);
9741}
9742
9743static void test_unload_trace(void)
9744{
9745 static const WCHAR imageW[] = {'m','s','x','m','l','3','.','d','l','l',0};
9746 RTL_UNLOAD_EVENT_TRACE *unload_trace, **unload_trace_ex = NULL, *ptr;
9748 HMODULE hmod;
9749 BOOL found;
9750
9751 unload_trace = pRtlGetUnloadEventTrace();
9752 ok(unload_trace != NULL, "Failed to get unload events pointer.\n");
9753
9754 if (pRtlGetUnloadEventTraceEx)
9755 {
9756 pRtlGetUnloadEventTraceEx(&element_size, &element_count, (void **)&unload_trace_ex);
9757 ok(*element_size >= sizeof(*ptr), "Unexpected element size.\n");
9758 ok(*element_count == RTL_UNLOAD_EVENT_TRACE_NUMBER, "Unexpected trace element count %lu.\n", *element_count);
9759 ok(unload_trace_ex != NULL, "Unexpected pointer %p.\n", unload_trace_ex);
9760 size = *element_size;
9761 }
9762 else
9763 size = sizeof(*unload_trace);
9764
9765 hmod = LoadLibraryA("msxml3.dll");
9766 ok(hmod != NULL, "Failed to load library.\n");
9768
9769 found = FALSE;
9770 ptr = unload_trace;
9771 while (ptr->BaseAddress != NULL)
9772 {
9773 if (!lstrcmpW(imageW, ptr->ImageName))
9774 {
9775 found = TRUE;
9776 break;
9777 }
9778 ptr = (RTL_UNLOAD_EVENT_TRACE *)((char *)ptr + size);
9779 }
9780 ok(found, "Unloaded module wasn't found.\n");
9781
9782 if (unload_trace_ex)
9783 {
9784 found = FALSE;
9785 ptr = *unload_trace_ex;
9786 while (ptr->BaseAddress != NULL)
9787 {
9788 if (!lstrcmpW(imageW, ptr->ImageName))
9789 {
9790 found = TRUE;
9791 break;
9792 }
9793 ptr = (RTL_UNLOAD_EVENT_TRACE *)((char *)ptr + size);
9794 }
9795 ok(found, "Unloaded module wasn't found.\n");
9796 }
9797}
9798
9799#if defined(__i386__) || defined(__x86_64__)
9800
9801static const unsigned int test_extended_context_data[8] = {1, 2, 3, 4, 5, 6, 7, 8};
9802static const unsigned test_extended_context_spoil_data1[8] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80};
9803static const unsigned test_extended_context_spoil_data2[8] = {0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, 0x85};
9804
9805static BOOL test_extended_context_modified_state;
9806static BOOL xsaveopt_enabled, compaction_enabled;
9807static ULONG64 xstate_supported_features;
9808
9809static DWORD test_extended_context_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
9811{
9812 const ULONG64 expected_compaction_mask = (0x8000000000000000 | xstate_supported_features) & ~(ULONG64)3;
9813 CONTEXT_EX *xctx = (CONTEXT_EX *)(context + 1);
9814 unsigned int *context_ymm_data;
9815 DWORD expected_min_offset;
9816 XSTATE *xs;
9817
9818 ok((context->ContextFlags & (CONTEXT_FULL | CONTEXT_XSTATE)) == (CONTEXT_FULL | CONTEXT_XSTATE),
9819 "Got unexpected ContextFlags %#lx.\n", context->ContextFlags);
9820
9821 if ((context->ContextFlags & (CONTEXT_FULL | CONTEXT_XSTATE)) != (CONTEXT_FULL | CONTEXT_XSTATE))
9822 goto done;
9823
9824#ifdef __x86_64__
9825 {
9826 /* Unwind contexts do not inherit xstate information. */
9828
9829 ok(!(dispatch->ContextRecord->ContextFlags & 0x40), "Got unexpected ContextRecord->ContextFlags %#lx.\n",
9830 dispatch->ContextRecord->ContextFlags);
9831 }
9832#endif
9833
9834 ok(xctx->Legacy.Offset == -(int)(sizeof(CONTEXT)), "Got unexpected Legacy.Offset %ld.\n", xctx->Legacy.Offset);
9835 ok(xctx->Legacy.Length == sizeof(CONTEXT), "Got unexpected Legacy.Length %ld.\n", xctx->Legacy.Length);
9836 ok(xctx->All.Offset == -(int)sizeof(CONTEXT), "Got unexpected All.Offset %ld.\n", xctx->All.Offset);
9837 ok(xctx->All.Length == sizeof(CONTEXT) + xctx->XState.Offset + xctx->XState.Length,
9838 "Got unexpected All.Offset %ld.\n", xctx->All.Offset);
9839 expected_min_offset = sizeof(void *) == 8 ? sizeof(CONTEXT_EX) + sizeof(EXCEPTION_RECORD) : sizeof(CONTEXT_EX);
9840 ok(xctx->XState.Offset >= expected_min_offset,
9841 "Got unexpected XState.Offset %ld.\n", xctx->XState.Offset);
9842 ok(xctx->XState.Length >= sizeof(XSTATE), "Got unexpected XState.Length %ld.\n", xctx->XState.Length);
9843
9844 xs = (XSTATE *)((char *)xctx + xctx->XState.Offset);
9845 context_ymm_data = (unsigned int *)&xs->YmmContext;
9846 ok(!((ULONG_PTR)xs % 64), "Got unexpected xs %p.\n", xs);
9847
9848 if (compaction_enabled)
9849 ok((xs->CompactionMask & (expected_compaction_mask | 3)) == expected_compaction_mask,
9850 "Got compaction mask %#I64x, expected %#I64x.\n", xs->CompactionMask, expected_compaction_mask);
9851 else
9852 ok(!xs->CompactionMask, "Got compaction mask %#I64x.\n", xs->CompactionMask);
9853
9854 if (test_extended_context_modified_state)
9855 {
9856 ok((xs->Mask & 7) == 4, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
9857 ok(!memcmp(context_ymm_data, test_extended_context_data + 4, sizeof(M128A)),
9858 "Got unexpected context data.\n");
9859 }
9860 else
9861 {
9862 ok((xs->Mask & 7) == (xsaveopt_enabled ? 0 : 4), "Got unexpected Mask %#I64x.\n", xs->Mask);
9863 /* The save area has garbage if xsaveopt is available, so we can't test
9864 * its contents. */
9865
9866 /* Clear the mask; the state should be restored to INIT_STATE without
9867 * using this data. */
9868 xs->Mask = 0;
9869 memcpy(context_ymm_data, test_extended_context_spoil_data1 + 4, sizeof(M128A));
9870 }
9871
9872done:
9873#ifdef __GNUC__
9874 __asm__ volatile("vmovups (%0),%%ymm0" : : "r"(test_extended_context_spoil_data2));
9875#endif
9876#ifdef __x86_64__
9877 ++context->Rip;
9878#else
9879 if (*(BYTE *)context->Eip == 0xcc)
9880 ++context->Eip;
9881#endif
9883}
9884
9885struct call_func_offsets
9886{
9887 unsigned int func_addr;
9888 unsigned int func_param1;
9889 unsigned int func_param2;
9890 unsigned int ymm0_save;
9891};
9892#ifdef __x86_64__
9893static BYTE call_func_code_set_ymm0[] =
9894{
9895 0x55, /* pushq %rbp */
9896 0x48, 0xb8, /* mov imm,%rax */
9897 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9898
9899 0x48, 0xb9, /* mov imm,%rcx */
9900 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9901
9902 0x48, 0xba, /* mov imm,%rdx */
9903 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9904
9905 0x48, 0xbd, /* mov imm,%rbp */
9906 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9907
9908 0xc5, 0xfc, 0x10, 0x45, 0x00, /* vmovups (%rbp),%ymm0 */
9909 0x48, 0x83, 0xec, 0x20, /* sub $0x20,%rsp */
9910 0xff, 0xd0, /* call *rax */
9911 0x48, 0x83, 0xc4, 0x20, /* add $0x20,%rsp */
9912 0xc5, 0xfc, 0x11, 0x45, 0x00, /* vmovups %ymm0,(%rbp) */
9913 0x5d, /* popq %rbp */
9914 0xc3, /* ret */
9915};
9916static BYTE call_func_code_reset_ymm_state[] =
9917{
9918 0x55, /* pushq %rbp */
9919 0x48, 0xb8, /* mov imm,%rax */
9920 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9921
9922 0x48, 0xb9, /* mov imm,%rcx */
9923 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9924
9925 0x48, 0xba, /* mov imm,%rdx */
9926 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9927
9928 0x48, 0xbd, /* mov imm,%rbp */
9929 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9930
9931 0xc5, 0xf8, 0x77, /* vzeroupper */
9932 0x0f, 0x57, 0xc0, /* xorps %xmm0,%xmm0 */
9933 0x48, 0x83, 0xec, 0x20, /* sub $0x20,%rsp */
9934 0xff, 0xd0, /* call *rax */
9935 0x48, 0x83, 0xc4, 0x20, /* add $0x20,%rsp */
9936 0xc5, 0xfc, 0x11, 0x45, 0x00, /* vmovups %ymm0,(%rbp) */
9937 0x5d, /* popq %rbp */
9938 0xc3, /* ret */
9939};
9940static const struct call_func_offsets call_func_offsets = {3, 13, 23, 33};
9941#else
9942static BYTE call_func_code_set_ymm0[] =
9943{
9944 0x55, /* pushl %ebp */
9945 0xb8, /* mov imm,%eax */
9946 0x00, 0x00, 0x00, 0x00,
9947
9948 0xb9, /* mov imm,%ecx */
9949 0x00, 0x00, 0x00, 0x00,
9950
9951 0xba, /* mov imm,%edx */
9952 0x00, 0x00, 0x00, 0x00,
9953
9954 0xbd, /* mov imm,%ebp */
9955 0x00, 0x00, 0x00, 0x00,
9956
9957 0x81, 0xfa, 0xef, 0xbe, 0xad, 0xde,
9958 /* cmpl $0xdeadbeef, %edx */
9959 0x74, 0x01, /* je 1f */
9960 0x52, /* pushl %edx */
9961 0x51, /* 1: pushl %ecx */
9962 0xc5, 0xfc, 0x10, 0x45, 0x00, /* vmovups (%ebp),%ymm0 */
9963 0xff, 0xd0, /* call *eax */
9964 0xc5, 0xfc, 0x11, 0x45, 0x00, /* vmovups %ymm0,(%ebp) */
9965 0x5d, /* popl %ebp */
9966 0xc3, /* ret */
9967};
9968static BYTE call_func_code_reset_ymm_state[] =
9969{
9970 0x55, /* pushl %ebp */
9971 0xb8, /* mov imm,%eax */
9972 0x00, 0x00, 0x00, 0x00,
9973
9974 0xb9, /* mov imm,%ecx */
9975 0x00, 0x00, 0x00, 0x00,
9976
9977 0xba, /* mov imm,%edx */
9978 0x00, 0x00, 0x00, 0x00,
9979
9980 0xbd, /* mov imm,%ebp */
9981 0x00, 0x00, 0x00, 0x00,
9982
9983 0x81, 0xfa, 0xef, 0xbe, 0xad, 0xde,
9984 /* cmpl $0xdeadbeef, %edx */
9985 0x74, 0x01, /* je 1f */
9986 0x52, /* pushl %edx */
9987 0x51, /* 1: pushl %ecx */
9988 0xc5, 0xf8, 0x77, /* vzeroupper */
9989 0x0f, 0x57, 0xc0, /* xorps %xmm0,%xmm0 */
9990 0xff, 0xd0, /* call *eax */
9991 0xc5, 0xfc, 0x11, 0x45, 0x00, /* vmovups %ymm0,(%ebp) */
9992 0x5d, /* popl %ebp */
9993 0xc3, /* ret */
9994};
9995static const struct call_func_offsets call_func_offsets = {2, 7, 12, 17};
9996#endif
9997
9998static DWORD WINAPI test_extended_context_thread(void *arg)
9999{
10001 static unsigned int data[8];
10002 unsigned int i;
10003
10004 memcpy(code_mem, call_func_code_reset_ymm_state, sizeof(call_func_code_reset_ymm_state));
10005 *(void **)((BYTE *)code_mem + call_func_offsets.func_addr) = SuspendThread;
10006 *(void **)((BYTE *)code_mem + call_func_offsets.func_param1) = (void *)GetCurrentThread();
10007 *(void **)((BYTE *)code_mem + call_func_offsets.func_param2) = (void *)0xdeadbeef;
10008 *(void **)((BYTE *)code_mem + call_func_offsets.ymm0_save) = data;
10009 func();
10010
10011 for (i = 0; i < 4; ++i)
10012 ok(!data[i], "Got unexpected data %#x, i %u.\n", data[i], i);
10013 for (; i < 8; ++i)
10014 ok(data[i] == 0x48484848, "Got unexpected data %#x, i %u.\n", data[i], i);
10015 memset(data, 0x68, sizeof(data));
10016
10017 memcpy(code_mem, call_func_code_set_ymm0, sizeof(call_func_code_set_ymm0));
10018 *(void **)((BYTE *)code_mem + call_func_offsets.func_addr) = SuspendThread;
10019 *(void **)((BYTE *)code_mem + call_func_offsets.func_param1) = (void *)GetCurrentThread();
10020 *(void **)((BYTE *)code_mem + call_func_offsets.func_param2) = (void *)0xdeadbeef;
10021 *(void **)((BYTE *)code_mem + call_func_offsets.ymm0_save) = data;
10022 func();
10023
10024 memcpy(code_mem, call_func_code_reset_ymm_state, sizeof(call_func_code_reset_ymm_state));
10025 *(void **)((BYTE *)code_mem + call_func_offsets.func_addr) = SuspendThread;
10026 *(void **)((BYTE *)code_mem + call_func_offsets.func_param1) = (void *)GetCurrentThread();
10027 *(void **)((BYTE *)code_mem + call_func_offsets.func_param2) = (void *)0xdeadbeef;
10028 *(void **)((BYTE *)code_mem + call_func_offsets.ymm0_save) = data;
10029 func();
10030 return 0;
10031}
10032
10033static void wait_for_thread_next_suspend(HANDLE thread)
10034{
10035 DWORD result;
10036
10038 ok(result == 1, "Got unexpected suspend count %lu.\n", result);
10039
10040 /* NtQueryInformationThread(ThreadSuspendCount, ...) is not supported on older Windows. */
10041 while (!(result = SuspendThread(thread)))
10042 {
10044 Sleep(1);
10045 }
10046 ok(result == 1, "Got unexpected suspend count %lu.\n", result);
10048 ok(result == 2, "Got unexpected suspend count %lu.\n", result);
10049}
10050
10051#define CONTEXT_NATIVE (CONTEXT_XSTATE & CONTEXT_CONTROL)
10052
10053struct context_parameters
10054{
10055 ULONG flag;
10056 ULONG supported_flags;
10057 ULONG broken_flags;
10058 ULONG context_length;
10059 ULONG legacy_length;
10060 ULONG context_ex_length;
10061 ULONG align;
10062 ULONG flags_offset;
10063 ULONG xsavearea_offset;
10064 ULONG vector_reg_count;
10065};
10066
10067static void test_extended_context(void)
10068{
10069 static BYTE except_code_reset_ymm_state[] =
10070 {
10071#ifdef __x86_64__
10072 0x48,
10073#endif
10074 0xb8, /* mov imm,%ax */
10075 0x00, 0x00, 0x00, 0x00,
10076#ifdef __x86_64__
10077 0x00, 0x00, 0x00, 0x00,
10078#endif
10079 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
10080
10081 0xc5, 0xf8, 0x77, /* vzeroupper */
10082 0x0f, 0x57, 0xc0, /* xorps %xmm0,%xmm0 */
10083 0xcc, /* int3 */
10084 0xc5, 0xfc, 0x11, 0x00, /* vmovups %ymm0,(%ax) */
10085 0xc3, /* ret */
10086 };
10087
10088 static const struct context_parameters context_arch_old[] =
10089 {
10090 {
10091 0x00100000, /* CONTEXT_AMD64 */
10092 0xd800005f,
10093 0xd8000000,
10094 0x4d0, /* sizeof(CONTEXT) */
10095 0x4d0, /* sizeof(CONTEXT) */
10096 0x20, /* sizeof(CONTEXT_EX) */
10097 7,
10098 0x30,
10099 0x100, /* offsetof(CONTEXT, FltSave) */
10100 16,
10101 },
10102 {
10103 0x00010000, /* CONTEXT_X86 */
10104 0xd800007f,
10105 0xd8000000,
10106 0x2cc, /* sizeof(CONTEXT) */
10107 0xcc, /* offsetof(CONTEXT, ExtendedRegisters) */
10108 0x18, /* sizeof(CONTEXT_EX) */
10109 3,
10110 0,
10111 0xcc, /* offsetof(CONTEXT, ExtendedRegisters) */
10112 8,
10113 },
10114 };
10115
10116 static const struct context_parameters context_arch_new[] =
10117 {
10118 {
10119 0x00100000, /* CONTEXT_AMD64 */
10120 0xf800005f,
10121 0xf8000000,
10122 0x4d0, /* sizeof(CONTEXT) */
10123 0x4d0, /* sizeof(CONTEXT) */
10124 0x20, /* sizeof(CONTEXT_EX) */
10125 15,
10126 0x30,
10127 0x100, /* offsetof(CONTEXT, FltSave) */
10128 16,
10129 },
10130 {
10131 0x00010000, /* CONTEXT_X86 */
10132 0xf800007f,
10133 0xf8000000,
10134 0x2cc, /* sizeof(CONTEXT) */
10135 0xcc, /* offsetof(CONTEXT, ExtendedRegisters) */
10136 0x20, /* sizeof(CONTEXT_EX) */
10137 3,
10138 0,
10139 0xcc, /* offsetof(CONTEXT, ExtendedRegisters) */
10140 8,
10141 },
10142 };
10143 const struct context_parameters *context_arch;
10144
10145 const ULONG64 supported_features = 0xff;
10146 const ULONG64 supported_compaction_mask = supported_features | ((ULONG64)1 << 63);
10147 ULONG expected_length, expected_length_xstate, context_flags, expected_offset, max_xstate_length;
10148 ULONG64 enabled_features, expected_compaction;
10149 DECLSPEC_ALIGN(64) BYTE context_buffer2[4096];
10150 DECLSPEC_ALIGN(64) BYTE context_buffer[4096];
10151 unsigned int i, j, address_offset, test;
10152 ULONG ret, ret2, length, length2, align;
10153 ULONG flags, flags_fpx, expected_flags;
10154 ULONG (WINAPI* func)(void) = code_mem;
10157 unsigned data[8];
10158 HANDLE thread;
10159 ULONG64 mask;
10160 XSTATE *xs;
10161 BOOL bret;
10162 void *p;
10163
10164 address_offset = sizeof(void *) == 8 ? 2 : 1;
10165 *(void **)(except_code_set_ymm0 + address_offset) = data;
10166 *(void **)(except_code_reset_ymm_state + address_offset) = data;
10167
10168 if (!pRtlGetEnabledExtendedFeatures)
10169 {
10170 win_skip("RtlGetEnabledExtendedFeatures is not available.\n");
10171 return;
10172 }
10173
10174 enabled_features = pRtlGetEnabledExtendedFeatures(~(ULONG64)0);
10175
10176#ifndef __arm64ec__
10177 if (enabled_features)
10178 {
10179 int regs[4];
10180
10181 __cpuidex(regs, 0xd, 1);
10182 xsaveopt_enabled = regs[0] & 1;
10183 compaction_enabled = regs[0] & 2;
10184 }
10185#endif
10186 xstate_supported_features = enabled_features & supported_features;
10187
10188 /* Test context manipulation functions. */
10189 length = 0xdeadbeef;
10190 ret = pRtlGetExtendedContextLength(0, &length);
10191 ok(ret == STATUS_INVALID_PARAMETER && length == 0xdeadbeef, "Got unexpected result ret %#lx, length %#lx.\n",
10192 ret, length);
10193
10194 ret = pRtlGetExtendedContextLength(context_arch_new[0].flag, &length);
10195 ok(!ret, "Got %#lx.\n", ret);
10196 if (length == context_arch_new[0].context_length + context_arch_new[0].context_ex_length
10197 + context_arch_new[0].align)
10198 context_arch = context_arch_new;
10199 else
10200 context_arch = context_arch_old;
10201
10202 for (test = 0; test < 2; ++test)
10203 {
10204 expected_length = context_arch[test].context_length + context_arch[test].context_ex_length
10205 + context_arch[test].align;
10206 expected_length_xstate = context_arch[test].context_length + context_arch[test].context_ex_length
10207 + sizeof(XSTATE) + 63;
10208
10209 length = 0xdeadbeef;
10210 ret = pRtlGetExtendedContextLength(context_arch[test].flag, &length);
10211 ok(!ret && length == expected_length, "Got unexpected result ret %#lx, length %#lx.\n",
10212 ret, length);
10213
10214 for (i = 0; i < 32; ++i)
10215 {
10216 if (i == 6) /* CONTEXT_XSTATE */
10217 continue;
10218
10219 flags = context_arch[test].flag | (1 << i);
10220 length = length2 = 0xdeadbeef;
10221 ret = pRtlGetExtendedContextLength(flags, &length);
10222
10223 if ((context_arch[test].supported_flags & flags) || flags == context_arch[test].flag)
10224 {
10225 ok((!ret && length == expected_length)
10226 || broken((context_arch[test].broken_flags & (1 << i))
10227 && ret == STATUS_INVALID_PARAMETER && length == 0xdeadbeef),
10228 "Got unexpected result ret %#lx, length %#lx, flags 0x%08lx.\n",
10229 ret, length, flags);
10230 }
10231 else
10232 {
10233 ok((ret == STATUS_INVALID_PARAMETER || ret == STATUS_NOT_SUPPORTED) && length == 0xdeadbeef,
10234 "Got unexpected result ret %#lx, length %#lx, flags 0x%08lx.\n", ret, length, flags);
10235 }
10236
10237 SetLastError(0xdeadbeef);
10238 bret = pInitializeContext(NULL, flags, NULL, &length2);
10239 ok(!bret && length2 == length && GetLastError()
10242 "Got unexpected bret %#x, length2 %#lx, GetLastError() %lu, flags %#lx.\n",
10243 bret, length2, GetLastError(), flags);
10244
10246 continue;
10247
10248 SetLastError(0xdeadbeef);
10249 context = (void *)0xdeadbeef;
10250 length2 = expected_length - 1;
10251 bret = pInitializeContext(context_buffer, flags, &context, &length2);
10253 "Got unexpected bret %#x, GetLastError() %lu, flags %#lx.\n", bret, GetLastError(), flags);
10254 ok(context == (void *)0xdeadbeef, "Got unexpected context %p.\n", context);
10255
10256 SetLastError(0xdeadbeef);
10257 memset(context_buffer, 0xcc, sizeof(context_buffer));
10258 length2 = expected_length;
10259 bret = pInitializeContext(context_buffer, flags, &context, &length2);
10260 ok(bret && GetLastError() == 0xdeadbeef,
10261 "Got unexpected bret %#x, GetLastError() %lu, flags %#lx.\n", bret, GetLastError(), flags);
10262 ok(length2 == expected_length, "Got unexpected length %#lx.\n", length);
10263 ok((BYTE *)context == context_buffer, "Got unexpected context %p, flags %#lx.\n", context, flags);
10264
10265 context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
10266 ok(context_flags == flags, "Got unexpected ContextFlags %#lx, flags %#lx.\n", context_flags, flags);
10267
10268 context_ex = (CONTEXT_EX *)(context_buffer + context_arch[test].context_length);
10269 ok(context_ex->Legacy.Offset == -(int)context_arch[test].context_length,
10270 "Got unexpected Offset %ld, flags %#lx.\n", context_ex->Legacy.Offset, flags);
10271 ok(context_ex->Legacy.Length == ((flags & 0x20) ? context_arch[test].context_length
10272 : context_arch[test].legacy_length),
10273 "Got unexpected Length %#lx, flags %#lx.\n", context_ex->Legacy.Length, flags);
10274 ok(context_ex->All.Offset == -(int)context_arch[test].context_length,
10275 "Got unexpected Offset %ld, flags %#lx.\n", context_ex->All.Offset, flags);
10276
10277 /* No extra 8 bytes in x64 CONTEXT_EX here (before Win11). */
10278 ok(context_ex->All.Length == context_arch[test].context_length + context_arch[1].context_ex_length,
10279 "Got unexpected Length %#lx, flags %#lx.\n", context_ex->All.Length, flags);
10280
10281 ok(context_ex->XState.Offset == context_arch[1].context_ex_length + 1,
10282 "Got unexpected Offset %ld, flags %#lx.\n", context_ex->XState.Offset, flags);
10284 "Got unexpected Length %#lx, flags %#lx.\n", context_ex->XState.Length, flags);
10285
10286 if (0)
10287 {
10288 /* Crashes on Windows. */
10290 }
10292 ok(p == context, "Got unexpected p %p, flags %#lx.\n", p, flags);
10293 length2 = 0xdeadbeef;
10295 ok(p == context && length2 == context_ex->Legacy.Length,
10296 "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags);
10297 length2 = expected_length;
10298
10299 if (0)
10300 {
10301 /* Crashes on Windows. */
10302 pGetXStateFeaturesMask(context, NULL);
10303 pRtlGetExtendedFeaturesMask(context_ex);
10304 pRtlSetExtendedFeaturesMask(context_ex, 0);
10305 }
10306
10307 flags_fpx = flags & 0x10000 ? flags | 0x20 : flags | 0x8;
10308
10309 mask = 0xdeadbeef;
10310 bret = pGetXStateFeaturesMask(context, &mask);
10311 SetLastError(0xdeadbeef);
10312 if (flags & CONTEXT_NATIVE)
10313 ok(bret && mask == ((flags & flags_fpx) == flags_fpx ? 0x3 : 0),
10314 "Got unexpected bret %#x, mask %s, flags %#lx.\n", bret, wine_dbgstr_longlong(mask), flags);
10315 else
10316 ok(!bret && mask == 0xdeadbeef && GetLastError() == 0xdeadbeef,
10317 "Got unexpected bret %#x, mask %s, GetLastError() %#lx, flags %#lx.\n",
10319
10320 bret = pSetXStateFeaturesMask(context, 0);
10321 ok(bret == !!(flags & CONTEXT_NATIVE), "Got unexpected bret %#x, flags %#lx.\n", bret, flags);
10322 context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
10323 ok(context_flags == flags, "Got unexpected ContextFlags %#lx, flags %#lx.\n", context_flags, flags);
10324
10325 bret = pSetXStateFeaturesMask(context, 1);
10326 ok(bret == !!(flags & CONTEXT_NATIVE), "Got unexpected bret %#x, flags %#lx.\n", bret, flags);
10327 context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
10328 ok(context_flags == (bret ? flags_fpx : flags),
10329 "Got unexpected ContextFlags %#lx, flags %#lx.\n", context_flags, flags);
10330
10331 bret = pSetXStateFeaturesMask(context, 2);
10332 ok(bret == !!(flags & CONTEXT_NATIVE), "Got unexpected bret %#x, flags %#lx.\n", bret, flags);
10333 context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
10334 ok(context_flags == (bret ? flags_fpx : flags),
10335 "Got unexpected ContextFlags %#lx, flags %#lx.\n", context_flags, flags);
10336
10337 bret = pSetXStateFeaturesMask(context, 4);
10338 ok(!bret, "Got unexpected bret %#x.\n", bret);
10339 context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
10340 ok(context_flags == (flags & CONTEXT_NATIVE ? flags_fpx : flags),
10341 "Got unexpected ContextFlags %#lx, flags %#lx.\n", context_flags, flags);
10342 *(DWORD *)(context_buffer + context_arch[test].flags_offset) = flags;
10343
10344 for (j = 0; j < context_arch[test].flags_offset; ++j)
10345 {
10346 if (context_buffer[j] != 0xcc)
10347 {
10348 ok(0, "Buffer data changed at offset %#x.\n", j);
10349 break;
10350 }
10351 }
10352 for (j = context_arch[test].flags_offset + sizeof(context_flags);
10353 j < context_arch[test].context_length; ++j)
10354 {
10355 if (context_buffer[j] != 0xcc)
10356 {
10357 ok(0, "Buffer data changed at offset %#x.\n", j);
10358 break;
10359 }
10360 }
10361 for (j = context_arch[test].context_length + context_arch[test].context_ex_length;
10362 j < sizeof(context_buffer); ++j)
10363 {
10364 if (context_buffer[j] != 0xcc)
10365 {
10366 ok(0, "Buffer data changed at offset %#x.\n", j);
10367 break;
10368 }
10369 }
10370
10371 memset(context_buffer2, 0xcc, sizeof(context_buffer2));
10372 ret2 = pRtlInitializeExtendedContext(context_buffer2, flags, &context_ex);
10373 ok(!ret2, "Got unexpected ret2 %#lx, flags %#lx.\n", ret2, flags);
10374 ok(!memcmp(context_buffer2, context_buffer, sizeof(context_buffer2)),
10375 "Context data do not match, flags %#lx.\n", flags);
10376
10377 memset(context_buffer2, 0xcc, sizeof(context_buffer2));
10378 ret2 = pRtlInitializeExtendedContext(context_buffer2 + 2, flags, &context_ex);
10379 ok(!ret2, "Got unexpected ret2 %#lx, flags %#lx.\n", ret2, flags);
10380
10381 /* Buffer gets aligned to 16 bytes on x64, while returned context length suggests it should be 8. */
10382 align = test ? 4 : 16;
10383 ok(!memcmp(context_buffer2 + align, context_buffer,
10384 sizeof(context_buffer2) - align),
10385 "Context data do not match, flags %#lx.\n", flags);
10386
10387 SetLastError(0xdeadbeef);
10388 memset(context_buffer2, 0xcc, sizeof(context_buffer2));
10389 bret = pInitializeContext(context_buffer2 + 2, flags, &context, &length2);
10390 ok(bret && GetLastError() == 0xdeadbeef,
10391 "Got unexpected bret %#x, GetLastError() %lu, flags %#lx.\n", bret, GetLastError(), flags);
10392 ok(length2 == expected_length, "Got unexpected length %#lx.\n", length);
10393 ok(!memcmp(context_buffer2 + align, context_buffer,
10394 sizeof(context_buffer2) - align),
10395 "Context data do not match, flags %#lx.\n", flags);
10396
10397 length2 = 0xdeadbeef;
10398 p = pLocateXStateFeature(context, 0, &length2);
10399 if (flags & CONTEXT_NATIVE)
10400 ok(p == (BYTE *)context + context_arch[test].xsavearea_offset
10401 && length2 == offsetof(XSAVE_FORMAT, XmmRegisters),
10402 "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags);
10403 else
10404 ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags);
10405 length2 = 0xdeadbeef;
10406 p = pLocateXStateFeature(context, 1, &length2);
10407 if (flags & CONTEXT_NATIVE)
10408 ok(p == (BYTE *)context + context_arch[test].xsavearea_offset + offsetof(XSAVE_FORMAT, XmmRegisters)
10409 && length2 == sizeof(M128A) * context_arch[test].vector_reg_count,
10410 "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags);
10411 else
10412 ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags);
10413 length2 = 0xdeadbeef;
10414 p = pLocateXStateFeature(context, 2, &length2);
10415 ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags);
10416
10417 if (!pRtlInitializeExtendedContext2 || !pInitializeContext2)
10418 {
10419 static int once;
10420
10421 if (!once++)
10422 win_skip("InitializeContext2 is not available.\n");
10423 continue;
10424 }
10425
10426 length2 = expected_length;
10427 memset(context_buffer2, 0xcc, sizeof(context_buffer2));
10428 ret2 = pRtlInitializeExtendedContext2(context_buffer2 + 2, flags, &context_ex, ~(ULONG64)0);
10429 ok(!ret2, "Got unexpected ret2 %#lx, flags %#lx.\n", ret2, flags);
10430 ok(!memcmp(context_buffer2 + align, context_buffer,
10431 sizeof(context_buffer2) - align),
10432 "Context data do not match, flags %#lx.\n", flags);
10433
10434 memset(context_buffer2, 0xcc, sizeof(context_buffer2));
10435 bret = pInitializeContext2(context_buffer2 + 2, flags, &context, &length2, 0);
10436 ok(bret && GetLastError() == 0xdeadbeef,
10437 "Got unexpected bret %#x, GetLastError() %lu, flags %#lx.\n", bret, GetLastError(), flags);
10438 ok(length2 == expected_length, "Got unexpected length %#lx.\n", length);
10439 ok(!memcmp(context_buffer2 + align, context_buffer,
10440 sizeof(context_buffer2) - align),
10441 "Context data do not match, flags %#lx.\n", flags);
10442
10443 length2 = 0xdeadbeef;
10444 p = pLocateXStateFeature(context, 0, &length2);
10445 if (flags & CONTEXT_NATIVE)
10446 ok(p == (BYTE *)context + context_arch[test].xsavearea_offset
10447 && length2 == offsetof(XSAVE_FORMAT, XmmRegisters),
10448 "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags);
10449 else
10450 ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags);
10451 }
10452
10453 flags = context_arch[test].flag | 0x40;
10454 flags_fpx = flags & 0x10000 ? flags | 0x20 : flags | 0x8;
10455
10456 length = 0xdeadbeef;
10457 ret = pRtlGetExtendedContextLength(flags, &length);
10458
10459 if (!enabled_features)
10460 {
10461 ok(ret == STATUS_NOT_SUPPORTED && length == 0xdeadbeef,
10462 "Got unexpected result ret %#lx, length %#lx.\n", ret, length);
10463
10464 context_ex = (void *)0xdeadbeef;
10465 ret2 = pRtlInitializeExtendedContext(context_buffer, flags, &context_ex);
10466 ok(ret2 == STATUS_NOT_SUPPORTED, "Got unexpected result ret %#lx, test %u.\n", ret2, test);
10467
10468 SetLastError(0xdeadbeef);
10469 length2 = sizeof(context_buffer);
10470 bret = pInitializeContext(context_buffer, flags, &context, &length2);
10471 ok(bret && GetLastError() == 0xdeadbeef,
10472 "Got unexpected bret %#x, GetLastError() %lu, flags %#lx.\n", bret, GetLastError(), flags);
10473 context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
10474 ok(context_flags == (flags & ~0x40), "Got unexpected ContextFlags %#lx, flags %#lx.\n",
10476
10477 if (pInitializeContext2)
10478 {
10479 SetLastError(0xdeadbeef);
10480 length2 = sizeof(context_buffer);
10481 bret = pInitializeContext2(context_buffer, flags, &context, &length2, ~(ULONG64)0);
10482 ok(bret && GetLastError() == 0xdeadbeef,
10483 "Got unexpected bret %#x, GetLastError() %lu, flags %#lx.\n", bret, GetLastError(), flags);
10484 context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
10485 ok(context_flags == (flags & ~0x40), "Got unexpected ContextFlags %#lx, flags %#lx.\n",
10487 }
10488 continue;
10489 }
10490
10491 ok(!ret && length >= expected_length_xstate,
10492 "Got unexpected result ret %#lx, length %#lx, test %u.\n", ret, length, test);
10493
10494 if (!pRtlGetExtendedContextLength2)
10495 {
10496 win_skip("RtlGetExtendedContextLength2 is not available.\n");
10497 }
10498 else
10499 {
10500 length = 0xdeadbeef;
10501 ret = pRtlGetExtendedContextLength2(flags, &length, 7);
10502 ok(!ret && length == expected_length_xstate,
10503 "Got unexpected result ret %#lx, length %#lx, test %u.\n", ret, length, test);
10504
10505 length = 0xdeadbeef;
10506 ret = pRtlGetExtendedContextLength2(flags, &length, ~0);
10507 ok(!ret && length >= expected_length_xstate,
10508 "Got unexpected result ret %#lx, length %#lx, test %u.\n", ret, length, test);
10509
10510 length = 0xdeadbeef;
10511 ret = pRtlGetExtendedContextLength2(flags, &length, 0);
10512 ok((!ret && length == expected_length_xstate - sizeof(YMMCONTEXT))
10513 || broken(!ret && length == expected_length_xstate) /* win10pro */,
10514 "Got unexpected result ret %#lx, length %#lx, test %u.\n", ret, length, test);
10515
10516 length = 0xdeadbeef;
10517 ret = pRtlGetExtendedContextLength2(flags, &length, 3);
10518 ok((!ret && length == expected_length_xstate - sizeof(YMMCONTEXT))
10519 || broken(!ret && length == expected_length_xstate) /* win10pro */,
10520 "Got unexpected result ret %#lx, length %#lx, test %u.\n", ret, length, test);
10521
10522 length = 0xdeadbeef;
10523 ret = pRtlGetExtendedContextLength2(flags, &length, 4);
10524 ok(!ret && length == expected_length_xstate,
10525 "Got unexpected result ret %#lx, length %#lx, test %u.\n", ret, length, test);
10526 }
10527
10528 pRtlGetExtendedContextLength(flags, &length);
10529 SetLastError(0xdeadbeef);
10530 bret = pInitializeContext(NULL, flags, NULL, &length2);
10531 ok(!bret && length2 == length && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
10532 "Got unexpected bret %#x, length2 %#lx, GetLastError() %lu, flags %#lx.\n",
10533 bret, length2, GetLastError(), flags);
10534
10535 SetLastError(0xdeadbeef);
10536 context = (void *)0xdeadbeef;
10537 length2 = length - 1;
10538 bret = pInitializeContext(context_buffer, flags, &context, &length2);
10539 ok(!bret && GetLastError() == ERROR_INSUFFICIENT_BUFFER && length2 == length && context == (void *)0xdeadbeef,
10540 "Got unexpected bret %#x, GetLastError() %lu, length2 %#lx, flags %#lx.\n",
10541 bret, GetLastError(), length2, flags);
10542
10543 SetLastError(0xdeadbeef);
10544 memset(context_buffer, 0xcc, sizeof(context_buffer));
10545 length2 = length + 1;
10546 bret = pInitializeContext(context_buffer, flags, &context, &length2);
10547 ok(bret && GetLastError() == 0xdeadbeef,
10548 "Got unexpected bret %#x, GetLastError() %lu, flags %#lx.\n", bret, GetLastError(), flags);
10549 ok(length2 == length, "Got unexpected length %#lx.\n", length);
10550 ok((BYTE *)context == context_buffer, "Got unexpected context %p.\n", context);
10551
10552 context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
10553 ok(context_flags == flags, "Got unexpected ContextFlags %#lx, flags %#lx.\n", context_flags, flags);
10554
10555 context_ex = (CONTEXT_EX *)(context_buffer + context_arch[test].context_length);
10556 ok(context_ex->Legacy.Offset == -(int)context_arch[test].context_length,
10557 "Got unexpected Offset %ld, flags %#lx.\n", context_ex->Legacy.Offset, flags);
10558 ok(context_ex->Legacy.Length == ((flags & 0x20) ? context_arch[test].context_length
10559 : context_arch[test].legacy_length),
10560 "Got unexpected Length %#lx, flags %#lx.\n", context_ex->Legacy.Length, flags);
10561
10562 expected_offset = (((ULONG_PTR)context + context_arch[test].context_length
10563 + context_arch[test].context_ex_length + 63) & ~(ULONG64)63) - (ULONG_PTR)context
10564 - context_arch[test].context_length;
10565 ok(context_ex->XState.Offset == expected_offset,
10566 "Got unexpected Offset %ld, flags %#lx.\n", context_ex->XState.Offset, flags);
10567 ok(context_ex->XState.Length >= sizeof(XSTATE),
10568 "Got unexpected Length %#lx, flags %#lx.\n", context_ex->XState.Length, flags);
10569
10570 ok(context_ex->All.Offset == -(int)context_arch[test].context_length,
10571 "Got unexpected Offset %ld, flags %#lx.\n", context_ex->All.Offset, flags);
10572 /* No extra 8 bytes in x64 CONTEXT_EX here. */
10573 ok(context_ex->All.Length == context_arch[test].context_length
10575 "Got unexpected Length %#lx, flags %#lx.\n", context_ex->All.Length, flags);
10576
10577 xs = (XSTATE *)((BYTE *)context_ex + context_ex->XState.Offset);
10578 length2 = 0xdeadbeef;
10579 for (i = 0; i < 2; ++i)
10580 {
10582 ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#lx.\n", p, length2);
10583 }
10584
10586 ok(length2 == sizeof(YMMCONTEXT), "Got unexpected length %#lx.\n", length2);
10587 ok(p == &xs->YmmContext, "Got unexpected p %p.\n", p);
10589 ok(p == &xs->YmmContext, "Got unexpected p %p.\n", p);
10590
10591 length2 = 0xdeadbeef;
10592 p = pLocateXStateFeature(context, 0, &length2);
10593 if (flags & CONTEXT_NATIVE)
10594 ok(p == (BYTE *)context + context_arch[test].xsavearea_offset
10595 && length2 == offsetof(XSAVE_FORMAT, XmmRegisters),
10596 "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags);
10597 else
10598 ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags);
10599
10600 length2 = 0xdeadbeef;
10601 p = pLocateXStateFeature(context, 1, &length2);
10602 if (flags & CONTEXT_NATIVE)
10603 ok(p == (BYTE *)context + context_arch[test].xsavearea_offset + offsetof(XSAVE_FORMAT, XmmRegisters)
10604 && length2 == sizeof(M128A) * context_arch[test].vector_reg_count,
10605 "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags);
10606 else
10607 ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags);
10608
10609 length2 = 0xdeadbeef;
10610 p = pLocateXStateFeature(context, 2, &length2);
10611 if (flags & CONTEXT_NATIVE)
10612 ok(p == &xs->YmmContext && length2 == sizeof(YMMCONTEXT),
10613 "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags);
10614 else
10615 ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags);
10616
10617 mask = 0xdeadbeef;
10618 bret = pGetXStateFeaturesMask(context, &mask);
10619 if (flags & CONTEXT_NATIVE)
10620 ok(bret && !mask,
10621 "Got unexpected bret %#x, mask %s, flags %#lx.\n", bret, wine_dbgstr_longlong(mask), flags);
10622 else
10623 ok(!bret && mask == 0xdeadbeef,
10624 "Got unexpected bret %#x, mask %s, flags %#lx.\n", bret, wine_dbgstr_longlong(mask), flags);
10625
10626 expected_compaction = compaction_enabled ? ((ULONG64)1 << 63) | enabled_features : 0;
10627 ok(!xs->Mask, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
10628 mask = pRtlGetExtendedFeaturesMask(context_ex);
10629 ok(mask == (xs->Mask & ~(ULONG64)3), "Got unexpected mask %s.\n", wine_dbgstr_longlong(mask));
10630 ok(xs->CompactionMask == expected_compaction,
10631 "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(xs->CompactionMask));
10632 ok(!xs->Reserved[0], "Got unexpected Reserved[0] %s.\n", wine_dbgstr_longlong(xs->Reserved[0]));
10633
10634 xs->Mask = 0xdeadbeef;
10635 xs->CompactionMask = 0xdeadbeef;
10636 pRtlSetExtendedFeaturesMask(context_ex, 0);
10637 ok(!xs->Mask, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
10638 mask = pRtlGetExtendedFeaturesMask(context_ex);
10639 ok(mask == (xs->Mask & ~(ULONG64)3), "Got unexpected mask %s.\n", wine_dbgstr_longlong(mask));
10640 ok(xs->CompactionMask == 0xdeadbeef, "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(xs->CompactionMask));
10641 context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
10642 ok(context_flags == flags, "Got unexpected ContextFlags %#lx, flags %#lx.\n", context->ContextFlags, flags);
10643
10644 xs->Mask = 0xdeadbeef;
10645 xs->CompactionMask = 0;
10646 pRtlSetExtendedFeaturesMask(context_ex, ~(ULONG64)0);
10647 ok(xs->Mask == (enabled_features & ~(ULONG64)3), "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
10648 mask = pRtlGetExtendedFeaturesMask(context_ex);
10649 ok(mask == (xs->Mask & ~(ULONG64)3), "Got unexpected mask %s.\n", wine_dbgstr_longlong(mask));
10650 ok(!xs->CompactionMask, "Got unexpected CompactionMask %s.\n",
10652 context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
10653 ok(context_flags == flags, "Got unexpected ContextFlags %#lx, flags %#lx.\n", context->ContextFlags, flags);
10654
10655 xs->Mask = 0xdeadbeef;
10656 xs->CompactionMask = 0xdeadbeef;
10657 bret = pSetXStateFeaturesMask(context, xstate_supported_features);
10658 ok(bret == !!(flags & CONTEXT_NATIVE), "Got unexpected bret %#x.\n", bret);
10659 context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
10660 ok(context_flags == (bret ? flags_fpx : flags),
10661 "Got unexpected ContextFlags %#lx, flags %#lx.\n", context_flags, flags);
10662 ok(xs->Mask == bret ? 4 : 0xdeadbeef, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
10663 mask = pRtlGetExtendedFeaturesMask(context_ex);
10664 ok(mask == (xs->Mask & ~(ULONG64)3), "Got unexpected mask %s.\n", wine_dbgstr_longlong(mask));
10665 ok(xs->CompactionMask == bret ? expected_compaction : 0xdeadbeef, "Got unexpected CompactionMask %s.\n",
10667
10668 mask = 0xdeadbeef;
10669 bret = pGetXStateFeaturesMask(context, &mask);
10670 if (flags & CONTEXT_NATIVE)
10671 ok(bret && mask == xstate_supported_features,
10672 "Got unexpected bret %#x, mask %s, flags %#lx (enabled_features & supported_features %#I64x).\n", bret, wine_dbgstr_longlong(mask), flags, xstate_supported_features);
10673 else
10674 ok(!bret && mask == 0xdeadbeef,
10675 "Got unexpected bret %#x, mask %s, flags %#lx.\n", bret, wine_dbgstr_longlong(mask), flags);
10676
10677 if (pRtlGetExtendedContextLength2)
10678 {
10679 memset(context_buffer, 0xcc, sizeof(context_buffer));
10680 pRtlGetExtendedContextLength2(flags, &length, 0);
10681 SetLastError(0xdeadbeef);
10682 memset(context_buffer, 0xcc, sizeof(context_buffer));
10683 length2 = length;
10684 bret = pInitializeContext2(context_buffer, flags, &context, &length2, 0);
10685 ok(bret && GetLastError() == 0xdeadbeef,
10686 "Got unexpected bret %#x, GetLastError() %lu, flags %#lx.\n", bret, GetLastError(), flags);
10687 ok(length2 == length, "Got unexpected length %#lx.\n", length);
10688 ok((BYTE *)context == context_buffer, "Got unexpected context %p.\n", context);
10689
10690 length2 = 0xdeadbeef;
10691 p = pLocateXStateFeature(context, 0, &length2);
10692 if (flags & CONTEXT_NATIVE)
10693 ok(p == (BYTE *)context + context_arch[test].xsavearea_offset
10694 && length2 == offsetof(XSAVE_FORMAT, XmmRegisters),
10695 "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags);
10696 else
10697 ok(!p && length2 == 0xdeadbeef, "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags);
10698
10699 length2 = 0xdeadbeef;
10700 p = pRtlLocateExtendedFeature(context_ex, 2, &length2);
10701 ok((!p && length2 == sizeof(YMMCONTEXT))
10702 || broken(p && length2 == sizeof(YMMCONTEXT)) /* win10pro */,
10703 "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags);
10704
10705 length2 = 0xdeadbeef;
10706 p = pLocateXStateFeature(context, 2, &length2);
10707 ok(!p && length2 == (flags & CONTEXT_NATIVE) ? sizeof(YMMCONTEXT) : 0xdeadbeef,
10708 "Got unexpected p %p, length %#lx, flags %#lx.\n", p, length2, flags);
10709
10710 context_flags = *(DWORD *)(context_buffer + context_arch[test].flags_offset);
10711 ok(context_flags == flags, "Got unexpected ContextFlags %#lx, flags %#lx.\n", context_flags, flags);
10712
10713 context_ex = (CONTEXT_EX *)(context_buffer + context_arch[test].context_length);
10714 ok(context_ex->Legacy.Offset == -(int)context_arch[test].context_length,
10715 "Got unexpected Offset %ld, flags %#lx.\n", context_ex->Legacy.Offset, flags);
10716 ok(context_ex->Legacy.Length == ((flags & 0x20) ? context_arch[test].context_length
10717 : context_arch[test].legacy_length),
10718 "Got unexpected Length %#lx, flags %#lx.\n", context_ex->Legacy.Length, flags);
10719
10720 expected_offset = (((ULONG_PTR)context + context_arch[test].context_length
10721 + context_arch[test].context_ex_length + 63) & ~(ULONG64)63) - (ULONG_PTR)context
10722 - context_arch[test].context_length;
10723 ok(context_ex->XState.Offset == expected_offset,
10724 "Got unexpected Offset %ld, flags %#lx.\n", context_ex->XState.Offset, flags);
10725 ok(context_ex->XState.Length == sizeof(XSTATE) - sizeof(YMMCONTEXT)
10726 || broken(context_ex->XState.Length == sizeof(XSTATE)) /* win10pro */,
10727 "Got unexpected Length %#lx, flags %#lx.\n", context_ex->XState.Length, flags);
10728
10729 ok(context_ex->All.Offset == -(int)context_arch[test].context_length,
10730 "Got unexpected Offset %ld, flags %#lx.\n", context_ex->All.Offset, flags);
10731 /* No extra 8 bytes in x64 CONTEXT_EX here. */
10732 ok(context_ex->All.Length == context_arch[test].context_length
10734 "Got unexpected Length %#lx, flags %#lx.\n", context_ex->All.Length, flags);
10735
10736 expected_compaction = compaction_enabled ? (ULONG64)1 << 63 : 0;
10737 xs = (XSTATE *)((BYTE *)context_ex + context_ex->XState.Offset);
10738 ok(!xs->Mask, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
10739 ok(xs->CompactionMask == expected_compaction,
10740 "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(xs->CompactionMask));
10741 ok(!xs->Reserved[0], "Got unexpected Reserved[0] %s.\n", wine_dbgstr_longlong(xs->Reserved[0]));
10742
10743 pRtlSetExtendedFeaturesMask(context_ex, ~(ULONG64)0);
10744 ok(xs->Mask == (enabled_features & ~(ULONG64)3), "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
10745 ok(xs->CompactionMask == expected_compaction, "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(xs->CompactionMask));
10746 }
10747 }
10748
10749 length = 0xdeadbeef;
10750 ret = pRtlGetExtendedContextLength(context_arch[0].flag | context_arch[1].flag, &length);
10751 ok(ret == STATUS_INVALID_PARAMETER && length == 0xdeadbeef, "Got unexpected result ret %#lx, length %#lx.\n",
10752 ret, length);
10753
10754 if (0)
10755 {
10756 /* Crashes on Windows. */
10757 pRtlGetExtendedContextLength(CONTEXT_FULL, NULL);
10758 length = sizeof(context_buffer);
10759 pInitializeContext(context_buffer, CONTEXT_FULL, NULL, &length);
10760 pInitializeContext(context_buffer, CONTEXT_FULL, &context, NULL);
10761 }
10762
10763 if (!(enabled_features & (1 << XSTATE_AVX)))
10764 {
10765 skip("AVX is not supported.\n");
10766 return;
10767 }
10768
10769 /* Test RtlCaptureContext (doesn't support xstates). */
10770 length = sizeof(context_buffer);
10771 memset(context_buffer, 0xcc, sizeof(context_buffer));
10772 bret = pInitializeContext(context_buffer, CONTEXT_XSTATE, &context, &length);
10773 ok(bret, "Got unexpected bret %#x.\n", bret);
10774 context_ex = (CONTEXT_EX *)(context + 1);
10775 xs = (XSTATE *)((BYTE *)context_ex + context_ex->XState.Offset);
10776
10777 max_xstate_length = context_ex->XState.Length;
10778 ok(max_xstate_length >= sizeof(XSTATE), "XSTATE size: %#lx; min: %#Ix.\n", max_xstate_length, sizeof(XSTATE));
10779
10780 *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_addr) = RtlCaptureContext;
10781 *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_param1) = context;
10782 *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_param2) = (void *)0xdeadbeef;
10783 *(void **)(call_func_code_set_ymm0 + call_func_offsets.ymm0_save) = data;
10784 memcpy(code_mem, call_func_code_set_ymm0, sizeof(call_func_code_set_ymm0));
10785
10786 memcpy(data, test_extended_context_data, sizeof(data));
10787 func();
10788 ok(context->ContextFlags == (CONTEXT_FULL | CONTEXT_SEGMENTS), "Got unexpected ContextFlags %#lx.\n",
10789 context->ContextFlags);
10790 for (i = 0; i < 8; ++i)
10791 ok(data[i] == test_extended_context_data[i], "Got unexpected data %#x, i %u.\n", data[i], i);
10792
10793 /* Test GetThreadContext (current thread, ymm0 set). */
10794 length = sizeof(context_buffer);
10795 memset(context_buffer, 0xcc, sizeof(context_buffer));
10796 bret = pInitializeContext(context_buffer, CONTEXT_FULL | CONTEXT_XSTATE | CONTEXT_FLOATING_POINT,
10797 &context, &length);
10798 ok(bret, "Got unexpected bret %#x.\n", bret);
10799 memset(&xs->YmmContext, 0xcc, sizeof(xs->YmmContext));
10800
10802#ifdef __i386__
10803 expected_flags |= CONTEXT_EXTENDED_REGISTERS;
10804#endif
10805 pSetXStateFeaturesMask(context, ~(ULONG64)0);
10806 ok(context->ContextFlags == expected_flags, "Got unexpected ContextFlags %#lx.\n",
10807 context->ContextFlags);
10808 *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_addr) = GetThreadContext;
10809 *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_param1) = (void *)GetCurrentThread();
10810 *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_param2) = context;
10811 *(void **)(call_func_code_set_ymm0 + call_func_offsets.ymm0_save) = data;
10812 memcpy(code_mem, call_func_code_set_ymm0, sizeof(call_func_code_set_ymm0));
10813 xs->CompactionMask = 2;
10814 xs->Mask = compaction_enabled ? 2 : 0;
10815 context_ex->XState.Length = sizeof(XSTATE);
10816
10817 bret = func();
10818 ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
10819
10820 ok(context->ContextFlags == expected_flags, "Got unexpected ContextFlags %#lx.\n",
10821 context->ContextFlags);
10822 expected_compaction = compaction_enabled ? (ULONG64)1 << 63 : 0;
10823
10824 ok(!xs->Mask || broken(xs->Mask == 4) /* win10pro */,
10825 "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
10826 ok(xs->CompactionMask == expected_compaction, "Got unexpected CompactionMask %s.\n",
10827 wine_dbgstr_longlong(xs->CompactionMask));
10828
10829 for (i = 4; i < 8; ++i)
10830 ok(data[i] == test_extended_context_data[i], "Got unexpected data %#x, i %u.\n", data[i], i);
10831
10832 for (i = 0; i < 4; ++i)
10833 ok(((ULONG *)&xs->YmmContext)[i] == (xs->Mask == 4 ? test_extended_context_data[i + 4] : 0xcccccccc),
10834 "Got unexpected data %#lx, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i);
10835
10836 expected_compaction = compaction_enabled ? ((ULONG64)1 << 63) | 4 : 0;
10837
10838 xs->CompactionMask = 4;
10839 xs->Mask = compaction_enabled ? 0 : 4;
10840 context_ex->XState.Length = max_xstate_length + 64;
10841 bret = func();
10843 "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
10844 ok(context->ContextFlags == expected_flags, "Got unexpected ContextFlags %#lx.\n",
10845 context->ContextFlags);
10846 ok(xs->Mask == (compaction_enabled ? 0 : 4), "Got unexpected Mask %#I64x.\n", xs->Mask);
10847 ok(xs->CompactionMask == 4, "Got unexpected CompactionMask %s.\n",
10848 wine_dbgstr_longlong(xs->CompactionMask));
10849 for (i = 0; i < 4; ++i)
10850 ok(((ULONG *)&xs->YmmContext)[i] == 0xcccccccc
10851 || broken(((ULONG *)&xs->YmmContext)[i] == test_extended_context_data[i + 4]) /* win10pro */,
10852 "Got unexpected data %#lx, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i);
10853
10854 xs->CompactionMask = 4;
10855 xs->Mask = compaction_enabled ? 0 : 4;
10856 context_ex->XState.Length = offsetof(XSTATE, YmmContext);
10857 bret = func();
10858 ok(context->ContextFlags == expected_flags, "Got unexpected ContextFlags %#lx.\n",
10859 context->ContextFlags);
10860 ok(!bret && GetLastError() == ERROR_MORE_DATA,
10861 "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
10862 ok(xs->Mask == 4, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
10863 ok(xs->CompactionMask == expected_compaction, "Got unexpected CompactionMask %s.\n",
10864 wine_dbgstr_longlong(xs->CompactionMask));
10865 for (i = 0; i < 4; ++i)
10866 ok(((ULONG *)&xs->YmmContext)[i] == 0xcccccccc
10867 || broken(((ULONG *)&xs->YmmContext)[i] == test_extended_context_data[i + 4]) /* win10pro */,
10868 "Got unexpected data %#lx, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i);
10869
10870 context_ex->XState.Length = sizeof(XSTATE);
10871 xs->CompactionMask = 4;
10872 xs->Mask = compaction_enabled ? 0 : 4;
10873 bret = func();
10874 ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
10875
10876 ok(context->ContextFlags == expected_flags, "Got unexpected ContextFlags %#lx.\n",
10877 context->ContextFlags);
10878
10879 ok(xs->Mask == 4, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
10880 ok(xs->CompactionMask == expected_compaction, "Got unexpected CompactionMask %s.\n",
10881 wine_dbgstr_longlong(xs->CompactionMask));
10882
10883 for (i = 4; i < 8; ++i)
10884 ok(data[i] == test_extended_context_data[i], "Got unexpected data %#x, i %u.\n", data[i], i);
10885
10886 for (i = 0; i < 4; ++i)
10887 ok(((ULONG *)&xs->YmmContext)[i] == test_extended_context_data[i + 4],
10888 "Got unexpected data %#lx, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i);
10889
10890 /* Test GetThreadContext (current thread, ymm state cleared). */
10891 length = sizeof(context_buffer);
10892 memset(context_buffer, 0xcc, sizeof(context_buffer));
10893 bret = pInitializeContext(context_buffer, CONTEXT_FULL | CONTEXT_XSTATE | CONTEXT_FLOATING_POINT,
10894 &context, &length);
10895 memset(&xs->YmmContext, 0xcc, sizeof(xs->YmmContext));
10896 ok(bret, "Got unexpected bret %#x.\n", bret);
10897
10898 /* clear potentially leftover xstate */
10899 pSetXStateFeaturesMask(context, 0);
10900 context->ContextFlags = CONTEXT_XSTATE;
10902
10904
10905 pSetXStateFeaturesMask(context, ~(ULONG64)0);
10906 *(void **)(call_func_code_reset_ymm_state + call_func_offsets.func_addr) = GetThreadContext;
10907 *(void **)(call_func_code_reset_ymm_state + call_func_offsets.func_param1) = (void *)GetCurrentThread();
10908 *(void **)(call_func_code_reset_ymm_state + call_func_offsets.func_param2) = context;
10909 *(void **)(call_func_code_reset_ymm_state + call_func_offsets.ymm0_save) = data;
10910 memcpy(code_mem, call_func_code_reset_ymm_state, sizeof(call_func_code_reset_ymm_state));
10911
10912 bret = func();
10913 ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
10914
10916#ifdef __i386__
10917 expected_flags |= CONTEXT_EXTENDED_REGISTERS;
10918#endif
10919 ok(context->ContextFlags == expected_flags, "Got unexpected ContextFlags %#lx.\n",
10920 context->ContextFlags);
10921
10922 expected_compaction = compaction_enabled ? ((ULONG64)1 << 63) | (xstate_supported_features & ~(UINT64)3) : 0;
10923
10924 xs = (XSTATE *)((BYTE *)context_ex + context_ex->XState.Offset);
10925 ok((xs->Mask & supported_features) == (xsaveopt_enabled ? 0 : 4), "Got unexpected Mask %#I64x.\n", xs->Mask);
10926 ok((xs->CompactionMask & (supported_features | ((ULONG64)1 << 63))) == expected_compaction,
10927 "Got unexpected CompactionMask %s (expected %#I64x).\n", wine_dbgstr_longlong(xs->CompactionMask), expected_compaction);
10928
10929 for (i = 4; i < 8; ++i)
10930 ok(!data[i], "Got unexpected data %#x, i %u.\n", data[i], i);
10931
10932 for (i = 0; i < 4; ++i)
10933 ok(((ULONG *)&xs->YmmContext)[i] == ((xs->Mask & 4) ? 0 : 0xcccccccc)
10934 || broken(((ULONG *)&xs->YmmContext)[i] == test_extended_context_data[i + 4]),
10935 "Got unexpected data %#lx, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i);
10936
10937 /* Test setting context which has only part of xstate in CompactionMask. */
10938 if (compaction_enabled && enabled_features & ((ULONG64)1 << XSTATE_AVX512_KMASK))
10939 {
10940 *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_addr) = SetThreadContext;
10941 *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_param1) = (void *)GetCurrentThread();
10942 *(void **)(call_func_code_set_ymm0 + call_func_offsets.func_param2) = context;
10943 *(void **)(call_func_code_set_ymm0 + call_func_offsets.ymm0_save) = data;
10944 memcpy(code_mem, call_func_code_set_ymm0, sizeof(call_func_code_set_ymm0));
10945 context->ContextFlags = CONTEXT_XSTATE;
10946 xs->CompactionMask = 0x8000000000000000 | ((ULONG64)1 << XSTATE_AVX512_KMASK);
10947 xs->Mask = 0;
10948 memcpy(data, test_extended_context_data, sizeof(data));
10949 bret = func();
10950 ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
10951 /* Setting a context with only part of xstate in CompactionMask doesn't change missing parts. */
10952 for (i = 4; i < 8; ++i)
10953 ok(data[i] == test_extended_context_data[i], "Got unexpected data %#x, i %u.\n", data[i], i);
10954
10955 memcpy(data, test_extended_context_data, sizeof(data));
10956 xs->CompactionMask |= XSTATE_MASK_GSSE;
10957 bret = func();
10958 ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
10959 for (i = 4; i < 8; ++i)
10960 ok(!data[i], "Got unexpected data %#x, i %u.\n", data[i], i);
10961 }
10962 else
10963 {
10964 skip("avx512 is not available, skipping test.\n");
10965 }
10966
10967 /* Test fault exception context. */
10968 memset(data, 0xff, sizeof(data));
10969 xs->Mask = 0;
10970 test_extended_context_modified_state = FALSE;
10971 run_exception_test(test_extended_context_handler, NULL, except_code_reset_ymm_state,
10972 ARRAY_SIZE(except_code_reset_ymm_state), PAGE_EXECUTE_READ);
10973 for (i = 0; i < 8; ++i)
10974 {
10975 /* Older Windows version do not reset AVX context to INIT_STATE on x86. */
10976 ok(!data[i] || broken(i >= 4 && sizeof(void *) == 4 && data[i] == test_extended_context_spoil_data2[i]),
10977 "Got unexpected data %#x, i %u.\n", data[i], i);
10978 }
10979
10980 memcpy(data, test_extended_context_data, sizeof(data));
10981 test_extended_context_modified_state = TRUE;
10982 run_exception_test(test_extended_context_handler, NULL, except_code_set_ymm0,
10983 ARRAY_SIZE(except_code_set_ymm0), PAGE_EXECUTE_READ);
10984
10985 for (i = 0; i < 8; ++i)
10986 ok(data[i] == test_extended_context_data[i], "Got unexpected data %#x, i %u.\n", data[i], i);
10987
10988 /* Test GetThreadContext for the other thread. */
10989 thread = CreateThread(NULL, 0, test_extended_context_thread, 0, CREATE_SUSPENDED, NULL);
10990 ok(!!thread, "Failed to create thread.\n");
10991
10992 bret = pInitializeContext(context_buffer, CONTEXT_FULL | CONTEXT_XSTATE | CONTEXT_FLOATING_POINT,
10993 &context, &length);
10994 ok(bret, "Got unexpected bret %#x.\n", bret);
10995 memset(&xs->YmmContext, 0xcc, sizeof(xs->YmmContext));
10996 context_ex = (CONTEXT_EX *)(context + 1);
10997 xs = (XSTATE *)((BYTE *)context_ex + context_ex->XState.Offset);
10998 pSetXStateFeaturesMask(context, 4);
10999
11001 ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
11002 todo_wine_if (!xsaveopt_enabled)
11003 ok((xs->Mask & supported_features) == (xsaveopt_enabled ? 0 : 4), "Got unexpected Mask %#I64x.\n", xs->Mask);
11004 ok((xs->CompactionMask & supported_compaction_mask) == expected_compaction,
11005 "Got unexpected CompactionMask %I64x, expected %I64x.\n", xs->CompactionMask,
11006 expected_compaction);
11007
11008 for (i = 0; i < 16 * 4; ++i)
11009 ok(((ULONG *)&xs->YmmContext)[i] == ((xs->Mask & 4) ? 0 : 0xcccccccc),
11010 "Got unexpected value %#lx, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i);
11011
11012 pSetXStateFeaturesMask(context, 4);
11013 memset(&xs->YmmContext, 0, sizeof(xs->YmmContext));
11015 ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
11016
11017 memset(&xs->YmmContext, 0xcc, sizeof(xs->YmmContext));
11019 ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
11020 ok(!(xs->Mask & supported_features) || broken((xs->Mask & supported_features) == 4), "Got unexpected Mask %s.\n",
11021 wine_dbgstr_longlong(xs->Mask));
11022 ok((xs->CompactionMask & supported_compaction_mask) == expected_compaction, "Got unexpected CompactionMask %s.\n",
11023 wine_dbgstr_longlong(xs->CompactionMask));
11024 for (i = 0; i < 16 * 4; ++i)
11025 ok(((ULONG *)&xs->YmmContext)[i] == 0xcccccccc || broken(xs->Mask == 4 && !((ULONG *)&xs->YmmContext)[i]),
11026 "Got unexpected value %#lx, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i);
11027
11028 pSetXStateFeaturesMask(context, 4);
11029 memset(&xs->YmmContext, 0x28, sizeof(xs->YmmContext));
11031 ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
11032 memset(&xs->YmmContext, 0xcc, sizeof(xs->YmmContext));
11034 ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
11035 ok((xs->Mask & supported_features) == 4, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
11036 ok((xs->CompactionMask & supported_compaction_mask) == expected_compaction, "Got unexpected CompactionMask %s.\n",
11037 wine_dbgstr_longlong(xs->CompactionMask));
11038 for (i = 0; i < 16 * 4; ++i)
11039 ok(((ULONG *)&xs->YmmContext)[i] == 0x28282828, "Got unexpected value %#lx, i %u.\n",
11040 ((ULONG *)&xs->YmmContext)[i], i);
11041
11042 wait_for_thread_next_suspend(thread);
11043
11045 ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
11046 pSetXStateFeaturesMask(context, 4);
11047 memset(&xs->YmmContext, 0x48, sizeof(xs->YmmContext));
11049 ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
11050
11051 wait_for_thread_next_suspend(thread);
11052
11053 memset(&xs->YmmContext, 0xcc, sizeof(xs->YmmContext));
11055 ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
11056 ok((xs->Mask & supported_features) == 4, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
11057
11058 for (i = 0; i < 4; ++i)
11059 ok(((ULONG *)&xs->YmmContext)[i] == 0x68686868, "Got unexpected value %#lx, i %u.\n",
11060 ((ULONG *)&xs->YmmContext)[i], i);
11061
11062 wait_for_thread_next_suspend(thread);
11063
11064 memset(&xs->YmmContext, 0xcc, sizeof(xs->YmmContext));
11066 ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
11067 todo_wine_if (!xsaveopt_enabled && sizeof(void *) != 4)
11068 ok((xs->Mask & supported_features) == (xsaveopt_enabled ? 0 : 4)
11069 || (sizeof(void *) == 4 && (xs->Mask & supported_features) == 4),
11070 "Got unexpected Mask %#I64x, supported_features.\n", xs->Mask);
11071 if ((xs->Mask & supported_features) == 4)
11072 {
11073 for (i = 0; i < 8 * sizeof(void *); ++i)
11074 ok(((ULONG *)&xs->YmmContext)[i] == 0,
11075 "Got unexpected value %#lx, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i);
11076 for (; i < 16 * 4; ++i)
11077 ok(((ULONG *)&xs->YmmContext)[i] == 0x48484848,
11078 "Got unexpected value %#lx, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i);
11079 }
11080 else
11081 {
11082 for (i = 0; i < 16 * 4; ++i)
11083 ok(((ULONG *)&xs->YmmContext)[i] == 0xcccccccc,
11084 "Got unexpected value %#lx, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i);
11085 }
11086
11087 if (compaction_enabled && enabled_features & ((ULONG64)1 << XSTATE_AVX512_KMASK))
11088 {
11089 ULONG64 saved_mask;
11090 ULONG *d;
11091
11092 saved_mask = xs->CompactionMask;
11093 xs->Mask = XSTATE_MASK_GSSE;
11094 xs->CompactionMask = 0x8000000000000000 | xs->Mask;
11095 *(ULONG *)&xs->YmmContext = 0x11111111;
11096 bret = SetThreadContext(thread, context);
11097 ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
11098
11099 xs->Mask = (ULONG64)1 << XSTATE_AVX512_KMASK;
11100 xs->CompactionMask = 0x8000000000000000 | xs->Mask;
11101 *(ULONG *)&xs->YmmContext = 0x22222222;
11102 bret = SetThreadContext(thread, context);
11103 ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
11104
11105 xs->CompactionMask = saved_mask;
11106 bret = GetThreadContext(thread, context);
11107 ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
11108
11109 todo_wine_if(xs->Mask == XSTATE_MASK_GSSE)
11110 ok((xs->Mask & (XSTATE_MASK_GSSE | ((ULONG64)1 << XSTATE_AVX512_KMASK)))
11111 == (XSTATE_MASK_GSSE | ((ULONG64)1 << XSTATE_AVX512_KMASK)), "got Mask %#I64x.\n", xs->Mask);
11112 d = pLocateXStateFeature(context, XSTATE_AVX, NULL);
11113 ok(!!d, "Got NULL.\n");
11114 ok(*d == 0x11111111, "got %#lx.\n", *d);
11115
11116 d = pLocateXStateFeature(context, XSTATE_AVX512_KMASK, NULL);
11117 ok(!!d, "Got NULL.\n");
11118 todo_wine ok(*d == 0x22222222, "got %#lx.\n", *d);
11119 }
11120 else
11121 {
11122 skip("avx512 is not available, skipping test.\n");
11123 }
11124
11125 bret = ResumeThread(thread);
11126 ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError());
11127
11128 WaitForSingleObject(thread, INFINITE);
11129 CloseHandle(thread);
11130}
11131
11132struct modified_range
11133{
11134 ULONG start;
11135 ULONG flag;
11136};
11137
11138#define check_changes_in_range(a, b, c, d) check_changes_in_range_(__FILE__, __LINE__, a, b, c, d)
11139static void check_changes_in_range_(const char *file, unsigned int line, const BYTE *p,
11140 const struct modified_range *range, ULONG flags, unsigned int length)
11141{
11142 ULONG range_flag, flag;
11143 unsigned int once = 0;
11144 unsigned int i;
11145
11146 range_flag = 0;
11147 for (i = 0; i < length; i++)
11148 {
11149 if (i == range->start)
11150 {
11151 range_flag = range->flag;
11152 ++range;
11153 }
11154
11155 if ((flag = range_flag) == ~0)
11156 continue;
11157
11158 if (flag & 0x80000000)
11159 {
11160 if (flag & flags && p[i] == 0xcc)
11161 {
11162 if (!once++)
11163 ok(broken(1), "Matched broken result at %#x, flags %#lx.\n", i, flags);
11164 continue;
11165 }
11166 flag = 0;
11167 }
11168
11169 if (flag & flags && p[i] != 0xcc)
11170 {
11171 ok_(file, line)(0, "Got unexpected byte %#x at %#x, flags %#lx.\n", p[i], i, flags);
11172 return;
11173 }
11174 else if (!(flag & flags) && p[i] != 0xdd)
11175 {
11176 ok_(file, line)(0, "Got unexpected byte %#x at %#x, flags %#lx.\n", p[i], i, flags);
11177 return;
11178 }
11179 }
11180 ok_(file, line)(1, "Range matches.\n");
11181}
11182
11183static void test_copy_context(void)
11184{
11185 static struct modified_range ranges_amd64[] =
11186 {
11187 {0x30, ~0}, {0x38, 0x1}, {0x3a, 0x4}, {0x42, 0x1}, {0x48, 0x10}, {0x78, 0x2}, {0x98, 0x1},
11188 {0xa0, 0x2}, {0xf8, 0x1}, {0x100, 0x8}, {0x2a0, 0x80000008}, {0x4b0, 0x10}, {0x4d0, ~0},
11189 {0x4e8, 0}, {0x500, ~0}, {0x640, 0}, {0x1000, 0},
11190 };
11191 static struct modified_range ranges_x86[] =
11192 {
11193 {0x0, ~0}, {0x4, 0x10}, {0x1c, 0x8}, {0x8c, 0x4}, {0x9c, 0x2}, {0xb4, 0x1}, {0xcc, 0x20}, {0x1ec, 0x80000020},
11194 {0x2cc, ~0}, {0x440, 0}, {0x1000, 0},
11195 };
11196 static const struct modified_range single_range[] =
11197 {
11198 {0x0, 0x1}, {0x1000, 0},
11199 };
11200
11201 static const ULONG tests[] =
11202 {
11203 /* AMD64 */
11204 CONTEXT_AMD64_CONTROL,
11205 CONTEXT_AMD64_INTEGER,
11206 CONTEXT_AMD64_SEGMENTS,
11207 CONTEXT_AMD64_FLOATING_POINT,
11208 CONTEXT_AMD64_DEBUG_REGISTERS,
11209 CONTEXT_AMD64_FULL,
11210 CONTEXT_AMD64_XSTATE,
11211 CONTEXT_AMD64_ALL,
11212 /* X86 */
11213 CONTEXT_I386_CONTROL,
11214 CONTEXT_I386_INTEGER,
11215 CONTEXT_I386_SEGMENTS,
11216 CONTEXT_I386_FLOATING_POINT,
11217 CONTEXT_I386_DEBUG_REGISTERS,
11218 CONTEXT_I386_EXTENDED_REGISTERS,
11219 CONTEXT_I386_XSTATE,
11220 CONTEXT_I386_ALL
11221 };
11222 static const ULONG arch_flags[] = {CONTEXT_AMD64, CONTEXT_i386};
11223
11224 DECLSPEC_ALIGN(64) BYTE src_context_buffer[4096];
11225 DECLSPEC_ALIGN(64) BYTE dst_context_buffer[4096];
11226 ULONG64 enabled_features, expected_compaction;
11227 unsigned int context_length, flags_offset, i;
11228 CONTEXT_EX *src_ex, *dst_ex;
11229 XSTATE *dst_xs, *src_xs;
11230 BOOL compaction, bret;
11231 CONTEXT *src, *dst;
11232 NTSTATUS status;
11233 DWORD length;
11234 ULONG flags;
11235
11236 if (!pRtlCopyExtendedContext)
11237 {
11238 win_skip("RtlCopyExtendedContext is not available.\n");
11239 return;
11240 }
11241
11242 if (!pRtlGetEnabledExtendedFeatures)
11243 {
11244 win_skip("RtlGetEnabledExtendedFeatures is not available.\n");
11245 return;
11246 }
11247
11248 enabled_features = pRtlGetEnabledExtendedFeatures(~(ULONG64)0);
11249
11250 memset(dst_context_buffer, 0xdd, sizeof(dst_context_buffer));
11251 memset(src_context_buffer, 0xcc, sizeof(src_context_buffer));
11252
11253 status = pRtlInitializeExtendedContext(src_context_buffer, CONTEXT_ALL | CONTEXT_XSTATE, &src_ex);
11254 if (!status)
11255 {
11256 src = pRtlLocateLegacyContext(src_ex, NULL);
11257 dst = (CONTEXT *)dst_context_buffer;
11258 dst->ContextFlags = CONTEXT_ALL;
11259 status = pRtlCopyContext(dst, dst->ContextFlags, src);
11260 ok(!status, "Got status %#lx.\n", status);
11261 check_changes_in_range((BYTE *)dst, CONTEXT_ALL & CONTEXT_AMD64 ? &ranges_amd64[0] : &ranges_x86[0],
11262 CONTEXT_ALL, sizeof(CONTEXT));
11263 }
11264 else
11265 {
11266 ok(status == STATUS_NOT_SUPPORTED, "Got status %#lx.\n", status);
11267 skip("Extended context is not supported.\n");
11268 }
11269
11270 for (i = 0; i < ARRAY_SIZE(tests); ++i)
11271 {
11272 flags = tests[i];
11273 flags_offset = (flags & CONTEXT_AMD64) ? offsetof(AMD64_CONTEXT,ContextFlags)
11274 : offsetof(I386_CONTEXT,ContextFlags);
11275
11276 memset(dst_context_buffer, 0xdd, sizeof(dst_context_buffer));
11277 memset(src_context_buffer, 0xcc, sizeof(src_context_buffer));
11278
11279 status = pRtlInitializeExtendedContext(src_context_buffer, flags, &src_ex);
11280 if (enabled_features || !(flags & 0x40))
11281 {
11282 ok(!status, "Got unexpected status %#lx, flags %#lx.\n", status, flags);
11283 }
11284 else
11285 {
11286 ok(status == STATUS_NOT_SUPPORTED, "Got unexpected status %#lx, flags %#lx.\n", status, flags);
11287 continue;
11288 }
11289 status = pRtlInitializeExtendedContext(dst_context_buffer, flags, &dst_ex);
11290 ok(!status, "Got unexpected status %#lx, flags %#lx.\n", status, flags);
11291
11292 src = pRtlLocateLegacyContext(src_ex, NULL);
11293 dst = pRtlLocateLegacyContext(dst_ex, NULL);
11294
11295 *(DWORD *)((BYTE *)dst + flags_offset) = 0;
11296 *(DWORD *)((BYTE *)src + flags_offset) = 0;
11297
11298 context_length = dst_ex->All.Length;
11299
11300 if (flags & 0x40)
11301 {
11302 src_xs = (XSTATE *)((BYTE *)src_ex + src_ex->XState.Offset);
11303 memset(src_xs, 0xcc, src_ex->XState.Length);
11304 src_xs->Mask = enabled_features & ~(ULONG64)4;
11305 src_xs->CompactionMask = ~(ULONG64)0;
11306 if (flags & CONTEXT_AMD64)
11307 ranges_amd64[ARRAY_SIZE(ranges_amd64) - 2].start = 0x640 + src_ex->XState.Length - sizeof(XSTATE);
11308 else
11309 ranges_x86[ARRAY_SIZE(ranges_x86) - 2].start = 0x440 + src_ex->XState.Length - sizeof(XSTATE);
11310 }
11311
11312 status = pRtlCopyExtendedContext(dst_ex, flags, src_ex);
11313 ok(!status, "Got unexpected status %#lx, flags %#lx.\n", status, flags);
11314
11315 check_changes_in_range((BYTE *)dst, flags & CONTEXT_AMD64 ? &ranges_amd64[0] : &ranges_x86[0],
11316 flags, context_length);
11317
11318 ok(*(DWORD *)((BYTE *)dst + flags_offset) == flags, "Got unexpected ContextFlags %#lx, flags %#lx.\n",
11319 *(DWORD *)((BYTE *)dst + flags_offset), flags);
11320
11321 memset(dst_context_buffer, 0xdd, sizeof(dst_context_buffer));
11322 status = pRtlInitializeExtendedContext(dst_context_buffer, flags, &dst_ex);
11323 ok(!status, "Got unexpected status %#lx, flags %#lx.\n", status, flags);
11324 *(DWORD *)((BYTE *)src + flags_offset) = 0;
11325 *(DWORD *)((BYTE *)dst + flags_offset) = 0;
11326 SetLastError(0xdeadbeef);
11327 status = pRtlCopyContext(dst, flags | 0x40, src);
11328 ok(status == (enabled_features ? STATUS_INVALID_PARAMETER : STATUS_NOT_SUPPORTED)
11329 || broken(status == STATUS_INVALID_PARAMETER),
11330 "Got unexpected status %#lx, flags %#lx.\n", status, flags);
11331 ok(*(DWORD *)((BYTE *)dst + flags_offset) == 0, "Got unexpected ContextFlags %#lx, flags %#lx.\n",
11332 *(DWORD *)((BYTE *)dst + flags_offset), flags);
11333 check_changes_in_range((BYTE *)dst, flags & CONTEXT_AMD64 ? &ranges_amd64[0] : &ranges_x86[0],
11334 0, context_length);
11335
11336 *(DWORD *)((BYTE *)dst + flags_offset) = flags & (CONTEXT_AMD64 | CONTEXT_i386);
11337 *(DWORD *)((BYTE *)src + flags_offset) = flags;
11338 status = pRtlCopyContext(dst, flags, src);
11339 if (flags & 0x40)
11340 ok((status == STATUS_BUFFER_OVERFLOW)
11341 || broken(!(flags & CONTEXT_NATIVE) && status == STATUS_INVALID_PARAMETER),
11342 "Got unexpected status %#lx, flags %#lx.\n", status, flags);
11343 else
11344 ok(!status || broken(!(flags & CONTEXT_NATIVE) && status == STATUS_INVALID_PARAMETER),
11345 "Got unexpected status %#lx, flags %#lx.\n", status, flags);
11346 if (!status)
11347 {
11348 ok(*(DWORD *)((BYTE *)dst + flags_offset) == flags, "Got unexpected ContextFlags %#lx, flags %#lx.\n",
11349 *(DWORD *)((BYTE *)dst + flags_offset), flags);
11350 check_changes_in_range((BYTE *)dst, flags & CONTEXT_AMD64 ? &ranges_amd64[0] : &ranges_x86[0],
11351 flags, context_length);
11352 }
11353 else
11354 {
11355 ok(*(DWORD *)((BYTE *)dst + flags_offset) == (flags & 0x110000),
11356 "Got unexpected ContextFlags %#lx, flags %#lx.\n",
11357 *(DWORD *)((BYTE *)dst + flags_offset), flags);
11358 check_changes_in_range((BYTE *)dst, flags & CONTEXT_AMD64 ? &ranges_amd64[0] : &ranges_x86[0],
11359 0, context_length);
11360 }
11361 }
11362
11363 for (i = 0; i < ARRAY_SIZE(arch_flags); ++i)
11364 {
11365 flags = arch_flags[i] | 0x42;
11366 flags_offset = (flags & CONTEXT_AMD64) ? offsetof(AMD64_CONTEXT,ContextFlags)
11367 : offsetof(I386_CONTEXT,ContextFlags);
11368 context_length = (flags & CONTEXT_AMD64) ? sizeof(AMD64_CONTEXT) : sizeof(I386_CONTEXT);
11369
11370 memset(dst_context_buffer, 0xdd, sizeof(dst_context_buffer));
11371 memset(src_context_buffer, 0xcc, sizeof(src_context_buffer));
11372 length = sizeof(src_context_buffer);
11373 bret = pInitializeContext(src_context_buffer, flags, &src, &length);
11374 ok(bret, "Got unexpected bret %#x, flags %#lx.\n", bret, flags);
11375
11376 length = sizeof(dst_context_buffer);
11377 bret = pInitializeContext(dst_context_buffer, flags, &dst, &length);
11378 ok(bret, "Got unexpected bret %#x, flags %#lx.\n", bret, flags);
11379
11380 dst_ex = (CONTEXT_EX *)((BYTE *)dst + context_length);
11381 src_ex = (CONTEXT_EX *)((BYTE *)src + context_length);
11382
11383 dst_xs = (XSTATE *)((BYTE *)dst_ex + dst_ex->XState.Offset);
11384 src_xs = (XSTATE *)((BYTE *)src_ex + src_ex->XState.Offset);
11385
11386 *(DWORD *)((BYTE *)dst + flags_offset) = 0;
11387 *(DWORD *)((BYTE *)src + flags_offset) = 0;
11388
11389 compaction = !!(src_xs->CompactionMask & ((ULONG64)1 << 63));
11390 expected_compaction = (compaction ? ((ULONG64)1 << (ULONG64)63) | enabled_features : 0);
11391
11392 memset(&src_xs->YmmContext, 0xcc, sizeof(src_xs->YmmContext));
11393 src_xs->CompactionMask = ~(ULONG64)0;
11394
11395 src_xs->Mask = 0;
11396 memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext));
11397 dst_xs->CompactionMask = 0xdddddddddddddddd;
11398 dst_xs->Mask = 0xdddddddddddddddd;
11399 dst_ex->XState.Length = 0;
11400 status = pRtlCopyExtendedContext(dst_ex, flags, src_ex);
11401 ok(status == (enabled_features ? STATUS_BUFFER_OVERFLOW : STATUS_NOT_SUPPORTED),
11402 "Got unexpected status %#lx, flags %#lx.\n", status, flags);
11403
11404 if (!enabled_features)
11405 continue;
11406
11407 ok(*(DWORD *)((BYTE *)dst + flags_offset) == flags, "Got unexpected ContextFlags %#lx, flags %#lx.\n",
11408 *(DWORD *)((BYTE *)dst + flags_offset), flags);
11409
11410 src_xs->Mask = ~(ULONG64)0;
11411
11412 memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext));
11413 dst_xs->CompactionMask = 0xdddddddddddddddd;
11414 dst_xs->Mask = 0xdddddddddddddddd;
11415 dst_ex->XState.Length = 0;
11416 status = pRtlCopyExtendedContext(dst_ex, flags, src_ex);
11417 ok(status == STATUS_BUFFER_OVERFLOW, "Got unexpected status %#lx, flags %#lx.\n", status, flags);
11418 ok(*(DWORD *)((BYTE *)dst + flags_offset) == flags, "Got unexpected ContextFlags %#lx, flags %#lx.\n",
11419 *(DWORD *)((BYTE *)dst + flags_offset), flags);
11420
11421 ok(dst_xs->Mask == 0xdddddddddddddddd, "Got unexpected Mask %s.\n",
11422 wine_dbgstr_longlong(dst_xs->Mask));
11423 ok(dst_xs->CompactionMask == 0xdddddddddddddddd, "Got unexpected CompactionMask %s.\n",
11424 wine_dbgstr_longlong(dst_xs->CompactionMask));
11425 check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range, 0, sizeof(dst_xs->YmmContext));
11426
11427 src_xs->Mask = 3;
11428 memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext));
11429 dst_xs->CompactionMask = 0xdddddddddddddddd;
11430 dst_xs->Mask = 0xdddddddddddddddd;
11431 dst_ex->XState.Length = offsetof(XSTATE, YmmContext);
11432 status = pRtlCopyExtendedContext(dst_ex, flags, src_ex);
11433 ok(!status, "Got unexpected status %#lx, flags %#lx.\n", status, flags);
11434 ok(*(DWORD *)((BYTE *)dst + flags_offset) == flags, "Got unexpected ContextFlags %#lx, flags %#lx.\n",
11435 *(DWORD *)((BYTE *)dst + flags_offset), flags);
11436 ok(dst_xs->Mask == 0, "Got unexpected Mask %s.\n",
11437 wine_dbgstr_longlong(dst_xs->Mask));
11438 ok(dst_xs->CompactionMask == expected_compaction,
11439 "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(dst_xs->CompactionMask));
11440 check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range, 0, sizeof(dst_xs->YmmContext));
11441
11442 memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext));
11443 dst_xs->CompactionMask = 0xdddddddddddddddd;
11444 dst_xs->Mask = 0xdddddddddddddddd;
11445 dst_ex->XState.Length = sizeof(XSTATE);
11446 status = pRtlCopyExtendedContext(dst_ex, flags, src_ex);
11447 ok(!status, "Got unexpected status %#lx, flags %#lx.\n", status, flags);
11448 ok(dst_xs->Mask == 0, "Got unexpected Mask %s.\n",
11449 wine_dbgstr_longlong(dst_xs->Mask));
11450 ok(dst_xs->CompactionMask == expected_compaction,
11451 "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(dst_xs->CompactionMask));
11452 check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range, 0, sizeof(dst_xs->YmmContext));
11453
11454 src_xs->Mask = 4;
11455 memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext));
11456 dst_xs->CompactionMask = 0xdddddddddddddddd;
11457 dst_xs->Mask = 0xdddddddddddddddd;
11458 status = pRtlCopyExtendedContext(dst_ex, flags, src_ex);
11459 ok(!status, "Got unexpected status %#lx, flags %#lx.\n", status, flags);
11460 ok(dst_xs->Mask == 4, "Got unexpected Mask %s.\n",
11461 wine_dbgstr_longlong(dst_xs->Mask));
11462 ok(dst_xs->CompactionMask == expected_compaction,
11463 "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(dst_xs->CompactionMask));
11464 check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range, 1, sizeof(dst_xs->YmmContext));
11465
11466 src_xs->Mask = 3;
11467 memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext));
11468 dst_xs->CompactionMask = 0xdddddddddddddddd;
11469 dst_xs->Mask = 0xdddddddddddddddd;
11470 status = pRtlCopyExtendedContext(dst_ex, flags, src_ex);
11471 ok(!status, "Got unexpected status %#lx, flags %#lx.\n", status, flags);
11472 ok(dst_xs->Mask == 0, "Got unexpected Mask %s.\n",
11473 wine_dbgstr_longlong(dst_xs->Mask));
11474 ok(dst_xs->CompactionMask == expected_compaction,
11475 "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(dst_xs->CompactionMask));
11476 check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range, 0, sizeof(dst_xs->YmmContext));
11477
11478
11479 *(DWORD *)((BYTE *)src + flags_offset) = arch_flags[i];
11480
11481 src_xs->Mask = 7;
11482 memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext));
11483 dst_xs->CompactionMask = 0xdddddddddddddddd;
11484 dst_xs->Mask = 0xdddddddddddddddd;
11485 status = pRtlCopyExtendedContext(dst_ex, flags, src_ex);
11486 ok(!status, "Got unexpected status %#lx, flags %#lx.\n", status, flags);
11487 ok(dst_xs->Mask == 4, "Got unexpected Mask %s.\n",
11488 wine_dbgstr_longlong(dst_xs->Mask));
11489 ok(dst_xs->CompactionMask == expected_compaction,
11490 "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(dst_xs->CompactionMask));
11491 check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range, 1, sizeof(dst_xs->YmmContext));
11492
11493 src_xs->Mask = 7;
11494 memset(&dst_xs->YmmContext, 0xdd, sizeof(dst_xs->YmmContext));
11495 dst_xs->CompactionMask = 0xdddddddddddddddd;
11496 dst_xs->Mask = 0xdddddddddddddddd;
11497 status = pRtlCopyContext(dst, flags, src);
11498 ok(!status || broken(!(flags & CONTEXT_NATIVE) && status == STATUS_INVALID_PARAMETER),
11499 "Got unexpected status %#lx, flags %#lx.\n", status, flags);
11500 ok(dst_xs->Mask == 0xdddddddddddddddd || broken(dst_xs->Mask == 4), "Got unexpected Mask %s, flags %#lx.\n",
11501 wine_dbgstr_longlong(dst_xs->Mask), flags);
11502 ok(dst_xs->CompactionMask == 0xdddddddddddddddd || broken(dst_xs->CompactionMask == expected_compaction),
11503 "Got unexpected CompactionMask %s, flags %#lx.\n", wine_dbgstr_longlong(dst_xs->CompactionMask), flags);
11504 check_changes_in_range((BYTE *)&dst_xs->YmmContext, single_range,
11505 dst_xs->Mask == 4, sizeof(dst_xs->YmmContext));
11506 }
11507}
11508
11509#if defined(__i386__)
11510# define IP_REG(ctx) ctx.Eip
11511#else
11512# define IP_REG(ctx) ctx.Rip
11513#endif
11514
11515static volatile int exit_ip_test;
11516static DWORD WINAPI ip_test_thread_proc( void *param )
11517{
11518 SetEvent( param );
11519 while (!exit_ip_test);
11520 return ERROR_SUCCESS;
11521}
11522
11523static void test_set_live_context(void)
11524{
11525 UINT_PTR old_ip, target;
11526 HANDLE thread, event;
11527 char *target_ptr;
11528 CONTEXT ctx;
11529 DWORD res;
11530 int i;
11531
11532 /* jmp to self at offset 0 and 4 */
11533 static const char target_code[] = {0xeb, 0xfe, 0x90, 0x90, 0xeb, 0xfe};
11534
11535 target_ptr = VirtualAlloc( NULL, 65536, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE );
11536 memcpy( target_ptr, target_code, sizeof(target_code) );
11537 target = (UINT_PTR)target_ptr;
11538
11539 event = CreateEventW( NULL, TRUE, FALSE, NULL );
11540 thread = CreateThread( NULL, 65536, ip_test_thread_proc, event, 0, NULL );
11541 ok( thread != NULL, "Failed to create thread: %lx\n", GetLastError() );
11542 res = WaitForSingleObject( event, 1000 );
11543 ok( !res, "wait returned: %ld\n", res );
11544 CloseHandle( event );
11545
11546 memset( &ctx, 0, sizeof(ctx) );
11547 ctx.ContextFlags = CONTEXT_ALL;
11548 res = GetThreadContext( thread, &ctx );
11549 ok( res, "Failed to get thread context: %lx\n", GetLastError() );
11550 old_ip = IP_REG(ctx);
11551
11552 IP_REG(ctx) = target;
11553 res = SetThreadContext( thread, &ctx );
11554 ok( res, "Failed to set thread context: %lx\n", GetLastError() );
11555
11556 for (i = 0; i < 10; i++)
11557 {
11558 IP_REG(ctx) = target;
11559 res = SetThreadContext( thread, &ctx );
11560 ok( res, "Failed to set thread context: %lx\n", GetLastError() );
11561
11562 ctx.ContextFlags = CONTEXT_ALL;
11563 res = GetThreadContext( thread, &ctx );
11564 ok( res, "Failed to get thread context: %lx\n", GetLastError() );
11565 ok( IP_REG(ctx) == target, "IP = %p, expected %p\n", (void *)IP_REG(ctx), target_ptr );
11566
11567 IP_REG(ctx) = target + 4;
11568 res = SetThreadContext( thread, &ctx );
11569 ok( res, "Failed to set thread context: %lx\n", GetLastError()) ;
11570
11571 ctx.ContextFlags = CONTEXT_ALL;
11572 res = GetThreadContext( thread, &ctx );
11573 ok( res, "Failed to get thread context: %lx\n", GetLastError() );
11574 ok( IP_REG(ctx) == target + 4, "IP = %p, expected %p\n", (void *)IP_REG(ctx), target_ptr + 4 );
11575 }
11576
11577 exit_ip_test = 1;
11578 ctx.ContextFlags = CONTEXT_ALL;
11579 IP_REG(ctx) = old_ip;
11580 res = SetThreadContext( thread, &ctx );
11581 ok( res, "Failed to restore thread context: %lx\n", GetLastError() );
11582
11583 res = WaitForSingleObject( thread, 1000 );
11584 ok( !res, "wait returned: %ld\n", res );
11585
11586 VirtualFree( target_ptr, 0, MEM_RELEASE );
11587}
11588#endif
11589
11590static void test_backtrace(void)
11591{
11592 void *buffer[1024];
11593 WCHAR name[MAX_PATH];
11594 void *module;
11595 ULONG hash, hash_expect;
11596 int i, count = RtlCaptureStackBackTrace( 0, 1024, buffer, &hash );
11597
11598 ok( count > 0, "got %u entries\n", count );
11599 for (i = hash_expect = 0; i < count; i++) hash_expect += (ULONG_PTR)buffer[i];
11600 ok( hash == hash_expect, "hash mismatch %lx / %lx\n", hash, hash_expect );
11601 pRtlPcToFileHeader( buffer[0], &module );
11602 if (is_arm64ec && module == hntdll) /* Windows arm64ec has an extra frame for the entry thunk */
11603 {
11604 ok( count > 1, "wrong count %u\n", count );
11605 pRtlPcToFileHeader( buffer[1], &module );
11606 }
11607 GetModuleFileNameW( module, name, ARRAY_SIZE(name) );
11608 ok( module == GetModuleHandleA(0), "wrong module %p %s / %p for %p\n",
11609 module, debugstr_w(name), GetModuleHandleA(0), buffer[0]);
11610
11611 if (pRtlGetCallersAddress)
11612 {
11613 void *caller, *parent;
11614
11615 caller = parent = (void *)0xdeadbeef;
11616 pRtlGetCallersAddress( &caller, &parent );
11617 ok( caller == (count > 1 ? buffer[1] : NULL) || broken(is_arm64ec), /* caller is entry thunk */
11618 "wrong caller %p / %p\n", caller, buffer[1] );
11619 ok( parent == (count > 2 ? buffer[2] : NULL), "wrong parent %p / %p\n", parent, buffer[2] );
11620 }
11621 else win_skip( "RtlGetCallersAddress not supported\n" );
11622
11623 if (count && !buffer[count - 1]) count--; /* win11 32-bit */
11624 if (count <= 1) return;
11625 pRtlPcToFileHeader( buffer[count - 1], &module );
11626 GetModuleFileNameW( module, name, ARRAY_SIZE(name) );
11627 ok( module == hntdll, "wrong module %p %s for frame %u %p\n",
11628 module, debugstr_w(name), count - 1, buffer[count - 1] );
11629}
11630
11631struct context_exception_request_thread_param
11632{
11633 LONG volatile sync;
11634 HANDLE event;
11635};
11636static volatile int *p_context_exception_request_value;
11637struct context_exception_request_thread_param *context_exception_request_param;
11638
11639static LONG CALLBACK test_context_exception_request_handler( EXCEPTION_POINTERS *info )
11640{
11641 PEXCEPTION_RECORD rec = info->ExceptionRecord;
11642 CONTEXT *c = info->ContextRecord;
11643 DWORD old_prot;
11644
11645 ok( !(c->ContextFlags & (CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE
11646 | CONTEXT_EXCEPTION_ACTIVE)), "got %#lx.\n", c->ContextFlags );
11647
11648 ok( rec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION, "got %#lx.\n", rec->ExceptionCode );
11649 VirtualProtect( (void *)p_context_exception_request_value, sizeof(*p_context_exception_request_value),
11650 PAGE_READWRITE, &old_prot );
11651
11652 WriteRelease( &context_exception_request_param->sync, 5 );
11653 while (ReadAcquire( &context_exception_request_param->sync ) != 6)
11654 ;
11655
11656 return EXCEPTION_CONTINUE_EXECUTION;
11657}
11658
11659#ifdef __i386__
11660static const BYTE call_func64_code[] =
11661{
11662 0x58, /* pop %eax */
11663 0x0e, /* push %cs */
11664 0x50, /* push %eax */
11665 0x6a, 0x33, /* push $0x33 */
11666 0xe8, 0x00, 0x00, 0x00, 0x00, /* call 1f */
11667 0x83, 0x04, 0x24, 0x05, /* 1: addl $0x5,(%esp) */
11668 0xcb, /* lret */
11669 /* in 64-bit mode: */
11670 0x4c, 0x87, 0xf4, /* xchg %r14,%rsp */
11671 0x55, /* push %rbp */
11672 0x48, 0x89, 0xe5, /* mov %rsp,%rbp */
11673 0x56, /* push %rsi */
11674 0x57, /* push %rdi */
11675 0x41, 0x8b, 0x4e, 0x10, /* mov 0x10(%r14),%ecx */
11676 0x41, 0x8b, 0x76, 0x14, /* mov 0x14(%r14),%esi */
11677 0x67, 0x8d, 0x04, 0xcd, 0, 0, 0, 0, /* lea 0x0(,%ecx,8),%eax */
11678 0x83, 0xf8, 0x20, /* cmp $0x20,%eax */
11679 0x7d, 0x05, /* jge 1f */
11680 0xb8, 0x20, 0x00, 0x00, 0x00, /* mov $0x20,%eax */
11681 0x48, 0x29, 0xc4, /* 1: sub %rax,%rsp */
11682 0x48, 0x83, 0xe4, 0xf0, /* and $~15,%rsp */
11683 0x48, 0x89, 0xe7, /* mov %rsp,%rdi */
11684 0xf3, 0x48, 0xa5, /* rep movsq */
11685 0x48, 0x8b, 0x0c, 0x24, /* mov (%rsp),%rcx */
11686 0x48, 0x8b, 0x54, 0x24, 0x08, /* mov 0x8(%rsp),%rdx */
11687 0x4c, 0x8b, 0x44, 0x24, 0x10, /* mov 0x10(%rsp),%r8 */
11688 0x4c, 0x8b, 0x4c, 0x24, 0x18, /* mov 0x18(%rsp),%r9 */
11689 0x41, 0xff, 0x56, 0x08, /* callq *0x8(%r14) */
11690 0x48, 0x8d, 0x65, 0xf0, /* lea -0x10(%rbp),%rsp */
11691 0x5f, /* pop %rdi */
11692 0x5e, /* pop %rsi */
11693 0x5d, /* pop %rbp */
11694 0x4c, 0x87, 0xf4, /* xchg %r14,%rsp */
11695 0xcb, /* lret */
11696};
11697
11698static NTSTATUS call_func64( ULONG64 func64, int nb_args, ULONG64 *args, void *code_mem )
11699{
11700 NTSTATUS (WINAPI *func)( ULONG64 func64, int nb_args, ULONG64 *args ) = code_mem;
11701
11702 memcpy( code_mem, call_func64_code, sizeof(call_func64_code) );
11703 return func( func64, nb_args, args );
11704}
11705#endif
11706
11707static DWORD WINAPI test_context_exception_request_thread( void *arg )
11708{
11709#ifdef __i386__
11710 static BYTE wait_sync_x64_code[] =
11711 {
11712 0x89, 0x11, /* mov %edx,(%rcx) */
11713 0x83, 0xc2, 0x01, /* add $0x1,%edx */
11714 0x0f, 0x1f, 0x00, /* 1: nopl (%rax) */
11715 0x8b, 0x01, /* mov (%rcx),%eax */
11716 0x39, 0xd0, /* cmp %edx,%eax */
11717 0x75, 0xfa, /* jne 1b */
11718 0xc3, /* ret */
11719 };
11720 ULONG64 args[2];
11721#endif
11722 struct context_exception_request_thread_param *p = arg;
11723 void *vectored_handler;
11724
11725 context_exception_request_param = p;
11726 vectored_handler = pRtlAddVectoredExceptionHandler( TRUE, test_context_exception_request_handler );
11727 ok( !!vectored_handler, "failed.\n" );
11728
11729 WriteRelease( &p->sync, 1 );
11730 while (ReadAcquire( &p->sync ) != 2)
11731 ;
11732
11733 WaitForSingleObject( p->event, INFINITE );
11734
11735#ifdef __i386__
11736 memcpy( (char *)code_mem + 1024, wait_sync_x64_code, sizeof(wait_sync_x64_code) );
11737 args[0] = (ULONG_PTR)&p->sync;
11738 args[1] = 3;
11739 if (is_wow64 && !old_wow64) call_func64( (ULONG64)(ULONG_PTR)code_mem + 1024, ARRAY_SIZE(args), args, code_mem );
11740#endif
11741
11742 p_context_exception_request_value = VirtualAlloc( NULL, sizeof(*p_context_exception_request_value),
11743 MEM_RESERVE | MEM_COMMIT, PAGE_READONLY );
11744 ok( !!p_context_exception_request_value, "got NULL.\n" );
11745 *p_context_exception_request_value = 1;
11746 ok( *p_context_exception_request_value == 1, "got %d.\n", *p_context_exception_request_value );
11747 VirtualFree( (void *)p_context_exception_request_value, 0, MEM_RELEASE );
11748 pRtlRemoveVectoredExceptionHandler( vectored_handler );
11749
11750#ifdef __i386__
11751 args[1] = 7;
11752 if (is_wow64 && !old_wow64) call_func64( (ULONG64)(ULONG_PTR)code_mem + 1024, ARRAY_SIZE(args), args, code_mem );
11753#endif
11754
11755 return 0;
11756}
11757
11758static void test_context_exception_request(void)
11759{
11760 struct context_exception_request_thread_param p;
11761 DWORD expected_flags;
11762 HANDLE thread;
11763 CONTEXT c;
11764 BOOL ret;
11765
11766 if (!pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler)
11767 {
11768 skip( "RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found.\n" );
11769 return;
11770 }
11771
11772 c.ContextFlags = CONTEXT_CONTROL;
11773 ret = GetThreadContext( GetCurrentThread(), &c );
11774 ok( ret, "got error %lu.\n", GetLastError() );
11775 ok( c.ContextFlags == CONTEXT_CONTROL, "got %#lx.\n", c.ContextFlags );
11776
11777 expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE;
11778
11779 c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST;
11780 ret = GetThreadContext( GetCurrentThread(), &c );
11781 ok( ret, "got error %lu.\n", GetLastError() );
11782 ok( c.ContextFlags == expected_flags || broken( c.ContextFlags == 0x10001 ) /* Win7 WoW64 */,
11783 "got %#lx.\n", c.ContextFlags );
11784 if (c.ContextFlags == 0x10001)
11785 {
11786 win_skip( "Old WoW64 behaviour, skipping tests.\n" );
11787 return;
11788 }
11789
11790 ret = DuplicateHandle( GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &thread, 0, TRUE, DUPLICATE_SAME_ACCESS );
11791 ok( ret, "got error %lu.\n", GetLastError() );
11792 c.ContextFlags = expected_flags | CONTEXT_EXCEPTION_REQUEST;
11793
11794 c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST;
11795 ret = GetThreadContext( thread, &c );
11796 ok( ret, "got error %lu.\n", GetLastError() );
11797 ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags );
11798 CloseHandle( thread );
11799
11800 c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE
11801 | CONTEXT_EXCEPTION_ACTIVE;
11802 ret = GetThreadContext( GetCurrentThread(), &c );
11803 ok( ret, "got error %lu.\n", GetLastError() );
11804 ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags );
11805
11806 p.event = CreateEventW( NULL, FALSE, FALSE, NULL );
11807 thread = CreateThread( NULL, 0, test_context_exception_request_thread, &p, CREATE_SUSPENDED, NULL );
11808 ok( !!thread, "got error %lu.\n", GetLastError() );
11809
11810 expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE;
11811
11812 c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST;
11813 ret = GetThreadContext( thread, &c );
11814 ok( ret, "got error %lu.\n", GetLastError() );
11815 ok( c.ContextFlags == expected_flags || broken( c.ContextFlags == (CONTEXT_CONTROL
11816 | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING)) /* Win7 64 */, "got %#lx.\n", c.ContextFlags );
11817
11818 p.sync = 0;
11819 ResumeThread(thread);
11820
11821 while (ReadAcquire( &p.sync ) != 1)
11822 SwitchToThread();
11823 /* thread is in user code. */
11824 SuspendThread( thread );
11825
11826 c.ContextFlags = CONTEXT_CONTROL;
11827 ret = GetThreadContext( thread, &c );
11828 ok( ret, "got error %lu.\n", GetLastError() );
11829 ok( c.ContextFlags == CONTEXT_CONTROL, "got %#lx.\n", c.ContextFlags );
11830
11831 c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE;
11832 ret = SetThreadContext( thread, &c );
11833 ok( ret, "got error %lu.\n", GetLastError() );
11834
11835 expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING;
11836
11837 c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST;
11838 ret = GetThreadContext( thread, &c );
11839 ok( ret, "got error %lu.\n", GetLastError() );
11840 ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags );
11841
11842 c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE
11843 | CONTEXT_EXCEPTION_ACTIVE;
11844 ret = GetThreadContext( thread, &c );
11845 ok( ret, "got error %lu.\n", GetLastError() );
11846 ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags );
11847
11848 ResumeThread(thread);
11849 WriteRelease( &p.sync, 2 );
11850 /* Try to make sure the thread entered WaitForSingleObject(). */
11851 Sleep(30);
11852
11853 expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE;
11854
11855 c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST;
11856 ret = GetThreadContext( thread, &c );
11857 ok( ret, "got error %lu.\n", GetLastError() );
11858 ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags );
11859
11860 c.ContextFlags = CONTEXT_CONTROL;
11861 ret = SetThreadContext( thread, &c );
11862 ok( ret, "got error %lu.\n", GetLastError() );
11863
11864 c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE
11865 | CONTEXT_EXCEPTION_ACTIVE;
11866 ret = GetThreadContext( thread, &c );
11867 ok( ret, "got error %lu.\n", GetLastError() );
11868 ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags );
11869
11870 SetEvent( p.event );
11871
11872 if (is_wow64 && !old_wow64)
11873 {
11874 while (ReadAcquire( &p.sync ) != 3)
11875 SwitchToThread();
11876 /* thread is in x64 code. */
11877
11878 expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE;
11879
11880 c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST;
11881 ret = GetThreadContext( thread, &c );
11882 ok( ret, "got error %lu.\n", GetLastError() );
11883 ok( c.ContextFlags == expected_flags, "got %#lx, expected %#lx.\n", c.ContextFlags, expected_flags );
11884
11885 WriteRelease( &p.sync, 4 );
11886 }
11887
11888 while (ReadAcquire( &p.sync ) != 5)
11889 SwitchToThread();
11890
11891#if defined(__REACTOS__) && defined(__i386__) && !defined(__GNUC__)
11892 if (is_wow64)
11893 {
11894 win_skip("Skipping on WOW64 with MSVC builds, because it makes the test crash\n");
11895 CloseHandle( thread );
11896 CloseHandle( p.event );
11897 return;
11898 }
11899#endif
11900
11901 expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING; // 0xC0010001
11902
11903 c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST;
11904 ret = GetThreadContext( thread, &c );
11905 ok( ret, "got error %lu.\n", GetLastError() );
11906#ifdef __REACTOS__
11907 if (GetNTVersion() < _WIN32_WINNT_VISTA)
11908 ok( c.ContextFlags == (expected_flags & ~CONTEXT_EXCEPTION_REPORTING), "got %#lx.\n", c.ContextFlags );
11909 else
11910#endif
11911 ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags );
11912
11913 c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_SERVICE_ACTIVE
11914 | CONTEXT_EXCEPTION_ACTIVE;
11915 ret = GetThreadContext( thread, &c );
11916 ok( ret, "got error %lu.\n", GetLastError() );
11917#ifdef __REACTOS__
11918 if (GetNTVersion() < _WIN32_WINNT_VISTA)
11919 ok( c.ContextFlags == (expected_flags | CONTEXT_SERVICE_ACTIVE | CONTEXT_EXCEPTION_ACTIVE), "got %#lx, expected %#lx.\n", c.ContextFlags, expected_flags );
11920 else
11921#endif
11922 ok( c.ContextFlags == expected_flags, "got %#lx.\n", c.ContextFlags );
11923
11924 WriteRelease( &p.sync, 6 );
11925
11926 if (is_wow64 && !old_wow64)
11927 {
11928 while (ReadAcquire( &p.sync ) != 7)
11929 SwitchToThread();
11930 /* thread is in x64 code. */
11931
11932 expected_flags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST | CONTEXT_EXCEPTION_REPORTING | CONTEXT_EXCEPTION_ACTIVE;
11933
11934 c.ContextFlags = CONTEXT_CONTROL | CONTEXT_EXCEPTION_REQUEST;
11935 ret = GetThreadContext( thread, &c );
11936 ok( ret, "got error %lu.\n", GetLastError() );
11937 ok( c.ContextFlags == expected_flags, "got %#lx, expected %#lx.\n", c.ContextFlags, expected_flags );
11938
11939 WriteRelease( &p.sync, 8 );
11940 }
11941
11942 WaitForSingleObject( thread, INFINITE );
11943 CloseHandle( thread );
11944 CloseHandle( p.event );
11945}
11946
11947START_TEST(exception)
11948{
11949 HMODULE hkernel32 = GetModuleHandleA("kernel32.dll");
11950 hntdll = GetModuleHandleA("ntdll.dll");
11951
11952 my_argc = winetest_get_mainargs( &my_argv );
11953
11954 if (my_argc >= 3 && !strcmp(my_argv[2], "suspend_process"))
11955 {
11956 suspend_process_proc();
11957 return;
11958 }
11959
11960 code_mem = VirtualAlloc(NULL, 65536, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
11961 if(!code_mem) {
11962 trace("VirtualAlloc failed\n");
11963 return;
11964 }
11965
11966#define X(f) p##f = (void*)GetProcAddress(hntdll, #f)
11967 X(NtGetContextThread);
11968 X(NtSetContextThread);
11969 X(NtQueueApcThread);
11970 X(NtContinueEx);
11971 X(NtReadVirtualMemory);
11972 X(NtClose);
11973 X(RtlUnwind);
11974 X(RtlRaiseException);
11975 X(RtlCaptureContext);
11976 X(NtTerminateProcess);
11977 X(RtlAddVectoredExceptionHandler);
11978 X(RtlRemoveVectoredExceptionHandler);
11979 X(RtlAddVectoredContinueHandler);
11980 X(RtlRemoveVectoredContinueHandler);
11981 X(RtlSetUnhandledExceptionFilter);
11982 X(NtQueryInformationThread);
11983 X(NtSetInformationProcess);
11984 X(NtSuspendProcess);
11985 X(NtRaiseException);
11986 X(NtResumeProcess);
11987 X(RtlGetUnloadEventTrace);
11988 X(RtlGetUnloadEventTraceEx);
11989 X(RtlGetEnabledExtendedFeatures);
11990 X(RtlGetExtendedContextLength);
11991 X(RtlGetExtendedContextLength2);
11992 X(RtlInitializeExtendedContext);
11993 X(RtlInitializeExtendedContext2);
11994 X(RtlLocateExtendedFeature);
11995 X(RtlLocateLegacyContext);
11996 X(RtlSetExtendedFeaturesMask);
11997 X(RtlGetExtendedFeaturesMask);
11998 X(RtlPcToFileHeader);
11999 X(RtlGetCallersAddress);
12000 X(RtlCopyContext);
12001 X(RtlCopyExtendedContext);
12002 X(KiUserApcDispatcher);
12003 X(KiUserCallbackDispatcher);
12004 X(KiUserExceptionDispatcher);
12005#ifndef __i386__
12006 X(RtlRestoreContext);
12007 X(RtlUnwindEx);
12008 X(RtlAddFunctionTable);
12009 X(RtlDeleteFunctionTable);
12010 X(RtlGetNativeSystemInformation);
12011#endif
12012
12013#ifdef __x86_64__
12014 if (pRtlGetNativeSystemInformation)
12015 {
12016 SYSTEM_CPU_INFORMATION info;
12017 ULONG len;
12018 if (!pRtlGetNativeSystemInformation( SystemCpuInformation, &info, sizeof(info), &len ))
12019 is_arm64ec = (info.ProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM64);
12020 }
12021#endif
12022#undef X
12023
12024#define X(f) p##f = (void*)GetProcAddress(hkernel32, #f)
12025 X(IsWow64Process);
12026 if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE;
12027 if (is_wow64)
12028 {
12029 TEB64 *teb64 = ULongToPtr( NtCurrentTeb()->GdiBatchCount );
12030
12031 if (teb64)
12032 {
12033 PEB64 *peb64 = ULongToPtr(teb64->Peb);
12034 old_wow64 = !peb64->LdrData;
12035 }
12036 }
12037
12038 X(InitializeContext);
12039 X(InitializeContext2);
12040 X(LocateXStateFeature);
12041 X(SetXStateFeaturesMask);
12042 X(GetXStateFeaturesMask);
12043 X(WaitForDebugEventEx);
12044#undef X
12045
12046 if (pRtlAddVectoredExceptionHandler && pRtlRemoveVectoredExceptionHandler)
12047 have_vectored_api = TRUE;
12048 else
12049 skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n");
12050
12051 my_argc = winetest_get_mainargs( &my_argv );
12052 if (my_argc >= 4)
12053 {
12054 void *addr;
12055
12056 if (strcmp(my_argv[2], "fastfail") == 0)
12057 {
12058 __fastfail(strtoul(my_argv[3], NULL, 0));
12059 return;
12060 }
12061
12062 sscanf( my_argv[3], "%p", &addr );
12063
12064 if (addr != &test_stage)
12065 {
12066 skip( "child process not mapped at same address (%p/%p)\n", &test_stage, addr);
12067 return;
12068 }
12069
12070 /* child must be run under a debugger */
12071 if (!NtCurrentTeb()->Peb->BeingDebugged)
12072 {
12073 ok(FALSE, "child process not being debugged?\n");
12074 return;
12075 }
12076
12077 if (pRtlRaiseException)
12078 {
12079 test_stage = STAGE_RTLRAISE_NOT_HANDLED;
12080 run_rtlraiseexception_test(0x12345);
12081 run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
12082 run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
12083 test_stage = STAGE_RTLRAISE_HANDLE_LAST_CHANCE;
12084 run_rtlraiseexception_test(0x12345);
12085 run_rtlraiseexception_test(EXCEPTION_BREAKPOINT);
12086 run_rtlraiseexception_test(EXCEPTION_INVALID_HANDLE);
12087 }
12088 else skip( "RtlRaiseException not found\n" );
12089
12090 test_stage = STAGE_OUTPUTDEBUGSTRINGA_CONTINUE;
12091
12092 test_outputdebugstring(FALSE, 0, FALSE, 0, 0);
12093 test_stage = STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED;
12094 test_outputdebugstring(FALSE, 2, TRUE, 0, 0); /* is 2 a Windows bug? */
12095 test_stage = STAGE_OUTPUTDEBUGSTRINGW_CONTINUE;
12096 /* depending on value passed DebugContinue we can get the unicode exception or not */
12097 test_outputdebugstring(TRUE, 0, FALSE, 0, 1);
12098 test_stage = STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED;
12099 /* depending on value passed DebugContinue we can get the unicode exception or not */
12100 test_outputdebugstring(TRUE, 2, TRUE, 0, 1); /* is 2 a Windows bug? */
12101 test_stage = STAGE_RIPEVENT_CONTINUE;
12102 test_ripevent(0);
12103 test_stage = STAGE_RIPEVENT_NOT_HANDLED;
12104 test_ripevent(1);
12105 test_stage = STAGE_SERVICE_CONTINUE;
12106 test_debug_service(0);
12107 test_stage = STAGE_SERVICE_NOT_HANDLED;
12108 test_debug_service(1);
12109 test_stage = STAGE_BREAKPOINT_CONTINUE;
12110 test_breakpoint(0);
12111 test_stage = STAGE_BREAKPOINT_NOT_HANDLED;
12112 test_breakpoint(1);
12113 test_stage = STAGE_EXCEPTION_INVHANDLE_CONTINUE;
12114 test_closehandle(0, (HANDLE)0xdeadbeef);
12115 test_closehandle(0, (HANDLE)0x7fffffff);
12116 test_stage = STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED;
12117 test_closehandle(1, (HANDLE)0xdeadbeef);
12118 test_closehandle(1, (HANDLE)~(ULONG_PTR)6);
12119 test_stage = STAGE_NO_EXCEPTION_INVHANDLE_NOT_HANDLED; /* special cases */
12120 test_closehandle(0, 0);
12121 test_closehandle(0, INVALID_HANDLE_VALUE);
12122 test_closehandle(0, GetCurrentProcess());
12123 test_closehandle(0, GetCurrentThread());
12124 test_closehandle(0, (HANDLE)~(ULONG_PTR)2);
12125 test_closehandle(0, GetCurrentProcessToken());
12126 test_closehandle(0, GetCurrentThreadToken());
12127 test_closehandle(0, GetCurrentThreadEffectiveToken());
12128#if defined(__i386__) || defined(__x86_64__)
12129 test_stage = STAGE_XSTATE;
12130 test_debuggee_xstate();
12131 test_stage = STAGE_XSTATE_LEGACY_SSE;
12132 test_debuggee_xstate();
12133 test_stage = STAGE_SEGMENTS;
12134 test_debuggee_segments();
12135#endif
12136
12137 /* rest of tests only run in parent */
12138 return;
12139 }
12140
12141#ifdef __i386__
12142
12143 test_unwind();
12144 test_exceptions();
12145 test_debug_registers();
12146 test_debug_service(1);
12147 test_simd_exceptions();
12148 test_fpu_exceptions();
12149 test_dpe_exceptions();
12150 test_prot_fault();
12151 test_extended_context();
12152 test_copy_context();
12153 test_set_live_context();
12154 test_hwbpt_in_syscall();
12155 test_instrumentation_callback();
12156
12157#elif defined(__x86_64__)
12158
12159#define X(f) p##f = (void*)GetProcAddress(hntdll, #f)
12160 X(__C_specific_handler);
12161 X(RtlWow64GetThreadContext);
12162 X(RtlWow64SetThreadContext);
12163 X(RtlWow64GetCpuAreaInfo);
12164#undef X
12165
12166 test_exceptions();
12167 test_debug_registers();
12168 test_debug_registers_wow64();
12169 test_debug_service(1);
12170 test_simd_exceptions();
12171 test_continue();
12172 test___C_specific_handler();
12173 test_restore_context();
12174 test_prot_fault();
12175 test_dpe_exceptions();
12176 test_wow64_context();
12177 test_nested_exception();
12178 test_collided_unwind();
12179 test_extended_context();
12180 test_copy_context();
12181 test_set_live_context();
12182 test_unwind_from_apc();
12183 test_syscall_clobbered_regs();
12184 test_raiseexception_regs();
12185 test_hwbpt_in_syscall();
12186 test_instrumentation_callback();
12187 test_direct_syscalls();
12188
12189#elif defined(__aarch64__)
12190
12191 test_continue();
12192 test_brk();
12193 test_nested_exception();
12194 test_collided_unwind();
12195 test_restore_context();
12196 test_mrs_currentel();
12197
12198#elif defined(__arm__)
12199
12200 test_nested_exception();
12201 test_collided_unwind();
12202 test_restore_context();
12203
12204#endif
12205
12206 test_KiUserExceptionDispatcher();
12207 test_KiUserApcDispatcher();
12208 test_KiUserCallbackDispatcher();
12209 test_rtlraiseexception();
12210 test_debugger(DBG_EXCEPTION_HANDLED, FALSE);
12211 test_debugger(DBG_CONTINUE, FALSE);
12212 test_debugger(DBG_EXCEPTION_HANDLED, TRUE);
12213 test_debugger(DBG_CONTINUE, TRUE);
12214 test_thread_context();
12215 test_outputdebugstring(FALSE, 1, FALSE, 0, 0);
12216 if (pWaitForDebugEventEx)
12217 {
12218 test_outputdebugstring(TRUE, 1, FALSE, 1, 1);
12219 test_outputdebugstring_newmodel();
12220 }
12221 else
12222 skip("Unsupported new unicode debug string model\n");
12223
12224 test_ripevent(1);
12225 test_fastfail();
12226 test_breakpoint(1);
12227 test_closehandle(0, (HANDLE)0xdeadbeef);
12228 /* Call of Duty WWII writes to BeingDebugged then closes an invalid handle,
12229 * crashing the game if an exception is raised. */
12230 NtCurrentTeb()->Peb->BeingDebugged = 0x98;
12231 test_closehandle(0, (HANDLE)0xdeadbeef);
12232 NtCurrentTeb()->Peb->BeingDebugged = 0;
12233
12234 test_user_apc();
12235 test_user_callback();
12236 test_vectored_continue_handler();
12237 test_suspend_thread();
12238 test_suspend_process();
12239 test_unload_trace();
12240 test_backtrace();
12241 test_context_exception_request();
12242 VirtualFree(code_mem, 0, MEM_RELEASE);
12243}
#define STATUS_PRIVILEGE_NOT_HELD
Definition: DriverTester.h:9
std::map< E_MODULE, HMODULE > mod
Definition: LocaleTests.cpp:68
#define expect(EXPECTED, GOT)
Definition: SystemMenu.c:483
#define VOID
Definition: acefi.h:82
unsigned char BOOLEAN
Definition: actypes.h:127
COMPILER_DEPENDENT_UINT64 UINT64
Definition: actypes.h:131
#define stat
Definition: acwin.h:99
static void startup(void)
NTSTATUS NTAPI NtCallbackReturn(_In_ PVOID Result, _In_ ULONG ResultLength, _In_ NTSTATUS CallbackStatus)
Definition: usercall.c:332
@ R15
Definition: amd64_sup.c:13
@ R13
Definition: amd64_sup.c:13
@ R12
Definition: amd64_sup.c:13
@ R9
Definition: amd64_sup.c:13
@ R14
Definition: amd64_sup.c:13
@ R8
Definition: amd64_sup.c:13
@ R11
Definition: amd64_sup.c:13
@ R10
Definition: amd64_sup.c:13
#define GetNTVersion()
Definition: apitest.h:17
#define RtlUnwind
Definition: longjmp.c:9
#define InterlockedIncrement
Definition: armddk.h:53
#define trace
Definition: atltest.h:70
#define ok(value,...)
Definition: atltest.h:57
#define skip(...)
Definition: atltest.h:64
#define broken(x)
Definition: atltest.h:178
#define ok_(x1, x2)
Definition: atltest.h:61
LONG NTSTATUS
Definition: precomp.h:26
#define ARRAY_SIZE(A)
Definition: main.h:20
void dispatch(HANDLE hStopEvent)
Definition: dispatch.c:70
static HANDLE thread
Definition: service.c:33
#define HandleToLong(h)
Definition: basetsd.h:74
#define ULongToPtr(ul)
Definition: basetsd.h:86
#define except(x)
Definition: btrfs_drv.h:136
_In_ DWORD feature_id
union _UNWIND_CODE UNWIND_CODE
struct _UNWIND_INFO UNWIND_INFO
#define STATUS_ILLEGAL_INSTRUCTION
Definition: d3dkmdt.h:41
#define STATUS_INVALID_HANDLE
Definition: d3dkmdt.h:40
#define STATUS_NOT_SUPPORTED
Definition: d3dkmdt.h:48
#define WAIT_TIMEOUT
Definition: dderror.h:14
#define ERROR_MORE_DATA
Definition: dderror.h:13
#define ERROR_INSUFFICIENT_BUFFER
Definition: dderror.h:10
size_t const element_size
Definition: debug_heap.cpp:510
#define free
Definition: debug_ros.c:5
#define malloc
Definition: debug_ros.c:4
PIMAGE_NT_HEADERS nt
Definition: delayimp.cpp:445
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define NTSTATUS
Definition: precomp.h:19
static WCHAR unknown[MAX_STRING_RESOURCE_LEN]
Definition: object.c:1605
#define CDECL
Definition: compat.h:29
#define CloseHandle
Definition: compat.h:739
#define ReadProcessMemory(a, b, c, d, e)
Definition: compat.h:758
@ ExceptionContinueSearch
Definition: compat.h:91
@ ExceptionContinueExecution
Definition: compat.h:90
#define ERROR_INVALID_PARAMETER
Definition: compat.h:101
#define EXCEPTION_MAXIMUM_PARAMETERS
Definition: compat.h:206
#define SetLastError(x)
Definition: compat.h:752
struct _EXCEPTION_RECORD EXCEPTION_RECORD
#define GetProcAddress(x, y)
Definition: compat.h:753
#define INVALID_HANDLE_VALUE
Definition: compat.h:731
@ ThreadBasicInformation
Definition: compat.h:935
@ ThreadZeroTlsCell
Definition: compat.h:945
#define FreeLibrary(x)
Definition: compat.h:748
static __inline const char * wine_dbgstr_longlong(ULONGLONG ll)
Definition: compat.h:49
#define GetCurrentProcess()
Definition: compat.h:759
enum _EXCEPTION_DISPOSITION EXCEPTION_DISPOSITION
#define WOW64_CONTEXT_ALL
Definition: compat.h:225
#define ERROR_NOT_SUPPORTED
Definition: compat.h:100
#define RtlImageNtHeader
Definition: compat.h:806
#define MAX_PATH
Definition: compat.h:34
#define ERROR_INVALID_HANDLE
Definition: compat.h:98
#define CALLBACK
Definition: compat.h:35
static const WCHAR version[]
Definition: asmname.c:66
BOOL WINAPI ContinueDebugEvent(IN DWORD dwProcessId, IN DWORD dwThreadId, IN DWORD dwContinueStatus)
Definition: debugger.c:413
BOOL WINAPI WaitForDebugEvent(IN LPDEBUG_EVENT lpDebugEvent, IN DWORD dwMilliseconds)
Definition: debugger.c:590
PPEB Peb
Definition: dllmain.c:27
VOID WINAPI RaiseException(_In_ DWORD dwExceptionCode, _In_ DWORD dwExceptionFlags, _In_ DWORD nNumberOfArguments, _In_opt_ const ULONG_PTR *lpArguments)
Definition: except.c:700
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI DECLSPEC_HOTPATCH SetUnhandledExceptionFilter(IN LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
Definition: except.c:790
HMODULE WINAPI DECLSPEC_HOTPATCH GetModuleHandleA(LPCSTR lpModuleName)
Definition: loader.c:812
HINSTANCE WINAPI DECLSPEC_HOTPATCH LoadLibraryA(LPCSTR lpLibFileName)
Definition: loader.c:111
BOOL WINAPI FlushInstructionCache(IN HANDLE hProcess, IN LPCVOID lpBaseAddress, IN SIZE_T nSize)
Definition: proc.c:1307
BOOL WINAPI TerminateProcess(IN HANDLE hProcess, IN UINT uExitCode)
Definition: proc.c:1376
DWORD WINAPI ResumeThread(IN HANDLE hThread)
Definition: thread.c:567
BOOL WINAPI SetThreadContext(IN HANDLE hThread, IN CONST CONTEXT *lpContext)
Definition: thread.c:521
DWORD WINAPI SuspendThread(IN HANDLE hThread)
Definition: thread.c:642
HANDLE WINAPI DECLSPEC_HOTPATCH CreateThread(IN LPSECURITY_ATTRIBUTES lpThreadAttributes, IN DWORD dwStackSize, IN LPTHREAD_START_ROUTINE lpStartAddress, IN LPVOID lpParameter, IN DWORD dwCreationFlags, OUT LPDWORD lpThreadId)
Definition: thread.c:137
BOOL WINAPI TlsFree(IN DWORD Index)
Definition: thread.c:1166
BOOL WINAPI GetThreadContext(IN HANDLE hThread, OUT LPCONTEXT lpContext)
Definition: thread.c:501
int WINAPI lstrcmpW(LPCWSTR str1, LPCWSTR str2)
Definition: locale.c:4152
BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessA(const char *app_name, char *cmd_line, SECURITY_ATTRIBUTES *process_attr, SECURITY_ATTRIBUTES *thread_attr, BOOL inherit, DWORD flags, void *env, const char *cur_dir, STARTUPINFOA *startup_info, PROCESS_INFORMATION *info)
Definition: process.c:686
DWORD WINAPI DECLSPEC_HOTPATCH TlsAlloc(void)
Definition: thread.c:657
UINT(* handler)(MSIPACKAGE *)
Definition: action.c:7512
void CDECL unexpected(void)
Definition: cpp.c:711
int CDECL fflush(FILE *file)
Definition: file.c:1182
#define DECLSPEC_ALIGN(x)
Definition: corecrt.h:141
#define __cdecl
Definition: corecrt.h:121
#define stdout
_ACRTIMP int __cdecl wcscmp(const wchar_t *, const wchar_t *)
Definition: wcs.c:1972
_ACRTIMP int __cdecl memcmp(const void *, const void *, size_t)
Definition: string.c:2802
#define setjmp(buf)
Definition: setjmp.h:175
#define _JBTYPE
Definition: setjmp.h:138
_ACRTIMP char *__cdecl strstr(const char *, const char *)
Definition: string.c:3415
_ACRTIMP int __cdecl strcmp(const char *, const char *)
Definition: string.c:3319
_ACRTIMP int __cdecl strncmp(const char *, const char *, size_t)
Definition: string.c:3330
unsigned int(__cdecl typeof(jpeg_read_scanlines))(struct jpeg_decompress_struct *
Definition: typeof.h:31
return ret
Definition: mutex.c:146
#define L(x)
Definition: resources.c:13
switch(r->id)
Definition: btrfs.c:3046
#define INFINITE
Definition: serial.h:102
#define ULONG_PTR
Definition: config.h:101
int align(int length, int align)
Definition: dsound8.c:36
unsigned short WORD
Definition: ntddk_ex.h:93
unsigned int BOOL
Definition: ntddk_ex.h:94
unsigned long DWORD
Definition: ntddk_ex.h:95
enum _SYSTEM_INFORMATION_CLASS SYSTEM_INFORMATION_CLASS
#define STATUS_ACCESS_VIOLATION
unsigned int Mask
Definition: fpcontrol.c:82
#define printf
Definition: freeldr.h:103
static char * path_name(DOS_FILE *file)
Definition: check.c:208
for(i=0;i< ARRAY_SIZE(offsets);i++)
void(* func_ptr)(void)
Definition: gccmain.c:11
GLint GLint GLint GLint GLint x
Definition: gl.h:1548
GLuint GLuint GLsizei count
Definition: gl.h:1545
GLdouble s
Definition: gl.h:2039
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: gl.h:1950
GLdouble GLdouble GLdouble r
Definition: gl.h:2055
GLenum func
Definition: glext.h:6028
struct _cl_event * event
Definition: glext.h:7739
GLuint res
Definition: glext.h:9613
GLuint address
Definition: glext.h:9393
GLuint buffer
Definition: glext.h:5915
GLsizeiptr size
Definition: glext.h:5919
GLintptr offset
Definition: glext.h:5920
GLuint GLuint GLuint GLuint arg1
Definition: glext.h:9513
const GLubyte * c
Definition: glext.h:8905
GLuint GLuint GLuint GLuint GLuint GLuint GLuint GLuint GLuint GLuint arg3
Definition: glext.h:9515
GLenum GLint GLuint mask
Definition: glext.h:6028
GLuint GLuint GLuint GLuint GLuint GLuint GLuint arg2
Definition: glext.h:9514
GLenum const GLfloat * params
Definition: glext.h:5645
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: glext.h:7751
GLenum GLenum dst
Definition: glext.h:6340
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition: glext.h:7005
GLbitfield flags
Definition: glext.h:7161
GLuint GLsizei GLsizei * length
Definition: glext.h:6040
const GLint * first
Definition: glext.h:5794
GLenum const GLvoid * addr
Definition: glext.h:9621
GLuint GLfloat * val
Definition: glext.h:7180
GLuint GLint GLboolean GLint GLenum access
Definition: glext.h:7866
GLuint64EXT * result
Definition: glext.h:11304
GLfloat GLfloat p
Definition: glext.h:8902
GLenum GLsizei len
Definition: glext.h:6722
GLuint id
Definition: glext.h:5910
GLfloat GLfloat GLfloat GLfloat h
Definition: glext.h:7723
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean flag
Definition: glfuncs.h:52
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble * u
Definition: glfuncs.h:240
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint GLint GLint j
Definition: glfuncs.h:250
#define fs
Definition: i386-dis.c:444
#define es
Definition: i386-dis.c:440
#define ss
Definition: i386-dis.c:441
#define ds
Definition: i386-dis.c:443
#define cs
Definition: i386-dis.c:442
#define gs
Definition: i386-dis.c:445
void WINAPI SHIM_OBJ_NAME() OutputDebugStringA(LPCSTR lpOutputString)
Definition: ignoredbgout.c:18
void WINAPI SHIM_OBJ_NAME() OutputDebugStringW(LPCWSTR lpOutputString)
Definition: ignoredbgout.c:23
#define EXCEPTION_EXECUTE_HANDLER
Definition: excpt.h:90
#define exception_code
Definition: excpt.h:84
#define EXCEPTION_CONTINUE_SEARCH
Definition: excpt.h:91
#define EXCEPTION_CONTINUE_EXECUTION
Definition: excpt.h:92
#define exception_info
Definition: excpt.h:86
static int expected_count(int *sink)
__INTRIN_INLINE void __cpuidex(int CPUInfo[4], int InfoType, int ECXValue)
Definition: intrin_x86.h:1680
#define NtCurrentTeb
uint32_t entry
Definition: isohybrid.c:63
ULONG Sp
Definition: kdb_expr.c:99
#define d
Definition: ke_i.h:81
#define e
Definition: ke_i.h:82
#define f
Definition: ke_i.h:83
#define c
Definition: ke_i.h:80
if(dx< 0)
Definition: linetemp.h:194
const char * winetest_platform
#define win_skip
Definition: minitest.h:67
#define todo_wine_if(is_todo)
Definition: minitest.h:81
void __cdecl void __cdecl void __cdecl void __cdecl void __cdecl void winetest_pop_context(void)
void __cdecl void __cdecl void __cdecl void __cdecl void __cdecl winetest_push_context(const char *fmt,...) __WINE_PRINTF_ATTR(1
Definition: test.h:537
#define todo_wine
Definition: minitest.h:80
#define EXCEPTION_SINGLE_STEP
Definition: minwinbase.h:47
#define RIP_EVENT
Definition: minwinbase.h:41
#define ZeroMemory
Definition: minwinbase.h:31
#define EXCEPTION_FLT_STACK_CHECK
Definition: minwinbase.h:54
#define CREATE_PROCESS_DEBUG_EVENT
Definition: minwinbase.h:35
#define EXCEPTION_INT_DIVIDE_BY_ZERO
Definition: minwinbase.h:56
#define EXIT_PROCESS_DEBUG_EVENT
Definition: minwinbase.h:37
#define EXCEPTION_INVALID_HANDLE
Definition: minwinbase.h:65
#define OUTPUT_DEBUG_STRING_EVENT
Definition: minwinbase.h:40
#define EXCEPTION_DEBUG_EVENT
Definition: minwinbase.h:33
#define EXCEPTION_ILLEGAL_INSTRUCTION
Definition: minwinbase.h:60
#define EXCEPTION_ACCESS_VIOLATION
Definition: minwinbase.h:44
#define EXCEPTION_BREAKPOINT
Definition: minwinbase.h:46
#define EXCEPTION_FLT_INVALID_OPERATION
Definition: minwinbase.h:52
#define EXCEPTION_FLT_DIVIDE_BY_ZERO
Definition: minwinbase.h:50
#define memcpy(s1, s2, n)
Definition: mkisofs.h:878
unsigned __int64 ULONG64
Definition: imports.h:198
static PVOID ptr
Definition: dispmode.c:27
static struct test_info tests[]
#define sprintf
Definition: sprintf.c:45
static NTSTATUS *static PWSTR CURDIR *static HMODULE hmod
Definition: security.c:134
#define X2(f, ord)
BOOL expected
Definition: store.c:2000
static const BYTE call_debug_service_code[]
Definition: debugger.c:1594
static HMODULE ntdll
Definition: debugger.c:59
static void test_debugger(const char *argv0)
Definition: debugger.c:1645
static PROCESS_INFORMATION pi
Definition: debugger.c:2303
static SYSTEM_INFO si
Definition: virtual.c:39
static const WCHAR sp[]
Definition: suminfo.c:287
static const DWORD padding[]
Definition: mciwnd.c:89
static DWORD outputdebugstring_exceptions_unicode
Definition: exception.c:8862
static HMODULE hntdll
Definition: exception.c:52
static char ** my_argv
Definition: exception.c:187
static ULONG * length
Definition: exception.c:69
static ULONG context_flags
Definition: exception.c:71
static const char * suspend_process_event2_name
Definition: exception.c:9612
static void CALLBACK apc_func(ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3)
Definition: exception.c:194
static DWORD outputdebugstring_exceptions_ansi
Definition: exception.c:8861
static void * pKiUserCallbackDispatcher
Definition: exception.c:108
static void test_suspend_process(void)
Definition: exception.c:9656
static void * code_mem
Definition: exception.c:51
static void test_unload_trace(void)
Definition: exception.c:9743
static BOOL have_vectored_api
Definition: exception.c:191
static void suspend_process_proc(void)
Definition: exception.c:9619
struct _RTL_UNLOAD_EVENT_TRACE * PRTL_UNLOAD_EVENT_TRACE
static ULONG
Definition: exception.c:85
static void test_outputdebugstring(BOOL unicode, DWORD numexc_ansi, BOOL todo_ansi, DWORD numexc_unicode_low, DWORD numexc_unicode_high)
Definition: exception.c:8896
static ULONG CONTEXT_EX ** context_ex
Definition: exception.c:71
static void *WINAPI * pRtlLocateLegacyContext(CONTEXT_EX *context_ex, ULONG *length)
static void **static CONTEXT * context
Definition: exception.c:82
static void test_closehandle(DWORD numexc, HANDLE handle)
Definition: exception.c:9336
static UINT apc_count
Definition: exception.c:190
static LONG CALLBACK outputdebugstring_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
Definition: exception.c:8864
static RTL_UNLOAD_EVENT_TRACE *WINAPI * pRtlGetUnloadEventTrace(void)
static BOOL is_arm64ec
Definition: exception.c:53
static BOOL is_wow64
Definition: exception.c:188
static const void void SIZE_T *static LONG exit_code
Definition: exception.c:84
static DWORD CONTEXT * src
Definition: exception.c:74
static PROCESSINFOCLASS
Definition: exception.c:86
static const char * suspend_process_event_name
Definition: exception.c:9611
static void *WINAPI * pRtlLocateExtendedFeature(CONTEXT_EX *context_ex, ULONG feature_id, ULONG *length)
static DWORD outputdebugstring_newmodel_return
Definition: exception.c:8930
static ULONG64 feature_mask
Definition: exception.c:78
static LONG CALLBACK ripevent_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
Definition: exception.c:9015
static BOOL old_wow64
Definition: exception.c:189
static ULONG ** element_count
Definition: exception.c:124
static void *WINAPI * pLocateXStateFeature(CONTEXT *context, DWORD feature_id, DWORD *length)
static EXCEPTION_RECORD *static void ULONG *static void * pKiUserApcDispatcher
Definition: exception.c:107
static int my_argc
Definition: exception.c:186
static DWORD WINAPI suspend_thread_test(void *arg)
Definition: exception.c:9497
static void test_ripevent(DWORD numexc)
Definition: exception.c:9031
static CONTEXT *static CONTEXT *static PNTAPCFUNC func
Definition: exception.c:57
static void *WINAPI * pRtlPcToFileHeader(PVOID pc, PVOID *address)
static PBOOL
Definition: exception.c:87
static ULONG ULONG64 compaction_mask
Definition: exception.c:70
static BOOL is_magic_handle(HANDLE handle)
Definition: exception.c:9331
static PEXCEPTION_RECORD
Definition: exception.c:61
static DWORD outputdebugstring_exceptions_newmodel_order
Definition: exception.c:8929
static DWORD
Definition: exception.c:98
static void test_fastfail(void)
Definition: exception.c:9124
static PVOID
Definition: exception.c:61
static DWORD ripevent_exceptions
Definition: exception.c:9013
static ULONG void ** event_trace
Definition: exception.c:124
static const void void SIZE_T
Definition: exception.c:83
static void test_vectored_continue_handler(void)
Definition: exception.c:9374
static VOID EXCEPTION_RECORD VOID CONTEXT UNWIND_HISTORY_TABLE *static DWORD64
Definition: exception.c:101
static LONG CALLBACK invalid_handle_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
Definition: exception.c:9319
static void test_suspend_count(HANDLE hthread, ULONG expected_count, int line)
Definition: exception.c:9504
static DWORD breakpoint_exceptions
Definition: exception.c:9148
static void subtest_fastfail(unsigned int code)
Definition: exception.c:9061
static void test_suspend_thread(void)
Definition: exception.c:9526
static CONTEXT *static CONTEXT *static PNTAPCFUNC ULONG_PTR ULONG_PTR ULONG_PTR arg3
Definition: exception.c:58
static void test_user_callback(void)
Definition: exception.c:9491
static DWORD WINAPI dummy_thread_proc(void *arg)
Definition: exception.c:9614
static DWORD invalid_handle_exceptions
Definition: exception.c:9317
static void **static CONTEXT BOOL first_chance
Definition: exception.c:82
static void test_outputdebugstring_newmodel(void)
Definition: exception.c:8965
static enum debugger_stages test_stage
Definition: exception.c:192
debugger_stages
Definition: exception.c:165
@ STAGE_SERVICE_NOT_HANDLED
Definition: exception.c:175
@ STAGE_OUTPUTDEBUGSTRINGA_CONTINUE
Definition: exception.c:168
@ STAGE_OUTPUTDEBUGSTRINGW_CONTINUE
Definition: exception.c:170
@ STAGE_EXCEPTION_INVHANDLE_NOT_HANDLED
Definition: exception.c:179
@ STAGE_RTLRAISE_NOT_HANDLED
Definition: exception.c:166
@ STAGE_SEGMENTS
Definition: exception.c:183
@ STAGE_NO_EXCEPTION_INVHANDLE_NOT_HANDLED
Definition: exception.c:180
@ STAGE_BREAKPOINT_CONTINUE
Definition: exception.c:176
@ STAGE_RTLRAISE_HANDLE_LAST_CHANCE
Definition: exception.c:167
@ STAGE_EXCEPTION_INVHANDLE_CONTINUE
Definition: exception.c:178
@ STAGE_XSTATE
Definition: exception.c:181
@ STAGE_BREAKPOINT_NOT_HANDLED
Definition: exception.c:177
@ STAGE_XSTATE_LEGACY_SSE
Definition: exception.c:182
@ STAGE_RIPEVENT_CONTINUE
Definition: exception.c:172
@ STAGE_SERVICE_CONTINUE
Definition: exception.c:174
@ STAGE_RIPEVENT_NOT_HANDLED
Definition: exception.c:173
@ STAGE_OUTPUTDEBUGSTRINGA_NOT_HANDLED
Definition: exception.c:169
@ STAGE_OUTPUTDEBUGSTRINGW_NOT_HANDLED
Definition: exception.c:171
static CONTEXT *static CONTEXT *static PNTAPCFUNC ULONG_PTR ULONG_PTR arg2
Definition: exception.c:58
struct _RTL_UNLOAD_EVENT_TRACE RTL_UNLOAD_EVENT_TRACE
static THREADINFOCLASS
Definition: exception.c:85
static LONG CALLBACK outputdebugstring_new_model_vectored_handler(EXCEPTION_POINTERS *ExceptionInfo)
Definition: exception.c:8932
#define TEST_SUSPEND_COUNT(thread, count)
static CONTEXT *static CONTEXT *static PNTAPCFUNC ULONG_PTR arg1
Definition: exception.c:58
static void test_user_apc(void)
Definition: exception.c:9411
static PULONG
Definition: exception.c:85
static void * pKiUserExceptionDispatcher
Definition: exception.c:109
static LONG CALLBACK breakpoint_handler(EXCEPTION_POINTERS *ExceptionInfo)
Definition: exception.c:9150
static void test_breakpoint(DWORD numexc)
Definition: exception.c:9203
#define RTL_UNLOAD_EVENT_TRACE_NUMBER
Definition: exception.c:111
static int got_exception
Definition: virtual.c:2720
static DWORD unk1
Definition: cursoricon.c:1804
#define argv
Definition: mplay32.c:18
unsigned __int3264 UINT_PTR
Definition: mstsclib_h.h:274
unsigned int UINT
Definition: ndis.h:50
#define MEM_EXECUTE_OPTION_DISABLE
Definition: mmtypes.h:73
#define MEM_EXECUTE_OPTION_PERMANENT
Definition: mmtypes.h:76
#define MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION
Definition: mmtypes.h:75
#define MEM_EXECUTE_OPTION_ENABLE
Definition: mmtypes.h:74
NTSYSAPI VOID NTAPI RtlCaptureContext(_Out_ PCONTEXT ContextRecord)
LONG(NTAPI * PVECTORED_EXCEPTION_HANDLER)(PEXCEPTION_POINTERS ExceptionPointers)
Definition: rtltypes.h:499
#define SYNCHRONIZE
Definition: nt_native.h:61
#define CONTEXT_DEBUG_REGISTERS
Definition: nt_native.h:1376
#define CONTEXT_CONTROL
Definition: nt_native.h:1372
struct _CONTEXT CONTEXT
#define PAGE_READWRITE
Definition: nt_native.h:1307
#define PAGE_EXECUTE_READ
Definition: nt_native.h:1310
#define CONTEXT_INTEGER
Definition: nt_native.h:1373
#define CONTEXT_FLOATING_POINT
Definition: nt_native.h:1375
#define CONTEXT_SEGMENTS
Definition: nt_native.h:1374
#define BOOL
Definition: nt_native.h:43
NTSYSAPI NTSTATUS NTAPI NtWaitForSingleObject(IN HANDLE hObject, IN BOOLEAN bAlertable, IN PLARGE_INTEGER Timeout)
#define CONTEXT_FULL
Definition: nt_native.h:1378
#define PAGE_NOACCESS
Definition: nt_native.h:1305
#define PAGE_EXECUTE_READWRITE
Definition: nt_native.h:1311
NTSTATUS NTAPI NtContinue(_In_ PCONTEXT Context, _In_ BOOLEAN TestAlert)
Definition: except.c:216
NTSTATUS NTAPI NtSetContextThread(IN HANDLE ThreadHandle, IN PCONTEXT ThreadContext)
Definition: debug.c:387
NTSTATUS NTAPI NtSetInformationProcess(_In_ HANDLE ProcessHandle, _In_ PROCESSINFOCLASS ProcessInformationClass, _In_reads_bytes_(ProcessInformationLength) PVOID ProcessInformation, _In_ ULONG ProcessInformationLength)
Definition: query.c:1390
NTSTATUS NTAPI NtSetInformationThread(_In_ HANDLE ThreadHandle, _In_ THREADINFOCLASS ThreadInformationClass, _In_reads_bytes_(ThreadInformationLength) PVOID ThreadInformation, _In_ ULONG ThreadInformationLength)
Definition: query.c:2269
NTSTATUS NTAPI NtQueryInformationProcess(_In_ HANDLE ProcessHandle, _In_ PROCESSINFOCLASS ProcessInformationClass, _Out_writes_bytes_to_opt_(ProcessInformationLength, *ReturnLength) PVOID ProcessInformation, _In_ ULONG ProcessInformationLength, _Out_opt_ PULONG ReturnLength)
Definition: query.c:211
NTSTATUS NTAPI NtResumeThread(IN HANDLE ThreadHandle, OUT PULONG SuspendCount OPTIONAL)
Definition: state.c:290
NTSTATUS NTAPI NtSuspendThread(IN HANDLE ThreadHandle, OUT PULONG PreviousSuspendCount OPTIONAL)
Definition: state.c:352
#define DBG_PRINTEXCEPTION_WIDE_C
Definition: ntstatus.h:108
#define STATUS_LONGJUMP
Definition: ntstatus.h:297
#define STATUS_ASSERTION_FAILURE
Definition: ntstatus.h:1099
#define STATUS_UNWIND
Definition: ntstatus.h:369
#define STATUS_NO_CALLBACK_ACTIVE
Definition: ntstatus.h:848
#define DBG_RIPEXCEPTION
Definition: ntstatus.h:105
#define STATUS_SINGLE_STEP
Definition: ntstatus.h:265
#define STATUS_UNWIND_CONSOLIDATE
Definition: ntstatus.h:300
#define DBG_CONTINUE
Definition: ntstatus.h:98
#define STATUS_PRIVILEGED_INSTRUCTION
Definition: ntstatus.h:480
#define STATUS_BREAKPOINT
Definition: ntstatus.h:264
#define STATUS_FLOAT_MULTIPLE_TRAPS
Definition: ntstatus.h:933
#define DBG_PRINTEXCEPTION_C
Definition: ntstatus.h:104
#define DBG_EXCEPTION_NOT_HANDLED
Definition: ntstatus.h:109
#define STATUS_STACK_BUFFER_OVERRUN
Definition: ntstatus.h:1078
#define STATUS_INVALID_INFO_CLASS
Definition: ntstatus.h:333
#define STATUS_PARTIAL_COPY
Definition: ntstatus.h:273
#define STATUS_SUSPEND_COUNT_EXCEEDED
Definition: ntstatus.h:404
#define LOWORD(l)
Definition: pedump.c:82
#define IMAGE_FILE_MACHINE_I386
Definition: pedump.c:174
short WCHAR
Definition: pedump.c:58
#define BOOLEAN
Definition: pedump.c:73
long LONG
Definition: pedump.c:60
unsigned short USHORT
Definition: pedump.c:61
__asm__(".p2align 4, 0x90\n" ".seh_proc __seh2_global_filter_func\n" "__seh2_global_filter_func:\n" "\tsub %rbp, %rax\n" "\tpush %rbp\n" "\t.seh_pushreg %rbp\n" "\tpush %rbx\n" "\t.seh_pushreg %rbx\n" "\tpush %rdi\n" "\t.seh_pushreg %rdi\n" "\tpush %rsi\n" "\t.seh_pushreg %rsi\n" "\tpush %r12\n" "\t.seh_pushreg %r12\n" "\tpush %r13\n" "\t.seh_pushreg %r13\n" "\tpush %r14\n" "\t.seh_pushreg %r14\n" "\tpush %r15\n" "\t.seh_pushreg %r15\n" "\tsub $40, %rsp\n" "\t.seh_stackalloc 40\n" "\t.seh_endprologue\n" "\tsub %rax, %rdx\n" "\tmov %rdx, %rbp\n" "\tjmp *%r8\n" "__seh2_global_filter_func_exit:\n" "\t.p2align 4\n" "\tadd $40, %rsp\n" "\tpop %r15\n" "\tpop %r14\n" "\tpop %r13\n" "\tpop %r12\n" "\tpop %rsi\n" "\tpop %rdi\n" "\tpop %rbx\n" "\tpop %rbp\n" "\tret\n" "\t.seh_endproc")
#define test
Definition: rosglue.h:37
#define offsetof(TYPE, MEMBER)
int winetest_get_mainargs(char ***pargv)
#define wait_child_process
Definition: test.h:159
#define CONTEXT_XSTATE
XSAVE_FORMAT XMM_SAVE_AREA32
Definition: ke.h:35
#define EXCEPTION_EXECUTE_FAULT
#define EXCEPTION_READ_FAULT
#define CONTEXT_ALL
#define CONTEXT_EXTENDED_REGISTERS
#define R1(v, w, x, y, z, i)
Definition: sha1.c:36
#define R2(v, w, x, y, z, i)
Definition: sha1.c:37
#define R0(v, w, x, y, z, i)
Definition: sha1.c:35
#define R3(v, w, x, y, z, i)
Definition: sha1.c:38
#define R4(v, w, x, y, z, i)
Definition: sha1.c:39
#define memset(x, y, z)
Definition: compat.h:39
#define args
Definition: format.c:66
#define _WIN32_WINNT_WIN8
Definition: sdkddkver.h:29
#define _WIN32_WINNT_WIN7
Definition: sdkddkver.h:28
#define STATUS_SUCCESS
Definition: shellext.h:65
_In_ PVOID Context
Definition: storport.h:2269
TCHAR * cmdline
Definition: stretchblt.cpp:32
@ High
Definition: strmini.h:378
@ Low
Definition: strmini.h:380
ULONG Length
Definition: ketypes.h:1094
CONTEXT_CHUNK XState
Definition: ketypes.h:1100
CONTEXT_CHUNK All
Definition: ketypes.h:1098
CONTEXT_CHUNK Legacy
Definition: ketypes.h:1099
ULONG Eip
Definition: nt_native.h:1479
ULONG R12
Definition: ke.h:267
ULONG Pc
Definition: ke.h:271
ULONG EFlags
Definition: nt_native.h:1481
ULONG Eax
Definition: nt_native.h:1471
union _DEBUG_EVENT::@3289 u
OUTPUT_DEBUG_STRING_INFO DebugString
Definition: minwinbase.h:208
DWORD dwDebugEventCode
Definition: minwinbase.h:196
RIP_INFO RipInfo
Definition: minwinbase.h:209
DWORD dwThreadId
Definition: minwinbase.h:198
DWORD dwProcessId
Definition: minwinbase.h:197
EXCEPTION_DEBUG_INFO Exception
Definition: minwinbase.h:201
CREATE_PROCESS_DEBUG_INFO CreateProcessInfo
Definition: minwinbase.h:203
PVOID NonVolatileRegisters
Definition: ketypes.h:685
EXCEPTION_RECORD ExceptionRecord
Definition: minwinbase.h:152
PEXCEPTION_RECORD ExceptionRecord
Definition: rtltypes.h:200
PCONTEXT ContextRecord
Definition: rtltypes.h:201
struct _EXCEPTION_RECORD * ExceptionRecord
Definition: compat.h:210
DWORD ExceptionCode
Definition: compat.h:208
DWORD NumberParameters
Definition: compat.h:212
DWORD ExceptionFlags
Definition: compat.h:209
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]
Definition: compat.h:213
PVOID ExceptionAddress
Definition: compat.h:211
PEXCEPTION_ROUTINE Handler
Definition: compat.h:727
struct _EXCEPTION_REGISTRATION_RECORD * Prev
Definition: exception.h:45
IMAGE_OPTIONAL_HEADER32 OptionalHeader
Definition: ntddk_ex.h:184
ULONG StackBase
Definition: winternl.h:716
PVOID StackBase
Definition: compat.h:712
PVOID ImageBaseAddress
Definition: ntddk_ex.h:245
BYTE BeingDebugged
Definition: btrfs_drv.h:1909
DWORD dwType
Definition: minwinbase.h:191
DWORD dwError
Definition: minwinbase.h:190
ULONG DeallocationStack
Definition: winternl.h:1197
NT_TIB32 Tib
Definition: winternl.h:1148
Definition: compat.h:836
LONG WowTebOffset
Definition: winternl.h:701
PVOID TlsSlots[64]
Definition: compat.h:879
PVOID DeallocationStack
Definition: compat.h:878
NT_TIB Tib
Definition: compat.h:837
BYTE CountOfCodes
Definition: cpu_x86_64.c:65
BYTE FrameOffset
Definition: cpu_x86_64.c:67
BYTE Version
Definition: cpu_x86_64.c:62
UNWIND_CODE UnwindCode[1]
Definition: cpu_x86_64.c:68
BYTE SizeOfProlog
Definition: cpu_x86_64.c:64
BYTE FrameRegister
Definition: cpu_x86_64.c:66
DWORD Ebp
Definition: compat.h:273
DWORD SegSs
Definition: compat.h:278
DWORD Ebx
Definition: compat.h:269
DWORD SegCs
Definition: compat.h:275
DWORD Dr0
Definition: compat.h:256
DWORD ContextFlags
Definition: compat.h:255
DWORD Dr2
Definition: compat.h:258
WOW64_FLOATING_SAVE_AREA FloatSave
Definition: compat.h:262
DWORD EFlags
Definition: compat.h:276
DWORD SegEs
Definition: compat.h:265
DWORD Eip
Definition: compat.h:274
DWORD Dr1
Definition: compat.h:257
DWORD SegDs
Definition: compat.h:266
DWORD Dr3
Definition: compat.h:259
DWORD Esi
Definition: compat.h:268
DWORD Dr7
Definition: compat.h:261
DWORD SegFs
Definition: compat.h:264
DWORD Edx
Definition: compat.h:270
DWORD Edi
Definition: compat.h:267
DWORD Eax
Definition: compat.h:272
BYTE ExtendedRegisters[WOW64_MAXIMUM_SUPPORTED_EXTENSION]
Definition: compat.h:279
DWORD SegGs
Definition: compat.h:263
DWORD Ecx
Definition: compat.h:271
Definition: winnt.h:146
ULONG64 Reserved[6]
Definition: winnt.h:149
ULONG64 Mask
Definition: winnt.h:147
YMMCONTEXT YmmContext
Definition: winnt.h:150
ULONG64 CompactionMask
Definition: winnt.h:148
M128A Ymm0
Definition: winnt.h:126
Definition: match.c:390
Definition: inflate.c:139
Definition: http.c:7252
Definition: ffs.h:70
Definition: parser.c:49
Definition: format.c:80
Definition: stat.h:66
Definition: ps.c:97
DWORD WINAPI SleepEx(IN DWORD dwMilliseconds, IN BOOL bAlertable)
Definition: synch.c:738
DWORD WINAPI WaitForSingleObject(IN HANDLE hHandle, IN DWORD dwMilliseconds)
Definition: synch.c:82
VOID WINAPI DECLSPEC_HOTPATCH Sleep(IN DWORD dwMilliseconds)
Definition: synch.c:726
HANDLE WINAPI DECLSPEC_HOTPATCH CreateEventA(IN LPSECURITY_ATTRIBUTES lpEventAttributes OPTIONAL, IN BOOL bManualReset, IN BOOL bInitialState, IN LPCSTR lpName OPTIONAL)
Definition: synch.c:573
HANDLE WINAPI DECLSPEC_HOTPATCH CreateEventW(IN LPSECURITY_ATTRIBUTES lpEventAttributes OPTIONAL, IN BOOL bManualReset, IN BOOL bInitialState, IN LPCWSTR lpName OPTIONAL)
Definition: synch.c:587
BOOL WINAPI DECLSPEC_HOTPATCH SetEvent(IN HANDLE hEvent)
Definition: synch.c:669
HANDLE WINAPI DECLSPEC_HOTPATCH OpenEventA(IN DWORD dwDesiredAccess, IN BOOL bInheritHandle, IN LPCSTR lpName)
Definition: synch.c:605
BOOL WINAPI DECLSPEC_HOTPATCH ResetEvent(IN HANDLE hEvent)
Definition: synch.c:650
__inline int before(__u32 seq1, __u32 seq2)
Definition: tcpcore.h:2390
__inline int after(__u32 seq1, __u32 seq2)
Definition: tcpcore.h:2395
#define DWORD_PTR
Definition: treelist.c:76
uint32_t DWORD_PTR
Definition: typedefs.h:65
PVOID HANDLE
Definition: typedefs.h:73
ULONG_PTR SIZE_T
Definition: typedefs.h:80
uint64_t DWORD64
Definition: typedefs.h:67
uint64_t ULONGLONG
Definition: typedefs.h:67
uint32_t ULONG_PTR
Definition: typedefs.h:65
uint32_t UINT32
Definition: typedefs.h:59
uint32_t ULONG
Definition: typedefs.h:59
pass
Definition: typegen.h:25
#define STATUS_ACCESS_DENIED
Definition: udferr_usr.h:145
#define STATUS_INVALID_PARAMETER
Definition: udferr_usr.h:135
#define STATUS_INFO_LENGTH_MISMATCH
Definition: udferr_usr.h:133
double FpNvRegs[NONVOL_FP_NUMREG_ARM64]
Definition: winnt_old.h:2506
DWORD64 GpNvRegs[NONVOL_INT_NUMREG_ARM64]
Definition: winnt_old.h:2505
BYTE UnwindOp
Definition: cpu_x86_64.c:54
BYTE CodeOffset
Definition: cpu_x86_64.c:53
USHORT FrameOffset
Definition: cpu_x86_64.c:57
int retval
Definition: wcstombs.cpp:91
BOOL NTAPI VirtualProtect(IN LPVOID lpAddress, IN SIZE_T dwSize, IN DWORD flNewProtect, OUT PDWORD lpflOldProtect)
Definition: virtmem.c:135
DWORD WINAPI GetLastError(void)
Definition: except.c:1042
HANDLE WINAPI GetCurrentThread(void)
Definition: proc.c:1146
#define EVENT_MODIFY_STATE
Definition: winbase.h:165
DWORD WINAPI GetCurrentProcessId(void)
Definition: proc.c:1156
#define CREATE_SUSPENDED
Definition: winbase.h:182
#define WAIT_OBJECT_0
Definition: winbase.h:383
#define DEBUG_PROCESS
Definition: winbase.h:180
_In_ LONG _In_ HWND hwnd
Definition: winddi.h:4023
void * arg
Definition: msvc.h:10
#define WINAPI
Definition: msvc.h:6
#define WOW64_TLS_CPURESERVED
Definition: winternl.h:1362
NTSTATUS(WINAPI * KERNEL_CALLBACK_PROC)(void *, ULONG)
Definition: winternl.h:461
@ KCONTINUE_UNWIND
Definition: winternl.h:4504
NTSYSAPI void WINAPI DbgBreakPoint(void)
@ ThreadSuspendCount
Definition: winternl.h:2316
@ ProcessInstrumentationCallback
Definition: winternl.h:1922
@ ProcessExecuteFlags
Definition: winternl.h:1916
void(CALLBACK * PNTAPCFUNC)(ULONG_PTR, ULONG_PTR, ULONG_PTR)
Definition: winternl.h:3491
#define KCONTINUE_FLAG_TEST_ALERT
Definition: winternl.h:4518
LONG(CALLBACK * PRTL_EXCEPTION_FILTER)(PEXCEPTION_POINTERS)
Definition: winternl.h:4108
#define CONTEXT_EXCEPTION_ACTIVE
Definition: winnt.h:155
#define CONTEXT_SERVICE_ACTIVE
Definition: winnt.h:156
#define CONTEXT_EXCEPTION_REQUEST
Definition: winnt.h:157
#define CONTEXT_EXCEPTION_REPORTING
Definition: winnt.h:158
struct _XSTATE XSTATE
#define CONTEXT_UNWOUND_TO_CALL
Definition: winnt.h:153
#define NONVOL_INT_NUMREG_ARM64
Definition: winnt_old.h:2494
ARM64EC_NT_CONTEXT
Definition: winnt_old.h:2476
#define NONVOL_FP_NUMREG_ARM64
Definition: winnt_old.h:2495
#define EXCEPTION_SOFTWARE_ORIGINATE
Definition: winnt_old.h:2587
#define CreateWindowA(a, b, c, d, e, f, g, h, i, j, k)
Definition: winuser.h:4469
BOOL WINAPI DestroyWindow(_In_ HWND)
ActualNumberDriverObjects * sizeof(PDRIVER_OBJECT)) PDRIVER_OBJECT *DriverObjectList
ARM64_NT_CONTEXT
Definition: ketypes.h:1262
XSAVE_FORMAT
Definition: ketypes.h:1018
M128A
Definition: ketypes.h:992
#define XSTATE_AVX
Definition: ketypes.h:1267
#define XSTATE_AVX512_KMASK
Definition: ketypes.h:1270
#define XSTATE_LEGACY_SSE
Definition: ketypes.h:1265
#define XSTATE_MASK_GSSE
Definition: ketypes.h:1285
#define FAST_FAIL_GS_COOKIE_INIT
#define FAST_FAIL_INVALID_FAST_FAIL_CODE
#define FAST_FAIL_INCORRECT_STACK
#define FAST_FAIL_LEGACY_GS_VIOLATION
#define FAST_FAIL_VTGUARD_CHECK_FAILURE
#define FAST_FAIL_STACK_COOKIE_CHECK_FAILURE
#define FAST_FAIL_FATAL_APP_EXIT
#define FAST_FAIL_INVALID_ARG
#define FAST_FAIL_CORRUPT_LIST_ENTRY
#define EXCEPTION_NESTED_CALL
Definition: rtltypes.h:158
#define EXCEPTION_UNWINDING
Definition: rtltypes.h:155
#define EXCEPTION_TARGET_UNWIND
Definition: rtltypes.h:159
#define EXCEPTION_COLLIDED_UNWIND
Definition: rtltypes.h:160
unsigned char BYTE
Definition: xxhash.c:193