[PATCH v7 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 -- v7: ntdll/tests: Add SIMD exception test for floating point overflow operation fault. ntdll: Complete handling of exception code setup for x86 and wow64 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 | 29 ++++++++++++++++++++--------- dlls/ntdll/unix/signal_x86_64.c | 22 +++++++++++++++++++++- 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index 738e7169cad..58fe4883366 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -2110,16 +2110,27 @@ 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; rec.ExceptionInformation[rec.NumberParameters++] = 0; - if (is_old_wow64()) rec.ExceptionInformation[rec.NumberParameters++] = ((XSAVE_FORMAT *)xcontext.c.ExtendedRegisters)->MxCsr; + if (is_old_wow64()) + rec.ExceptionInformation[rec.NumberParameters++] = ((XSAVE_FORMAT *)xcontext.c.ExtendedRegisters)->MxCsr; + 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; + } break; default: WINE_ERR( "Got unexpected trap %d\n", TRAP_sig(sigcontext) ); diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index e0eddb83e9a..0dc5fa9e4ad 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -2459,7 +2459,27 @@ static void fpe_handler( int signal, siginfo_t *siginfo, void *_sigcontext ) rec.NumberParameters = 2; rec.ExceptionInformation[0] = 0; rec.ExceptionInformation[1] = context.c.FltSave.MxCsr; - if (CS_sig(sigcontext) != cs64_sel) rec.ExceptionCode = STATUS_FLOAT_MULTIPLE_TRAPS; + if (CS_sig(sigcontext) != cs64_sel) + { + 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; + } + } } setup_raise_exception( data, sigcontext, &rec, &context ); } -- 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
participants (2)
-
Ralf Habacker -
Ralf Habacker (@rhabacker)