2017-03-03 24 views
1

我正在嘗試編寫一個未處理的異常過濾器(請參閱SetUnhandledExceptionFilter())以與Windows SEH配合使用來報告無效的浮點操作。我想捕捉異常,打印堆棧跟蹤,然後禁用浮點異常,並重新執行,生成非有限或非數值的值。在WIN32和WIN64中配置浮點單元上下文

我寫了下面的簡單程序來演示捕捉異常並恢復執行的能力。注意使用#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#####! 
... 

這似乎表明,我不是成功的配置浮點單元,這是我想象有關的Contex的不同定義在WIN32和WIN64之間的tRecord,但我一直無法找到任何關於如何設置浮點上下文的相關文檔。

關於如何正確設置WIN64的浮點上下文的任何想法?

在此先感謝

+2

默認情況下,MSC總是編譯浮點指令以將SSE單元用於64位代碼。根據您運行的CPU,_control87'調用可能不會爲SSE單元設置相應的MXCSR標誌。 [本問答]中的一些更多詳細信息(http://stackoverflow.com/q/20045968/1889329)。 – IInspectable

+0

@IInspectable,謝謝你的回覆。事實上,在64位平臺上,MXCSR顯示在ContextRecord下:ContextRecord-> FltSave.MxCsr; 'ContextRecord-> FltSave.MxCsr_Mask';最後是'ContextRecord-> MxCsr'。我想我已經讀過MXCSR與_control87具有相同的定義,所以我嘗試將上述上下文記錄的各種組合分配給我的ctrl1變量,但是迄今爲止沒有成功 – jHops

+0

我想我現在看到現在發生了什麼:使用FPU代碼,當操作失敗時,它將設置異常標誌,但僅在** next ** FPU指令中引發浮點異常。由於你的異常過濾器指示控制流返回到觸發異常的操作碼(可能是'FST',而不是實際的分區),異常位被清除,它將繼續執行。有了SSE代碼,我相信這個例外是由失敗的操作碼引起的,在您的案例中這個分支。該指令一遍又一遍地執行。通過組裝來驗證這是否正確。 – IInspectable

回答

0

我現在有一個工作示例。感謝@IInspectable指引我在正確的方向(SSE)。問題是需要在ExecutionContext中設置MxCs寄存器(s?)。請注意,調用controlfp DID確實正確設置了MxCsr寄存器,但看起來當過濾函數返回時,所有寄存器都將重置爲ExceptionInfo中給出的上下文。訣竅就是在下面的代碼中覆蓋MxCsr。

對於上下文中MxCsr的兩個位置,我還是有點困惑。 controlfp()似乎將BOTH一起設置爲相同的值(如在調試器中所觀察到的)。

#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; 
}