Конфигурирование контекста модуля с плавающей запятой в WIN32 против WIN64

Я пытаюсь написать необработанный фильтр исключений (см. 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#####!
...

Похоже, это указывает на то, что мне не удалось настроить модуль с плавающей запятой, который, как я полагаю, связан с различным определением ContextRecord между WIN32 и WIN64, но я не смог найти какую-либо хорошую документацию о том, как именно установить контекст с плавающей запятой.

Любые мысли о том, как правильно установить контекст с плавающей запятой для WIN64?

заранее спасибо

1 ответ

Решение

У меня есть рабочий пример сейчас. Спасибо @IInspectable за то, что указал мне правильное направление (SSE). Проблема заключалась в том, что регистр (ы?) MxCsr должен быть установлен в ExecutionContext. Обратите внимание, что вызов 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;
}
Другие вопросы по тегам