[PATCH v4 0/2] MR11465: ntdll: Complete handling of exception code setup for x86
With the changes from this commit, the exception code is now correctly returned in the `EXCEPTION_RECORD` structure for previously unhandled cases in which the following messages were displayed: ``` 00f4:fixme:seh:fpe_handler untested SIMD exception: 0x4. Might not work correctly 00f4:fixme:seh:fpe_handler untested SIMD exception: 0x5. Might not work correctly 00f4:fixme:seh:fpe_handler untested SIMD exception: 0x6. Might not work correctly ``` Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=56213 -- v4: ntdll/tests: Add SIMD exception test for floating point overflow operation fault. https://gitlab.winehq.org/wine/wine/-/merge_requests/11465
From: Ralf Habacker <ralf.habacker@freenet.de> With the changes from this commit, the exception code is now correctly returned in the `EXCEPTION_RECORD` structure for previously unhandled cases in which the following messages were displayed: 00f4:fixme:seh:fpe_handler untested SIMD exception: 0x4. Might not work correctly 00f4:fixme:seh:fpe_handler untested SIMD exception: 0x5. Might not work correctly 00f4:fixme:seh:fpe_handler untested SIMD exception: 0x6. Might not work correctly Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=56213 --- dlls/ntdll/unix/signal_i386.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index 738e7169cad..fe018b53e51 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -2110,14 +2110,24 @@ static void fpe_handler( int signal, siginfo_t *siginfo, void *_sigcontext ) rec.ExceptionAddress = (void *)xcontext.c.FloatSave.ErrorOffset; break; case TRAP_x86_CACHEFLT: /* SIMD exception */ - /* TODO: - * Behaviour only tested for divide-by-zero exceptions - * Check for other SIMD exceptions as well */ - if(siginfo->si_code != FPE_FLTDIV && siginfo->si_code != FPE_FLTINV) - FIXME("untested SIMD exception: %#x. Might not work correctly\n", - siginfo->si_code); - - rec.ExceptionCode = STATUS_FLOAT_MULTIPLE_TRAPS; + switch (siginfo->si_code) + { + case FPE_FLTDIV: + case FPE_FLTINV: + rec.ExceptionCode = STATUS_FLOAT_MULTIPLE_TRAPS; + break; + + case FPE_FLTOVF: + case FPE_FLTUND: + case FPE_FLTRES: + rec.ExceptionCode = STATUS_FLOAT_MULTIPLE_FAULTS; + break; + + default: + FIXME("unknown SIMD exception: %#x\n", siginfo->si_code); + rec.ExceptionCode = STATUS_FLOAT_MULTIPLE_TRAPS; + break; + } rec.ExceptionInformation[rec.NumberParameters++] = 0; if (is_old_wow64()) rec.ExceptionInformation[rec.NumberParameters++] = ((XSAVE_FORMAT *)xcontext.c.ExtendedRegisters)->MxCsr; break; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11465
From: Ralf Habacker <ralf.habacker@freenet.de> Signed-off-by: Ralf Habacker <ralf.habacker@freenet.de> --- dlls/ntdll/tests/exception.c | 167 +++++++++++++++++++++++++---------- 1 file changed, 122 insertions(+), 45 deletions(-) diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index abeae85a068..0931b53dc36 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -1563,34 +1563,47 @@ static DWORD simd_fault_handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_R CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher ) { int *stage = *(int **)(frame + 1); + DWORD expected; got_exception++; - if( *stage == 1) { - /* fault while executing sse instruction */ - context->Eip += 3; /* skip addps */ - return ExceptionContinueExecution; + switch(*stage) + { + case 1: /* fault while executing sse instruction */ + context->Eip += 3; /* skip addps */ + return ExceptionContinueExecution; + + case 2: /* divide by zero */ + case 3: /* invalid operation */ + expected = STATUS_FLOAT_MULTIPLE_TRAPS; + context->Eip += 3; /* skip instruction */ + goto check_exception; + + case 4: /* overflow */ + expected = STATUS_FLOAT_MULTIPLE_FAULTS; + context->Eip += 3; /* skip instruction */ + goto check_exception; + + default: + ok(FALSE, "unexpected stage %d\n", *stage); + return ExceptionContinueExecution; } - else if ( *stage == 2 || *stage == 3 ) { - /* stage 2 - divide by zero fault */ - /* stage 3 - invalid operation fault */ - if( rec->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION) - skip("system doesn't support SIMD exceptions\n"); - else { - ok( rec->ExceptionCode == STATUS_FLOAT_MULTIPLE_TRAPS, - "exception code: %#lx, should be %#lx\n", - rec->ExceptionCode, STATUS_FLOAT_MULTIPLE_TRAPS); - ok( rec->NumberParameters == is_wow64 ? 2 : 1, "# of params: %li\n", rec->NumberParameters); - ok( rec->ExceptionInformation[0] == 0, "param #1: %Ix, should be 0\n", rec->ExceptionInformation[0]); - if (rec->NumberParameters == 2) - ok( rec->ExceptionInformation[1] == ((XSAVE_FORMAT *)context->ExtendedRegisters)->MxCsr, - "param #1: %Ix / %lx\n", rec->ExceptionInformation[1], - ((XSAVE_FORMAT *)context->ExtendedRegisters)->MxCsr); - } - context->Eip += 3; /* skip divps */ + +check_exception: + if( rec->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION) + { + skip("system doesn't support SIMD exceptions\n"); + return ExceptionContinueExecution; } - else - ok(FALSE, "unexpected stage %x\n", *stage); + + ok( rec->ExceptionCode == expected, "exception code: %#lx, should be %#lx\n", + rec->ExceptionCode, expected); + ok( rec->NumberParameters == is_wow64 ? 2 : 1, "# of params: %li\n", rec->NumberParameters); + ok( rec->ExceptionInformation[0] == 0, "param #0: %Ix\n", rec->ExceptionInformation[0]); + if (rec->NumberParameters == 2) + ok( rec->ExceptionInformation[1] == ((XSAVE_FORMAT *)context->ExtendedRegisters)->MxCsr, + "param #1: %Ix / %lx\n", rec->ExceptionInformation[1], + ((XSAVE_FORMAT *)context->ExtendedRegisters)->MxCsr); return ExceptionContinueExecution; } @@ -1630,6 +1643,25 @@ static const BYTE simd_exception_test2[] = { 0xc3, /* ret */ }; +static const BYTE simd_exception_test3[] = { + 0x83, 0xec, 0x04, /* sub $4,%esp */ + 0x0f, 0xae, 0x1c, 0x24, /* stmxcsr (%esp) */ + 0x8b, 0x04, 0x24, /* mov (%esp),%eax * store mxcsr */ + 0x66, 0x81, 0x24, 0x24, 0xff, 0xfb, /* andw $0xfbff,(%esp) * enable overflow */ + 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%esp) * operation exceptions */ + 0x68, 0xff, 0xff, 0x7f, 0x7f, /* push 0x7f7fffff * load large float values */ + 0x68, 0xff, 0xff, 0x7f, 0x7f, + 0x68, 0xff, 0xff, 0x7f, 0x7f, + 0x68, 0xff, 0xff, 0x7f, 0x7f, + 0x0f, 0x10, 0x0c, 0x24, /* movups (%esp),%xmm1 */ + 0x0f, 0x59, 0xc9, /* mulps %xmm1,%xmm1 * generate overflow fault */ + 0x83, 0xc4, 0x10, /* add $16,%esp * pop float value */ + 0x89, 0x04, 0x24, /* mov %eax,(%esp) * restore to old mxcsr */ + 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%esp) */ + 0x83, 0xc4, 0x04, /* add $4,%esp */ + 0xc3 /* ret */ +}; + static const BYTE sse_check[] = { 0x0f, 0x58, 0xc8, /* addps %xmm0,%xmm1 */ 0xc3, /* ret */ @@ -1661,6 +1693,13 @@ static void test_simd_exceptions(void) run_exception_test(simd_fault_handler, &stage, simd_exception_test2, sizeof(simd_exception_test2), 0); ok(got_exception == 1, "got exception: %i, should be 1\n", got_exception); + + /* generate a SIMD overflow exception */ + stage = 4; + got_exception = 0; + run_exception_test(simd_fault_handler, &stage, simd_exception_test3, + sizeof(simd_exception_test3), 0); + ok(got_exception == 1, "got exception: %i, should be 1\n", got_exception); } struct fpu_exception_info @@ -3442,35 +3481,49 @@ static DWORD WINAPI simd_fault_handler( EXCEPTION_RECORD *rec, ULONG64 frame, CONTEXT *context, DISPATCHER_CONTEXT *dispatcher ) { int *stage = *(int **)dispatcher->HandlerData; + DWORD expected; got_exception++; - if (*stage == 1) + switch(*stage) { - /* fault while executing sse instruction */ - context->Rip += 3; /* skip addps */ - return ExceptionContinueExecution; + case 1: /* fault while executing sse instruction */ + context->Rip += 3; /* skip addps */ + return ExceptionContinueExecution; + + case 2: /* divide by zero */ + expected = STATUS_FLOAT_DIVIDE_BY_ZERO; + context->Rip += 3; /* skip instruction */ + goto check_exception; + + case 3: /* invalid operation */ + expected = STATUS_FLOAT_INVALID_OPERATION; + context->Rip += 3; /* skip instruction */ + goto check_exception; + + case 4: /* overflow */ + expected = STATUS_FLOAT_OVERFLOW; + context->Rip += 3; /* skip instruction */ + goto check_exception; + + default: + ok(FALSE, "unexpected stage %d\n", *stage); + return ExceptionContinueExecution; } - else if (*stage == 2 || *stage == 3 ) + +check_exception: + if( rec->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION) { - /* stage 2 - divide by zero fault */ - /* stage 3 - invalid operation fault */ - if( rec->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION) - skip("system doesn't support SIMD exceptions\n"); - else - { - ULONG expect = *stage == 2 ? EXCEPTION_FLT_DIVIDE_BY_ZERO : EXCEPTION_FLT_INVALID_OPERATION; - ok( rec->ExceptionCode == expect, "exception code: %#lx, should be %#lx\n", - rec->ExceptionCode, expect ); - ok( rec->NumberParameters == 2, "# of params: %li, should be 2\n", rec->NumberParameters); - ok( rec->ExceptionInformation[0] == 0, "param #0: %Ix\n", rec->ExceptionInformation[0]); - ok( rec->ExceptionInformation[1] == context->MxCsr, "param #1: %Ix / %lx\n", - rec->ExceptionInformation[1], context->MxCsr); - } - context->Rip += 3; /* skip divps */ + skip("system doesn't support SIMD exceptions\n"); + return ExceptionContinueExecution; } - else - ok(FALSE, "unexpected stage %x\n", *stage); + + ok( rec->ExceptionCode == expected, "exception code: %#lx, should be %#lx\n", + rec->ExceptionCode, expected); + ok( rec->NumberParameters == 2, "# of params: %li, should be 2\n", rec->NumberParameters); + ok( rec->ExceptionInformation[0] == 0, "param #0: %Ix\n", rec->ExceptionInformation[0]); + ok( rec->ExceptionInformation[1] == context->MxCsr, "param #1: %Ix / %lx\n", + rec->ExceptionInformation[1], context->MxCsr); return ExceptionContinueExecution; } @@ -3508,6 +3561,23 @@ static const BYTE simd_exception_test2[] = 0xc3, /* ret */ }; +static const BYTE simd_exception_test3[] = +{ + 0x48, 0x83, 0xec, 0x08, /* sub $0x8,%rsp */ + 0x0f, 0xae, 0x1c, 0x24, /* stmxcsr (%rsp) */ + 0x8b, 0x04, 0x24, /* mov (%rsp),%eax * store mxcsr */ + 0x66, 0x81, 0x24, 0x24, 0xff, 0xfb, /* andw $0xfbff,(%rsp) * unmask overflow exception */ + 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%rsp) * zero exceptions */ + 0xb9, 0xff, 0xff, 0x7f, 0x7f, /* mov $0x7f7fffff,%ecx * load large number */ + 0x66, 0x0f, 0x6e, 0xc9, /* movd %ecx,%xmm1 * transfer to sse register */ + 0x0f, 0xc6, 0xc9, 0x00, /* shufps $0,%xmm1,%xmm1 * replicate to all 4 lanes */ + 0x0f, 0x59, 0xc9, /* mulps %xmm1,%xmm1 * generate overflow fault */ + 0x89, 0x04, 0x24, /* mov %eax,(%rsp) * restore to old mxcsr */ + 0x0f, 0xae, 0x14, 0x24, /* ldmxcsr (%rsp) */ + 0x48, 0x83, 0xc4, 0x08, /* add $0x8,%rsp */ + 0xc3, /* ret */ +}; + static const BYTE sse_check[] = { 0x0f, 0x58, 0xc8, /* addps %xmm0,%xmm1 */ @@ -3540,6 +3610,13 @@ static void test_simd_exceptions(void) run_exception_test(simd_fault_handler, &stage, simd_exception_test2, sizeof(simd_exception_test2), 0); ok(got_exception == 1, "got exception: %i, should be 1\n", got_exception); + + /* generate a SIMD overflow exception */ + stage = 4; + got_exception = 0; + run_exception_test(simd_fault_handler, &stage, simd_exception_test3, + sizeof(simd_exception_test3), 0); + ok(got_exception == 1, "got exception: %i, should be 1\n", got_exception); } static void test_prot_fault(void) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11465
Now that the issue reported at https://gitlab.winehq.org/rhabacker/wine/-/jobs/291759 ``` exception.c:3521:0.000 Test failed: exception code: 0xc000008e, should be 0xc00002b5 exception.c:3521:0.000 Test failed: exception code: 0xc0000090, should be 0xc00002b5 exception.c:3521:0.000 Test failed: exception code: 0xc0000091, should be 0xc00002b4 ``` has been resolved, the test has completed successfully ``` 2026-08-03T10:40:40.453673Z 01O ntdll:exception start dlls/ntdll/tests/exception.c 2026-08-03T10:40:42.035111Z 01O ntdll:exception:07d8 done (0) in 1s 7867B ``` The tests that have now failed are unrelated to the changes in this merge request. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11465#note_147681
On Mon Aug 3 21:11:15 2026 +0000, Jinoh Kang wrote:
Thanks for your contribution! Could you add some tests? Done.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/11465#note_147756
On Mon Aug 3 21:14:28 2026 +0000, Ralf Habacker wrote:
Done. A reliable test for checking overflow exception has been added.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/11465#note_147757
participants (2)
-
Ralf Habacker -
Ralf Habacker (@rhabacker)