Configuring floating point unit context in WIN32 vs WIN64

空扰寡人 提交于 2019-12-11 04:43:07

问题


I am attempting to write an unhandled exception filter ( see SetUnhandledExceptionFilter()) to use with Windows SEH to report invalid floating point operations. I'd like to trap the exception, print a stack trace, then disable floating point exceptions and resume execution with the resulting non-finite or not-a-number value.

I wrote the simple program below to demonstrate the ability to catch the exception and resume execution. Note the use of #ifdef _WIN64, as the definition of the ContextRecord changes depending on the targeted architecture (WIN32 uses "FloatSave", WIN64 uses "FltSave").

#include "stdafx.h"
#include <float.h>
#include <Windows.h>

double zero = 0.0;
LONG WINAPI myfunc(EXCEPTION_POINTERS * ExceptionInfo){
    /* clear the exception */
    unsigned int stat = _clear87();

    /* disable fp exceptions*/
    unsigned int ctrl1 = _control87(_MCW_EM, _MCW_EM);

    /* Disable and clear fp exceptions in the exception context */
    #if _WIN64
        ExceptionInfo->ContextRecord->FltSave.ControlWord = ctrl1;
        ExceptionInfo->ContextRecord->FltSave.StatusWord = 0;
    #else
        ExceptionInfo->ContextRecord->FloatSave.ControlWord = ctrl1;
        ExceptionInfo->ContextRecord->FloatSave.StatusWord = 0;
    #endif

    printf("#########Caught Ya#####!\n");

    return EXCEPTION_CONTINUE_EXECUTION;
}


int _tmain(int argc, _TCHAR* argv[])
{
    double a;

    /* enable fp exceptions*/
    _controlfp(0, _MCW_EM);

    /* Setup our unhandled exception filter */
    SetUnhandledExceptionFilter(myfunc);

    /* do something bad */
    a = 5.0 / zero;

    printf("a = %f\n",a);

    return 0;
}

When run as a WIN32 .exe, the above program runs as expected, with the output:

#########Caught Ya#####!
a = -1.#IND00

However, when run as a WIN64 .exe, the program enters an infinite loop, continuously re-catching the floating point exception:

#########Caught Ya#####!
#########Caught Ya#####!
#########Caught Ya#####!
#########Caught Ya#####!
#########Caught Ya#####!
#########Caught Ya#####!
...

This seems to indicate that I'm not successful at configuring the floating point unit, which I imagine is related to the different definition of ContextRecord between WIN32 and WIN64, but I haven't been able to find any good related documentation on exactly how to set the floating point context.

Any thoughts on how to correctly set the floating point context for WIN64?

Thanks in advance


回答1:


I've got a working example now. Thanks to @IInspectable for pointing me in the right direction (SSE). The issue was that the MxCsr register(s?) need to be set in the ExecutionContext. Note that calling controlfp DID indeed set the MxCsr register correctly, however it appears that when the filter function returns, ALL registers are reset to the context given in ExceptionInfo. The trick is to overwrite MxCsr in that context, which the code below does.

I am still a bit confused as to the two locations of MxCsr in the context. controlfp() seems to set BOTH together and to the same value (as observed in debugger).

#include "stdafx.h"
#include <float.h>
#include <Windows.h>
#include <xmmintrin.h>

double zero = 0.0;
LONG WINAPI myfunc(EXCEPTION_POINTERS * ExceptionInfo){
    /* clear the exception */
    unsigned int stat = _clearfp();

    /* disable all fp exceptions*/
    unsigned int ctrlwrd;
    errno_t err =  _controlfp_s(&ctrlwrd, _MCW_EM, _MCW_EM);

    /* Disable and clear the exceptions in the exception context */
    #if _WIN64
        /* Get current context to get the values of MxCsr register, which was
         * set by the calls to _controlfp above, we need to copy these into
         * the exception context so that exceptions really stay disabled.
         * References:
         *    https://msdn.microsoft.com/en-us/library/yxty7t75.aspx
         *    https://software.intel.com/en-us/articles/x87-and-sse-floating-point-assists-in-ia-32-flush-to-zero-ftz-and-denormals-are-zero-daz
         */
        CONTEXT myContext;
        RtlCaptureContext(&myContext);

        ExceptionInfo->ContextRecord->FltSave.ControlWord = ctrlwrd;
        ExceptionInfo->ContextRecord->FltSave.StatusWord = 0;
        ExceptionInfo->ContextRecord->FltSave.MxCsr = myContext.FltSave.MxCsr;
        ExceptionInfo->ContextRecord->FltSave.MxCsr_Mask = myContext.FltSave.MxCsr_Mask;
        ExceptionInfo->ContextRecord->MxCsr = myContext.MxCsr;
    #else
        ExceptionInfo->ContextRecord->FloatSave.ControlWord = ctrlwrd;
        ExceptionInfo->ContextRecord->FloatSave.StatusWord = 0;
    #endif

    printf("#########Caught Ya#####!\n");

    return EXCEPTION_CONTINUE_EXECUTION;
}

int _tmain(int argc, _TCHAR* argv[])
{
    double a;

    /* Enable fp exceptions */
    _controlfp_s(0, 0, _MCW_EM);

    /* Setup our unhandled exception filter */
    SetUnhandledExceptionFilter(myfunc);

    /* do something bad */
    a = 5.0 / zero;

    printf("a = %f\n",a);
    return 0;
}


来源:https://stackoverflow.com/questions/42590408/configuring-floating-point-unit-context-in-win32-vs-win64

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!