And fix some test failures on the way.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54289
-- v2: ntdll: Do not use extended context in RtlCopyContext() if extended state is not copied. ntdll: Factor out context_copy_ranges() function. ntdll/tests: Fix random test failure in test_restore_context(). ntdll/tests: Fix xstate tests failing on modern Windows and CPUs with more xstate features. ntdll/tests: Fix the case of absent XSTATE context in test_copy_context().
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/exception.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 4bf2e4ec78e..b9d38ef4f0e 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -10524,10 +10524,13 @@ static void test_copy_context(void) *(DWORD *)((BYTE *)dst + flags_offset) = 0; *(DWORD *)((BYTE *)src + flags_offset) = 0;
- src_xs = (XSTATE *)((BYTE *)src_ex + src_ex->XState.Offset); - memset(src_xs, 0xcc, sizeof(XSTATE)); - src_xs->Mask = 3; - src_xs->CompactionMask = ~(ULONG64)0; + if (flags & 0x40) + { + src_xs = (XSTATE *)((BYTE *)src_ex + src_ex->XState.Offset); + memset(src_xs, 0xcc, sizeof(XSTATE)); + src_xs->Mask = 3; + src_xs->CompactionMask = ~(ULONG64)0; + }
status = pRtlCopyExtendedContext(dst_ex, flags, src_ex); ok(!status, "Got unexpected status %#lx, flags %#lx.\n", status, flags);
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/exception.c | 51 +++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 21 deletions(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index b9d38ef4f0e..0d77ffd90a0 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -198,7 +198,7 @@ static int test_stage; #if defined(__i386__) || defined(__x86_64__) static void test_debugger_xstate(HANDLE thread, CONTEXT *ctx, int stage) { - char context_buffer[sizeof(CONTEXT) + sizeof(CONTEXT_EX) + sizeof(XSTATE) + 63]; + char context_buffer[sizeof(CONTEXT) + sizeof(CONTEXT_EX) + sizeof(XSTATE) + 1024]; CONTEXT_EX *c_ex; NTSTATUS status; YMMCONTEXT *ymm; @@ -9487,6 +9487,7 @@ static void test_extended_context(void) 8, }, }; + const ULONG64 supported_features = 7, supported_compaction_mask = supported_features | ((ULONG64)1 << 63); ULONG expected_length, expected_length_xstate, context_flags, expected_offset; ULONG64 enabled_features, expected_compaction; DECLSPEC_ALIGN(64) BYTE context_buffer2[2048]; @@ -9999,7 +10000,7 @@ static void test_extended_context(void) mask = 0xdeadbeef; bret = pGetXStateFeaturesMask(context, &mask); if (flags & CONTEXT_NATIVE) - ok(bret && mask == enabled_features, + ok(bret && mask == (enabled_features & supported_features), "Got unexpected bret %#x, mask %s, flags %#lx.\n", bret, wine_dbgstr_longlong(mask), flags); else ok(!bret && mask == 0xdeadbeef, @@ -10243,9 +10244,9 @@ static void test_extended_context(void) expected_compaction = compaction_enabled ? ((ULONG64)1 << 63) | 4 : 0;
xs = (XSTATE *)((BYTE *)context_ex + context_ex->XState.Offset); - ok(xs->Mask == (xsaveopt_enabled ? 0 : 4), "Got unexpected Mask %#I64x.\n", xs->Mask); - ok(xs->CompactionMask == expected_compaction, "Got unexpected CompactionMask %s.\n", - wine_dbgstr_longlong(xs->CompactionMask)); + ok((xs->Mask & supported_features) == (xsaveopt_enabled ? 0 : 4), "Got unexpected Mask %#I64x.\n", xs->Mask); + ok((xs->CompactionMask & (supported_features | ((ULONG64)1 << 63))) == expected_compaction, + "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(xs->CompactionMask));
for (i = 4; i < 8; ++i) ok(!data[i], "Got unexpected data %#x, i %u.\n", data[i], i); @@ -10291,9 +10292,10 @@ static void test_extended_context(void) bret = GetThreadContext(thread, context); ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); todo_wine_if (!xsaveopt_enabled) - ok(xs->Mask == (xsaveopt_enabled ? 0 : 4), "Got unexpected Mask %#I64x.\n", xs->Mask); - ok(xs->CompactionMask == expected_compaction, "Got unexpected CompactionMask %s.\n", - wine_dbgstr_longlong(xs->CompactionMask)); + ok((xs->Mask & supported_features) == (xsaveopt_enabled ? 0 : 4), "Got unexpected Mask %#I64x.\n", xs->Mask); + ok((xs->CompactionMask & supported_compaction_mask) == expected_compaction, + "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(xs->CompactionMask)); + for (i = 0; i < 16 * 4; ++i) ok(((ULONG *)&xs->YmmContext)[i] == ((xs->Mask & 4) ? 0 : 0xcccccccc), "Got unexpected value %#lx, i %u.\n", ((ULONG *)&xs->YmmContext)[i], i); @@ -10306,8 +10308,9 @@ static void test_extended_context(void) memset(&xs->YmmContext, 0xcc, sizeof(xs->YmmContext)); bret = GetThreadContext(thread, context); ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); - ok(!xs->Mask || broken(xs->Mask == 4), "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask)); - ok(xs->CompactionMask == expected_compaction, "Got unexpected CompactionMask %s.\n", + ok(!(xs->Mask & supported_features) || broken((xs->Mask & supported_features) == 4), "Got unexpected Mask %s.\n", + wine_dbgstr_longlong(xs->Mask)); + ok((xs->CompactionMask & supported_compaction_mask) == expected_compaction, "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(xs->CompactionMask)); for (i = 0; i < 16 * 4; ++i) ok(((ULONG *)&xs->YmmContext)[i] == 0xcccccccc || broken(xs->Mask == 4 && !((ULONG *)&xs->YmmContext)[i]), @@ -10320,8 +10323,8 @@ static void test_extended_context(void) memset(&xs->YmmContext, 0xcc, sizeof(xs->YmmContext)); bret = GetThreadContext(thread, context); ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); - ok(xs->Mask == 4, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask)); - ok(xs->CompactionMask == expected_compaction, "Got unexpected CompactionMask %s.\n", + ok((xs->Mask & supported_features) == 4, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask)); + ok((xs->CompactionMask & supported_compaction_mask) == expected_compaction, "Got unexpected CompactionMask %s.\n", wine_dbgstr_longlong(xs->CompactionMask)); for (i = 0; i < 16 * 4; ++i) ok(((ULONG *)&xs->YmmContext)[i] == 0x28282828, "Got unexpected value %#lx, i %u.\n", @@ -10341,7 +10344,7 @@ static void test_extended_context(void) memset(&xs->YmmContext, 0xcc, sizeof(xs->YmmContext)); bret = GetThreadContext(thread, context); ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); - ok(xs->Mask == 4, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask)); + ok((xs->Mask & supported_features) == 4, "Got unexpected Mask %s.\n", wine_dbgstr_longlong(xs->Mask));
for (i = 0; i < 4; ++i) ok(((ULONG *)&xs->YmmContext)[i] == 0x68686868, "Got unexpected value %#lx, i %u.\n", @@ -10353,9 +10356,10 @@ static void test_extended_context(void) bret = GetThreadContext(thread, context); ok(bret, "Got unexpected bret %#x, GetLastError() %lu.\n", bret, GetLastError()); todo_wine_if (!xsaveopt_enabled && sizeof(void *) != 4) - ok(xs->Mask == (xsaveopt_enabled ? 0 : 4) || (sizeof(void *) == 4 && xs->Mask == 4), - "Got unexpected Mask %#I64x.\n", xs->Mask); - if (xs->Mask == 4) + ok((xs->Mask & supported_features) == (xsaveopt_enabled ? 0 : 4) + || (sizeof(void *) == 4 && (xs->Mask & supported_features) == 4), + "Got unexpected Mask %#I64x, supported_features.\n", xs->Mask); + if ((xs->Mask & supported_features) == 4) { for (i = 0; i < 8 * sizeof(void *); ++i) ok(((ULONG *)&xs->YmmContext)[i] == 0, @@ -10431,13 +10435,13 @@ static void check_changes_in_range_(const char *file, unsigned int line, const B
static void test_copy_context(void) { - static const struct modified_range ranges_amd64[] = + static struct modified_range ranges_amd64[] = { {0x30, ~0}, {0x38, 0x1}, {0x3a, 0x4}, {0x42, 0x1}, {0x48, 0x10}, {0x78, 0x2}, {0x98, 0x1}, {0xa0, 0x2}, {0xf8, 0x1}, {0x100, 0x8}, {0x2a0, 0x80000008}, {0x4b0, 0x10}, {0x4d0, ~0}, {0x4e8, 0}, {0x500, ~0}, {0x640, 0}, {0x1000, 0}, }; - static const struct modified_range ranges_x86[] = + static struct modified_range ranges_x86[] = { {0x0, ~0}, {0x4, 0x10}, {0x1c, 0x8}, {0x8c, 0x4}, {0x9c, 0x2}, {0xb4, 0x1}, {0xcc, 0x20}, {0x1ec, 0x80000020}, {0x2cc, ~0}, {0x294, 0}, {0x1000, 0}, @@ -10524,18 +10528,23 @@ static void test_copy_context(void) *(DWORD *)((BYTE *)dst + flags_offset) = 0; *(DWORD *)((BYTE *)src + flags_offset) = 0;
+ context_length = (BYTE *)dst_ex - (BYTE *)dst + dst_ex->All.Length; + if (flags & 0x40) { src_xs = (XSTATE *)((BYTE *)src_ex + src_ex->XState.Offset); - memset(src_xs, 0xcc, sizeof(XSTATE)); - src_xs->Mask = 3; + memset(src_xs, 0xcc, src_ex->XState.Length); + src_xs->Mask = enabled_features & ~(ULONG64)4; src_xs->CompactionMask = ~(ULONG64)0; + if (flags & CONTEXT_AMD64) + ranges_amd64[ARRAY_SIZE(ranges_amd64) - 2].start = 0x640 + src_ex->XState.Length - sizeof(XSTATE); + else + ranges_x86[ARRAY_SIZE(ranges_x86) - 2].start = 0x294 + src_ex->XState.Length - sizeof(XSTATE); }
status = pRtlCopyExtendedContext(dst_ex, flags, src_ex); ok(!status, "Got unexpected status %#lx, flags %#lx.\n", status, flags);
- context_length = (BYTE *)dst_ex - (BYTE *)dst + dst_ex->All.Length; check_changes_in_range((BYTE *)dst, flags & CONTEXT_AMD64 ? &ranges_amd64[0] : &ranges_x86[0], flags, context_length);
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/exception.c | 1 - 1 file changed, 1 deletion(-)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 0d77ffd90a0..a09beee2203 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -2465,7 +2465,6 @@ static void test_restore_context(void) rec.NumberParameters = 1; rec.ExceptionInformation[0] = (DWORD64)&buf;
- ok(buf.MxCsr == 0x1f80, "Got unexpected MxCsr %#lx.\n", buf.MxCsr); ok(buf.FpCsr == 0x27f, "Got unexpected FpCsr %#x.\n", buf.FpCsr); buf.FpCsr = 0x7f; buf.MxCsr = 0x3f80;
From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/exception.c | 51 ++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 24 deletions(-)
diff --git a/dlls/ntdll/exception.c b/dlls/ntdll/exception.c index 22c9c829a4d..fa30118f142 100644 --- a/dlls/ntdll/exception.c +++ b/dlls/ntdll/exception.c @@ -961,6 +961,32 @@ ULONG64 WINAPI RtlGetExtendedFeaturesMask( CONTEXT_EX *context_ex ) }
+static void context_copy_ranges( BYTE *d, DWORD context_flags, BYTE *s, const struct context_parameters *p ) +{ + const struct context_copy_range *range; + unsigned int start; + + *((ULONG *)(d + p->flags_offset)) |= context_flags; + + start = 0; + range = p->copy_ranges; + do + { + if (range->flag & context_flags) + { + if (!start) + start = range->start; + } + else if (start) + { + memcpy( d + start, s + start, range->start - start ); + start = 0; + } + } + while (range++->start != p->context_size); +} + + /*********************************************************************** * RtlCopyContext (NTDLL.@) */ @@ -1010,12 +1036,9 @@ NTSTATUS WINAPI RtlCopyContext( CONTEXT *dst, DWORD context_flags, CONTEXT *src */ NTSTATUS WINAPI RtlCopyExtendedContext( CONTEXT_EX *dst, ULONG context_flags, CONTEXT_EX *src ) { - const struct context_copy_range *range; const struct context_parameters *p; XSTATE *dst_xs, *src_xs; ULONG64 feature_mask; - unsigned int start; - BYTE *d, *s;
TRACE( "dst %p, context_flags %#lx, src %p.\n", dst, context_flags, src );
@@ -1025,27 +1048,7 @@ NTSTATUS WINAPI RtlCopyExtendedContext( CONTEXT_EX *dst, ULONG context_flags, CO if (!(feature_mask = RtlGetEnabledExtendedFeatures( ~(ULONG64)0 )) && context_flags & 0x40) return STATUS_NOT_SUPPORTED;
- d = RtlLocateLegacyContext( dst, NULL ); - s = RtlLocateLegacyContext( src, NULL ); - - *((ULONG *)(d + p->flags_offset)) |= context_flags; - - start = 0; - range = p->copy_ranges; - do - { - if (range->flag & context_flags) - { - if (!start) - start = range->start; - } - else if (start) - { - memcpy( d + start, s + start, range->start - start ); - start = 0; - } - } - while (range++->start != p->context_size); + context_copy_ranges( RtlLocateLegacyContext( dst, NULL ), context_flags, RtlLocateLegacyContext( src, NULL ), p );
if (!(context_flags & 0x40)) return STATUS_SUCCESS;
From: Paul Gofman pgofman@codeweavers.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54289 --- dlls/ntdll/exception.c | 12 ++++++++++-- dlls/ntdll/tests/exception.c | 20 ++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-)
diff --git a/dlls/ntdll/exception.c b/dlls/ntdll/exception.c index fa30118f142..556b424ac6e 100644 --- a/dlls/ntdll/exception.c +++ b/dlls/ntdll/exception.c @@ -994,6 +994,7 @@ NTSTATUS WINAPI RtlCopyContext( CONTEXT *dst, DWORD context_flags, CONTEXT *src { DWORD context_size, arch_flag, flags_offset, dst_flags, src_flags; static const DWORD arch_mask = CONTEXT_i386 | CONTEXT_AMD64; + const struct context_parameters *p; BYTE *d, *s;
TRACE("dst %p, context_flags %#lx, src %p.\n", dst, context_flags, src); @@ -1026,8 +1027,15 @@ NTSTATUS WINAPI RtlCopyContext( CONTEXT *dst, DWORD context_flags, CONTEXT *src context_flags &= src_flags; if (context_flags & ~dst_flags & 0x40) return STATUS_BUFFER_OVERFLOW;
- return RtlCopyExtendedContext( (CONTEXT_EX *)(d + context_size), context_flags, - (CONTEXT_EX *)(s + context_size) ); + if (context_flags & 0x40) + return RtlCopyExtendedContext( (CONTEXT_EX *)(d + context_size), context_flags, + (CONTEXT_EX *)(s + context_size) ); + + if (!(p = context_get_parameters( context_flags ))) + return STATUS_INVALID_PARAMETER; + + context_copy_ranges( d, context_flags, s, p ); + return STATUS_SUCCESS; }
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index a09beee2203..94b2ccf61a6 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -10499,6 +10499,26 @@ static void test_copy_context(void)
enabled_features = pRtlGetEnabledExtendedFeatures(~(ULONG64)0);
+ memset(dst_context_buffer, 0xdd, sizeof(dst_context_buffer)); + memset(src_context_buffer, 0xcc, sizeof(src_context_buffer)); + + status = pRtlInitializeExtendedContext(src_context_buffer, CONTEXT_ALL | CONTEXT_XSTATE, &src_ex); + if (!status) + { + src = pRtlLocateLegacyContext(src_ex, NULL); + dst = (CONTEXT *)dst_context_buffer; + dst->ContextFlags = CONTEXT_ALL; + status = pRtlCopyContext(dst, dst->ContextFlags, src); + ok(!status, "Got status %#lx.\n", status); + check_changes_in_range((BYTE *)dst, CONTEXT_ALL & CONTEXT_AMD64 ? &ranges_amd64[0] : &ranges_x86[0], + CONTEXT_ALL, sizeof(CONTEXT)); + } + else + { + ok(status == STATUS_NOT_SUPPORTED, "Got status %#lx.\n", status); + skip("Extended context is not supported.\n"); + } + for (i = 0; i < ARRAY_SIZE(tests); ++i) { flags = tests[i];
v2: - change xstate presence condition in patch 1; - just remove randomly failing test in patch 3 (in is not the one which actually checks mxcsr restoration; original version is randomly failing as depends on dynamic FPU state, v1 version makes it test nothing).