在 WIN32 与 WIN64 中配置浮点单元上下文
Configuring floating point unit context in WIN32 vs 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时,上面的程序运行s如预期,输出:
#########Caught Ya#####!
a = -1.#IND00
但是,当运行为WIN64.exe时,程序进入死循环,不断重新捕获浮点数异常:
#########Caught Ya#####!
#########Caught Ya#####!
#########Caught Ya#####!
#########Caught Ya#####!
#########Caught Ya#####!
#########Caught Ya#####!
...
这似乎表明我没有配置浮点单元,我想这与WIN32和WIN64之间的ContextRecord定义不同有关,但我一直没有找到任何好的相关关于如何设置浮点上下文的文档。
关于如何为 WIN64 正确设置浮点上下文有什么想法吗?
提前致谢
我现在有一个工作示例。感谢@IInspectable 为我指明了正确的方向 (SSE)。问题是需要在 ExecutionContext 中设置 MxCsr 寄存器。请注意,调用 controlfp DID 确实正确设置了 MxCsr 寄存器,但是似乎当过滤器函数 returns 时,所有寄存器都重置为 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;
}
我正在尝试编写一个未处理的异常过滤器(请参阅 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时,上面的程序运行s如预期,输出:
#########Caught Ya#####!
a = -1.#IND00
但是,当运行为WIN64.exe时,程序进入死循环,不断重新捕获浮点数异常:
#########Caught Ya#####!
#########Caught Ya#####!
#########Caught Ya#####!
#########Caught Ya#####!
#########Caught Ya#####!
#########Caught Ya#####!
...
这似乎表明我没有配置浮点单元,我想这与WIN32和WIN64之间的ContextRecord定义不同有关,但我一直没有找到任何好的相关关于如何设置浮点上下文的文档。
关于如何为 WIN64 正确设置浮点上下文有什么想法吗?
提前致谢
我现在有一个工作示例。感谢@IInspectable 为我指明了正确的方向 (SSE)。问题是需要在 ExecutionContext 中设置 MxCsr 寄存器。请注意,调用 controlfp DID 确实正确设置了 MxCsr 寄存器,但是似乎当过滤器函数 returns 时,所有寄存器都重置为 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;
}