Signed-off-by: Paul Gofman <pgofman(a)codeweavers.com>
---
dlls/ntdll/tests/exception.c | 160 +++++++++++++++++++++++++++++++++++
1 file changed, 160 insertions(+)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c
index 1effffd421f..460f0e9a03e 100644
--- a/dlls/ntdll/tests/exception.c
+++ b/dlls/ntdll/tests/exception.c
@@ -30,6 +30,7 @@
#include "winternl.h"
#include "excpt.h"
#include "wine/test.h"
+#include "intrin.h"
static void *code_mem;
@@ -43,6 +44,7 @@ static ULONG (WINAPI *pRtlRemoveVectoredExceptionHandler)(PVOID handler);
static PVOID (WINAPI *pRtlAddVectoredContinueHandler)(ULONG first, PVECTORED_EXCEPTION_HANDLER func);
static ULONG (WINAPI *pRtlRemoveVectoredContinueHandler)(PVOID handler);
static void (WINAPI *pRtlSetUnhandledExceptionFilter)(PRTL_EXCEPTION_FILTER filter);
+static ULONG64 (WINAPI *pRtlGetEnabledExtendedFeatures)(ULONG64);
static NTSTATUS (WINAPI *pNtReadVirtualMemory)(HANDLE, const void*, void*, SIZE_T, SIZE_T*);
static NTSTATUS (WINAPI *pNtTerminateProcess)(HANDLE handle, LONG exit_code);
static NTSTATUS (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
@@ -5458,6 +5460,161 @@ static void test_unload_trace(void)
}
}
+#if defined(__i386__) || defined(__x86_64__)
+
+static const unsigned int test_extended_context_data[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+static const unsigned test_extended_context_spoil_data1[8] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80};
+static const unsigned test_extended_context_spoil_data2[8] = {0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, 0x85};
+
+static BOOL test_extended_context_modified_state;
+
+static DWORD test_extended_context_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
+ CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher)
+{
+ static const ULONG64 expected_compaction_mask = 0x8000000000000004;
+ CONTEXT_EX *xctx = (CONTEXT_EX *)(context + 1);
+ unsigned int *context_ymm_data;
+ DWORD expected_min_offset;
+ BOOL compaction;
+ int regs[4];
+ XSTATE *xs;
+
+ /* Since we got xstates enabled by OS this cpuid level should be supported. */
+ __cpuidex(regs, 0xd, 1);
+ compaction = regs[0] & 2;
+ todo_wine ok((context->ContextFlags & (CONTEXT_FULL | CONTEXT_XSTATE)) == (CONTEXT_FULL | CONTEXT_XSTATE),
+ "Got unexpected ContextFlags %#x.\n", context->ContextFlags);
+
+ if ((context->ContextFlags & (CONTEXT_FULL | CONTEXT_XSTATE)) != (CONTEXT_FULL | CONTEXT_XSTATE))
+ goto done;
+
+#ifdef __x86_64__
+ {
+ /* Unwind contexts do not inherit xstate information. */
+ DISPATCHER_CONTEXT *dispatch = (DISPATCHER_CONTEXT *)dispatcher;
+
+ ok(!(dispatch->ContextRecord->ContextFlags & 0x40), "Got unexpected ContextRecord->ContextFlags %#x.\n",
+ dispatch->ContextRecord->ContextFlags);
+ }
+#endif
+
+ ok(xctx->Legacy.Offset == -(int)(sizeof(CONTEXT)), "Got unexpected Legacy.Offset %d.\n", xctx->Legacy.Offset);
+ ok(xctx->Legacy.Length == sizeof(CONTEXT), "Got unexpected Legacy.Length %d.\n", xctx->Legacy.Length);
+ ok(xctx->All.Offset == -(int)sizeof(CONTEXT), "Got unexpected All.Offset %d.\n", xctx->All.Offset);
+ ok(xctx->All.Length == sizeof(CONTEXT) + xctx->XState.Offset + xctx->XState.Length,
+ "Got unexpected All.Offset %d.\n", xctx->All.Offset);
+ expected_min_offset = sizeof(void *) == 8 ? sizeof(CONTEXT_EX) + sizeof(EXCEPTION_RECORD) : sizeof(CONTEXT_EX);
+ ok(xctx->XState.Offset >= expected_min_offset,
+ "Got unexpected XState.Offset %d.\n", xctx->XState.Offset);
+ ok(xctx->XState.Length >= sizeof(XSTATE), "Got unexpected XState.Length %d.\n", xctx->XState.Length);
+
+ xs = (XSTATE *)((char *)xctx + xctx->XState.Offset);
+ context_ymm_data = (unsigned int *)&xs->YmmContext;
+ ok(!((ULONG_PTR)xs % 64), "Got unexpected xs %p.\n", xs);
+
+ ok((compaction && (xs->CompactionMask & (expected_compaction_mask | 3)) == expected_compaction_mask)
+ || (!compaction && !xs->CompactionMask), "Got unexpected CompactionMask %s, compaction %#x.\n",
+ wine_dbgstr_longlong(xs->CompactionMask), compaction);
+
+ if (test_extended_context_modified_state)
+ {
+ ok((xs->Mask & 7) == 4, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
+ ok(!memcmp(context_ymm_data, test_extended_context_data + 4, sizeof(M128A)),
+ "Got unexpected context data.\n");
+ }
+ else
+ {
+ ok(!xs->Mask, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
+ /* The save area has garbage in this case, the state should be restored to INIT_STATE
+ * without using these data. */
+ memcpy(context_ymm_data, test_extended_context_spoil_data1 + 4, sizeof(M128A));
+ }
+
+done:
+#ifdef __GNUC__
+ __asm__ volatile("vmovups %0,%%ymm0" : : "m"(test_extended_context_spoil_data2));
+#endif
+#ifdef __x86_64__
+ ++context->Rip;
+#else
+ if (*(BYTE *)context->Eip == 0xcc)
+ ++context->Eip;
+#endif
+ return ExceptionContinueExecution;
+}
+
+static void test_extended_context(void)
+{
+ static BYTE except_code_set_ymm0[] =
+ {
+#ifdef __x86_64__
+ 0x48,
+#endif
+ 0xb8, /* mov imm,%ax */
+ 0x00, 0x00, 0x00, 0x00,
+#ifdef __x86_64__
+ 0x00, 0x00, 0x00, 0x00,
+#endif
+
+ 0xc5, 0xfc, 0x10, 0x00, /* vmovups (%ax),%ymm0 */
+ 0xcc, /* int3 */
+ 0xc5, 0xfc, 0x11, 0x00, /* vmovups %ymm0,(%ax) */
+ 0xc3, /* ret */
+ };
+ static BYTE except_code_reset_ymm_state[] =
+ {
+#ifdef __x86_64__
+ 0x48,
+#endif
+ 0xb8, /* mov imm,%ax */
+ 0x00, 0x00, 0x00, 0x00,
+#ifdef __x86_64__
+ 0x00, 0x00, 0x00, 0x00,
+#endif
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0xc5, 0xf8, 0x77, /* vzeroupper */
+ 0x0f, 0x57, 0xc0, /* xorps %xmm0,%xmm0 */
+ 0xcc, /* int3 */
+ 0xc5, 0xfc, 0x11, 0x00, /* vmovups %ymm0,(%ax) */
+ 0xc3, /* ret */
+ };
+ unsigned int i, address_offset;
+ unsigned data[8];
+
+ address_offset = sizeof(void *) == 8 ? 2 : 1;
+ *(void **)(except_code_set_ymm0 + address_offset) = data;
+ *(void **)(except_code_reset_ymm_state + address_offset) = data;
+
+ if (!pRtlGetEnabledExtendedFeatures || !(pRtlGetEnabledExtendedFeatures(~(ULONG64)0) & (1 << XSTATE_AVX)))
+ {
+ skip("AVX is not supported.\n");
+ return;
+ }
+
+ memset(data, 0xff, sizeof(data));
+ test_extended_context_modified_state = FALSE;
+ run_exception_test(test_extended_context_handler, NULL, except_code_reset_ymm_state,
+ ARRAY_SIZE(except_code_reset_ymm_state), PAGE_EXECUTE_READ);
+ for (i = 0; i < 8; ++i)
+ {
+ /* Older Windows version do not reset AVX context to INIT_STATE on x86. */
+ todo_wine_if(i >= 4)
+ ok(!data[i] || broken(i >= 4 && sizeof(void *) == 4 && data[i] == test_extended_context_spoil_data2[i]),
+ "Got unexpected data %#x, i %u.\n", data[i], i);
+ }
+
+ memcpy(data, test_extended_context_data, sizeof(data));
+ test_extended_context_modified_state = TRUE;
+ run_exception_test(test_extended_context_handler, NULL, except_code_set_ymm0,
+ ARRAY_SIZE(except_code_set_ymm0), PAGE_EXECUTE_READ);
+
+ for (i = 0; i < 8; ++i)
+ todo_wine_if(i >= 4)
+ ok(data[i] == test_extended_context_data[i], "Got unexpected data %#x, i %u.\n", data[i], i);
+}
+#endif
+
START_TEST(exception)
{
HMODULE hntdll = GetModuleHandleA("ntdll.dll");
@@ -5500,6 +5657,7 @@ START_TEST(exception)
X(NtResumeProcess);
X(RtlGetUnloadEventTrace);
X(RtlGetUnloadEventTraceEx);
+ X(RtlGetEnabledExtendedFeatures);
#undef X
pIsWow64Process = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");
@@ -5582,6 +5740,7 @@ START_TEST(exception)
test_dpe_exceptions();
test_prot_fault();
test_kiuserexceptiondispatcher();
+ test_extended_context();
#elif defined(__x86_64__)
@@ -5620,6 +5779,7 @@ START_TEST(exception)
test_dynamic_unwind();
else
skip( "Dynamic unwind functions not found\n" );
+ test_extended_context();
#endif
test_debugger();
--
2.26.2