Конфигурирование контекста модуля с плавающей запятой в 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;
}