1

我正在尝试编写一个未处理的异常过滤器(请参阅 SetUnhandledExceptionFilter())以与 Windows SEH 一起使用以报告无效的浮点操作。我想捕获异常,打印堆栈跟踪,然后禁用浮点异常并使用生成的非有限或非数字值恢复执行。

我编写了下面的简单程序来演示捕获异常并恢复执行的能力。注意#ifdef _WIN64 的使用,因为 ContextRecord 的定义根据目标体系结构而变化(WIN32 使用“FloatSave”,WIN64 使用“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;
}

当作为 WIN32 .exe 运行时,上述程序按预期运行,输出:

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

但是,当以 WIN64 .exe 运行时,程序进入死循环,不断重新捕获浮点异常:

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

这似乎表明我没有成功配置浮点单元,我想这与 WIN32 和 WIN64 之间 ContextRecord 的不同定义有关,但我还没有找到任何好的相关文档来确切说明如何设置浮点上下文。

关于如何正确设置 WIN64 的浮点上下文的任何想法?

提前致谢

4

1 回答 1

0

我现在有一个工作示例。感谢@IInspectable 为我指明了正确的方向(SSE)。问题是需要在 ExecutionContext 中设置 MxCsr 寄存器。请注意,调用 controlfp DID 确实正确设置了 MxCsr 寄存器,但似乎当过滤器函数返回时,所有寄存器都重置为 ExceptionInfo 中给出的上下文。诀窍是在该上下文中覆盖 MxCsr,下面的代码就是这样做的。

对于上下文中 MxCsr 的两个位置,我仍然有些困惑。controlfp() 似乎将两者设置在一起并设置为相同的值(如在调试器中观察到的)。

#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;
}
于 2017-03-05T19:26:58.010 回答