[PATCH 0/2] MR10248: ntdll: Prefer CONTEXT.MxCsr over CONTEXT.FltSave.MxCsr in NtSetContextThread() on x64.
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/ntdll/tests/exception.c | 149 +++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index dfc1581492e..dfc4985cf85 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -6473,6 +6473,154 @@ static void test_backtrace_without_runtime_function(void) ok( ret, "RtlDeleteFunctionTable failed.\n" ); } +static DWORD WINAPI test_set_context_mxcsr_thread_proc( void *dummy ) +{ + return 0; +} + +static int set_context_mxcsr_test_ctx_flags; + +static LONG WINAPI test_set_context_mxcsr_handler(struct _EXCEPTION_POINTERS *e) +{ + EXCEPTION_RECORD *rec = e->ExceptionRecord; + CONTEXT *ctx = e->ContextRecord; + + ok( rec->ExceptionCode == 0x80000003, "got %#lx.\n", rec->ExceptionCode ); + ++ctx->Rip; + + ctx->MxCsr = 0x1f81; + ctx->FltSave.MxCsr = 0x1f82; + if (set_context_mxcsr_test_ctx_flags) ctx->ContextFlags = set_context_mxcsr_test_ctx_flags; + else set_context_mxcsr_test_ctx_flags = ctx->ContextFlags; + + return EXCEPTION_CONTINUE_EXECUTION; +} + +static void test_set_context_mxcsr(void) +{ + static BYTE func[] = + { + 0x51, /* push %rcx */ + 0x0f, 0xae, 0x11, /* ldmxcsr (%rcx) */ + 0xcc, /* int3 */ + 0x59, /* pop %rcx */ + 0x0f, 0xae, 0x19, /* stmxcsr (%rcx) */ + 0xc3, /* ret */ + }; + char context_buffer[sizeof(CONTEXT) + sizeof(CONTEXT_EX) + sizeof(XSTATE) + 4096]; + NTSTATUS (*func_ptr)( DWORD *mxcsr ); + CONTEXT_EX *c_ex; + XSAVE_FORMAT *xs; + XSTATE *xstate; + void *handler; + HANDLE thread; + CONTEXT *ctx; + DWORD length; + DWORD mxcsr; + BOOL bret; + + if (!pRtlGetEnabledExtendedFeatures || !pRtlGetEnabledExtendedFeatures( 1 << XSTATE_LEGACY_FLOATING_POINT )) + { + skip( "XState legacy FP is not supported.\n" ); + return; + } + + length = sizeof(context_buffer); + bret = pInitializeContext (context_buffer, CONTEXT_ALL | CONTEXT_XSTATE, &ctx, &length ); + ok( bret, "got error %lu.\n", GetLastError() ); + + xs = LocateXStateFeature( ctx, XSTATE_LEGACY_FLOATING_POINT, NULL ); + ok( xs == &ctx->FltSave, "got %p, %p.\n", xs, &ctx->FltSave ); + + thread = CreateThread( NULL, 0, test_set_context_mxcsr_thread_proc, NULL, CREATE_SUSPENDED, NULL ); + ok( !!thread, "got NULL.\n" ); + + memset( ctx, 0xcc, sizeof(*ctx) ); + ctx->ContextFlags = CONTEXT_ALL | CONTEXT_XSTATE; + bret = GetThreadContext( thread, ctx ); + ok( bret, "got error %lu.\n", GetLastError() ); + + ok( ctx->MxCsr == 0x1f80, "got %#lx.\n", ctx->MxCsr ); + ok( xs->MxCsr == 0x1f80, "got %#lx.\n", ctx->MxCsr ); + + ctx->MxCsr = 0x1f81; + xs->MxCsr = 0x1f82; + + ctx->ContextFlags = CONTEXT_FLOATING_POINT; + bret = SetThreadContext( thread, ctx ); + ok( bret, "got error %lu.\n", GetLastError() ); + ctx->ContextFlags = CONTEXT_ALL | CONTEXT_XSTATE; + bret = GetThreadContext( thread, ctx ); + ok( bret, "got error %lu.\n", GetLastError() ); + todo_wine ok( ctx->MxCsr == 0x1f81, "got %#lx.\n", ctx->MxCsr ); + todo_wine ok( xs->MxCsr == 0x1f81, "got %#lx.\n", ctx->MxCsr ); + + if (ctx->MxCsr == 0x1f82) + { + ctx->ContextFlags = CONTEXT_FLOATING_POINT; + xs->MxCsr = 0x1f81; + bret = SetThreadContext( thread, ctx ); + ok( bret, "got error %lu.\n", GetLastError() ); + } + + ctx->MxCsr = 0x1f83; + xs->MxCsr = 0x1f84; + ctx->ContextFlags = CONTEXT_CONTROL; + bret = SetThreadContext( thread, ctx ); + ok( bret, "got error %lu.\n", GetLastError() ); + ctx->ContextFlags = CONTEXT_ALL | CONTEXT_XSTATE; + bret = GetThreadContext( thread, ctx ); + ok( bret, "got error %lu.\n", GetLastError() ); + ok( ctx->MxCsr == 0x1f81, "got %#lx.\n", ctx->MxCsr ); + ok( xs->MxCsr == 0x1f81, "got %#lx.\n", ctx->MxCsr ); + + ctx->MxCsr = 0x1f83; + xs->MxCsr = 0x1f84; + c_ex = (CONTEXT_EX *)(ctx + 1); + xstate = (XSTATE *)((char *)c_ex + c_ex->XState.Offset); + xstate->Mask |= XSTATE_MASK_LEGACY; + ctx->ContextFlags = CONTEXT_XSTATE; + bret = SetThreadContext( thread, ctx ); + ok( bret, "got error %lu.\n", GetLastError() ); + ctx->ContextFlags = CONTEXT_ALL | CONTEXT_XSTATE; + bret = GetThreadContext( thread, ctx ); + ok( bret, "got error %lu.\n", GetLastError() ); + ok( ctx->MxCsr == 0x1f81, "got %#lx.\n", ctx->MxCsr ); + ok( xs->MxCsr == 0x1f81, "got %#lx.\n", ctx->MxCsr ); + + ctx->MxCsr = 0x1f83; + xs->MxCsr = 0x1f84; + ctx->ContextFlags = (CONTEXT_ALL & ~CONTEXT_FLOATING_POINT) | CONTEXT_AMD64; + bret = SetThreadContext( thread, ctx ); + ok( bret, "got error %lu.\n", GetLastError() ); + ctx->ContextFlags = CONTEXT_ALL | CONTEXT_XSTATE; + bret = GetThreadContext( thread, ctx ); + ok( bret, "got error %lu.\n", GetLastError() ); + ok( ctx->MxCsr == 0x1f81, "got %#lx.\n", ctx->MxCsr ); + ok( xs->MxCsr == 0x1f81, "got %#lx.\n", ctx->MxCsr ); + + ResumeThread( thread ); + WaitForSingleObject( thread, INFINITE ); + CloseHandle( thread ); + + handler = AddVectoredExceptionHandler( TRUE, test_set_context_mxcsr_handler ); + + memcpy( code_mem, func, sizeof(func) ); + func_ptr = code_mem; + + set_context_mxcsr_test_ctx_flags = 0; + mxcsr = 0x1f85; + func_ptr( &mxcsr ); + todo_wine ok( mxcsr == 0x1f81, "got %#lx.\n", mxcsr ); + + set_context_mxcsr_test_ctx_flags &= ~CONTEXT_FLOATING_POINT | CONTEXT_AMD64; + mxcsr = 0x1f85; + func_ptr( &mxcsr ); + ok( mxcsr == 0x1f85, "got %#lx.\n", mxcsr ); + + RemoveVectoredExceptionHandler( handler ); +} + #elif defined(__arm__) static void test_thread_context(void) @@ -12771,6 +12919,7 @@ START_TEST(exception) test_single_step_address(); test_base_init_thunk_unwind(); test_backtrace_without_runtime_function(); + test_set_context_mxcsr(); #elif defined(__aarch64__) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10248
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/ntdll/tests/exception.c | 14 +++----------- dlls/ntdll/unix/signal_x86_64.c | 1 + dlls/ntdll/unix/thread.c | 1 + 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index dfc4985cf85..b114b82cd4b 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -6552,16 +6552,8 @@ static void test_set_context_mxcsr(void) ctx->ContextFlags = CONTEXT_ALL | CONTEXT_XSTATE; bret = GetThreadContext( thread, ctx ); ok( bret, "got error %lu.\n", GetLastError() ); - todo_wine ok( ctx->MxCsr == 0x1f81, "got %#lx.\n", ctx->MxCsr ); - todo_wine ok( xs->MxCsr == 0x1f81, "got %#lx.\n", ctx->MxCsr ); - - if (ctx->MxCsr == 0x1f82) - { - ctx->ContextFlags = CONTEXT_FLOATING_POINT; - xs->MxCsr = 0x1f81; - bret = SetThreadContext( thread, ctx ); - ok( bret, "got error %lu.\n", GetLastError() ); - } + ok( ctx->MxCsr == 0x1f81, "got %#lx.\n", ctx->MxCsr ); + ok( xs->MxCsr == 0x1f81, "got %#lx.\n", ctx->MxCsr ); ctx->MxCsr = 0x1f83; xs->MxCsr = 0x1f84; @@ -6611,7 +6603,7 @@ static void test_set_context_mxcsr(void) set_context_mxcsr_test_ctx_flags = 0; mxcsr = 0x1f85; func_ptr( &mxcsr ); - todo_wine ok( mxcsr == 0x1f81, "got %#lx.\n", mxcsr ); + ok( mxcsr == 0x1f81, "got %#lx.\n", mxcsr ); set_context_mxcsr_test_ctx_flags &= ~CONTEXT_FLOATING_POINT | CONTEXT_AMD64; mxcsr = 0x1f85; diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 0c62e8a1ad4..8f6f1908ae4 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1189,6 +1189,7 @@ NTSTATUS WINAPI NtSetContextThread( HANDLE handle, const CONTEXT *context ) if (flags & CONTEXT_FLOATING_POINT) { frame->xsave = context->FltSave; + frame->xsave.MxCsr = context->MxCsr; frame->xstate.Mask |= XSTATE_MASK_LEGACY; } if (flags & CONTEXT_XSTATE) diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index cba34ed8ca7..dafc6ae6079 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -448,6 +448,7 @@ static NTSTATUS context_to_server( struct context_data *to, USHORT to_machine, c { to->flags |= SERVER_CTX_FLOATING_POINT; memcpy( to->fp.x86_64_regs.fpregs, &from->FltSave, sizeof(to->fp.x86_64_regs.fpregs) ); + ((XSAVE_FORMAT *)to->fp.x86_64_regs.fpregs)->MxCsr = from->MxCsr; } if (flags & CONTEXT_AMD64_DEBUG_REGISTERS) { -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10248
OpenJDK JVM runtime wants to comapre MxCsr with the initial one and restore the initial if that differs by setting CONTEXT.MxCsr (in exception handler). We currently prefer MxCsr from CONTEXT.FltSave but turns out that is not the case on Windows. Failure to actually update MxCsr from exception context when continuing execution causes inifinite exception loop. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10248#note_131376
On Thu Mar 5 19:56:50 2026 +0000, Paul Gofman wrote:
OpenJDK JVM runtime wants to comapre MxCsr with the initial one and restore the initial if that differs by setting CONTEXT.MxCsr (in exception handler). We currently prefer MxCsr from CONTEXT.FltSave but turns out that is not the case on Windows. Failure to actually update MxCsr from exception context when continuing execution causes inifinite exception loop. Can you add this to the commit msg? You can edit it with this
{width=900 height=267} -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10248#note_131381
We rarely go for long commit messages, and certainly not for describing such details of functioning of the software which is not Wine. Maybe there can be an exception when a commit is introducing something weird looking, but I think it is not the case, the fix itself looks rather straightforward and is backed by tests. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10248#note_131384
participants (3)
-
Paul Gofman -
Paul Gofman (@gofman) -
Vishnunithyasoundhar S (@svishnunithyasoundhar)