2017-03-06 10 views
1

浮動小数点例外をキャッチし、スタックトレースを出力し、浮動小数点例外を無効にして実行を再開する手段を提供します(得られた非有限/非数の値を使用して)。私はoriginal questionから少し進歩しました。SSE(デフォルトはx64)を使用して浮動小数点ユニットをクリア/設定するために調整が必要なレジスタが増えていることに気付きました。例外を処理した後のコンテキストの回復は、VS/RTC(ランタイムチェック)でのみ有効です

私は非常に簡単な例を得ましたが、リリースビルドに行くと、x64のために物事が崩壊しています。デバッグ/リリースビルドは、x86ターゲットに対してうまく機能します。私は"Run-Time-Checks" option of Visual-Studioに絞っています。具体的には、「スタックフレームランタイムエラーチェックを有効にする」RTCです。ここで

はプログラム例です:ENABLEDのRTCで

#include "stdafx.h" 
#include <float.h> 
#include <Windows.h> 
#include <xmmintrin.h> 

double zero = 0.0; 

void printException(EXCEPTION_POINTERS * ExceptionInfo){ 
    bool bFloatingPointRecoverFlag = false; 
    switch(ExceptionInfo->ExceptionRecord->ExceptionCode) 
    { 
     case EXCEPTION_ACCESS_VIOLATION: 
      fputs(" EXCEPTION_ACCESS_VIOLATION\n", stderr); 
      break; 
     case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: 
      fputs(" EXCEPTION_ARRAY_BOUNDS_EXCEEDED\n", stderr); 
      break; 
     case EXCEPTION_BREAKPOINT: 
      fputs(" EXCEPTION_BREAKPOINT\n", stderr); 
      break; 
     case EXCEPTION_DATATYPE_MISALIGNMENT: 
      fputs(" EXCEPTION_DATATYPE_MISALIGNMENT\n", stderr); 
      break; 
     case EXCEPTION_FLT_DENORMAL_OPERAND: 
      fputs(" EXCEPTION_FLT_DENORMAL_OPERAND\n", stderr); 
      break; 
     case EXCEPTION_FLT_DIVIDE_BY_ZERO: 
      fputs(" EXCEPTION_FLT_DIVIDE_BY_ZERO\n", stderr); 
      break; 
     case EXCEPTION_FLT_INEXACT_RESULT: 
      fputs(" EXCEPTION_FLT_INEXACT_RESULT\n", stderr); 
      break; 
     case EXCEPTION_FLT_INVALID_OPERATION: 
      fputs(" EXCEPTION_FLT_INVALID_OPERATION\n", stderr); 
      break; 
     case EXCEPTION_FLT_OVERFLOW: 
      fputs(" EXCEPTION_FLT_OVERFLOW\n", stderr); 
      break; 
     case EXCEPTION_FLT_STACK_CHECK: 
      fputs(" EXCEPTION_FLT_STACK_CHECK\n", stderr); 
      break; 
     case EXCEPTION_FLT_UNDERFLOW: 
      fputs(" EXCEPTION_FLT_UNDERFLOW\n", stderr); 
      bFloatingPointRecoverFlag = 1; 
      break; 
     case EXCEPTION_ILLEGAL_INSTRUCTION: 
      fputs(" EXCEPTION_ILLEGAL_INSTRUCTION\n", stderr); 
      break; 
     case EXCEPTION_IN_PAGE_ERROR: 
      fputs(" EXCEPTION_IN_PAGE_ERROR\n", stderr); 
      break; 
     case EXCEPTION_INT_DIVIDE_BY_ZERO: 
      fputs(" EXCEPTION_INT_DIVIDE_BY_ZERO\n", stderr); 
      break; 
     case EXCEPTION_INT_OVERFLOW: 
      fputs(" EXCEPTION_INT_OVERFLOW\n", stderr); 
      break; 
     case EXCEPTION_INVALID_DISPOSITION: 
      fputs(" EXCEPTION_INVALID_DISPOSITION\n", stderr); 
      break; 
     case EXCEPTION_NONCONTINUABLE_EXCEPTION: 
      fputs(" EXCEPTION_NONCONTINUABLE_EXCEPTION\n", stderr); 
      break; 
     case EXCEPTION_PRIV_INSTRUCTION: 
      fputs(" EXCEPTION_PRIV_INSTRUCTION\n", stderr); 
      break; 
     case EXCEPTION_SINGLE_STEP: 
      fputs(" EXCEPTION_SINGLE_STEP\n", stderr); 
      break; 
     case EXCEPTION_STACK_OVERFLOW: 
      fputs(" EXCEPTION_STACK_OVERFLOW\n", stderr); 
      break; 
     default: 
      fputs(" Unrecognized Exception\n", stderr); 
      break; 
    } 
} 

LONG WINAPI myfunc(EXCEPTION_POINTERS * ExceptionInfo){ 
    printf("#########Caught Ya:"); 
    printException(ExceptionInfo); 
    printf("ExceptionAddr = 0x%p\n",ExceptionInfo->ExceptionRecord->ExceptionAddress); 

    /* clear the exception */ 
    unsigned int stat = _clearfp(); 

    /* disable 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; 
     GetThreadContext(GetCurrentThread(),&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 

    return EXCEPTION_CONTINUE_EXECUTION; 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    double a; 
    double b; 
    double c; 
    double d; 
    double e; 

    /* do something so that zero can't get optimized */ 
    if(argc > 999999){ 
     zero = (double) argc; 
    } 


    /* Enable fp exceptions */ 
    _controlfp_s(0, 0, _MCW_EM); 

    /* Setup our unhandled exception filter */ 
    SetUnhandledExceptionFilter(myfunc); 

    b = 5.0+zero; 

    /* do something bad */ 
    a = 5.0/zero; 

    c = a * b; 

    e = 5.0/zero; 

    d = 4.0 + e; 

    printf("a = %f\n",a); 
    printf("b = %f\n",b); 
    printf("c = %f\n",c); 
    printf("d = %f\n",d); 
    printf("e = %f\n",e); 

    return 0; 
} 

、このコードは(私が期待するものである)出力を作成します。

#########Caught Ya: EXCEPTION_FLT_DIVIDE_BY_ZERO 
ExceptionAddr = 0x000000013F7A1638 
a = 1.#INF00 
b = 5.000000 
c = 1.#INF00 
d = 1.#INF00 
e = 1.#INF00 

のRTCを無効にして、このコードは出力を作成します。

#########Caught Ya: EXCEPTION_FLT_DIVIDE_BY_ZERO 
ExceptionAddr = 0x000000013F0415F2 
#########Caught Ya: EXCEPTION_ACCESS_VIOLATION 
ExceptionAddr = 0x000000007711B519 
#########Caught Ya: EXCEPTION_ACCESS_VIOLATION 
ExceptionAddr = 0x000000007711B519 
#########Caught Ya: EXCEPTION_ACCESS_VIOLATION 
ExceptionAddr = 0x000000007711B519 
.... repeat forever 

したがって、要約:

  • x86ターゲット(WIN32):いいえデバッグまたはリリースビルドで問題はありません!

  • (x64ターゲット):RTCが無効になっている場合にのみ、浮動小数点例外からの回復を試みた後にアクセス違反が発生します。

RTCの考え方、および浮動小数点例外からのリカバリの動作に影響する理由は何ですか?

編集: これをさらに組み立ててデバッグしました。違反は、フィルタ関数から復帰した後、ゼロ除算で復帰する前に発生します。以下は、アクセス違反に至るまでのアセンブリは、(組立の最後の行が犯人である)である:

000000007711B2EF mov   dword ptr [rcx+0F0h],edi 
000000007711B2F5 fxsave  [rcx+100h] 
000000007711B2FC movaps  xmmword ptr [rcx+1A0h],xmm0 
000000007711B303 movaps  xmmword ptr [rcx+1B0h],xmm1 
000000007711B30A movaps  xmmword ptr [rcx+1C0h],xmm2 
000000007711B311 movaps  xmmword ptr [rcx+1D0h],xmm3 
000000007711B318 movaps  xmmword ptr [rcx+1E0h],xmm4 
000000007711B31F movaps  xmmword ptr [rcx+1F0h],xmm5 
000000007711B326 movaps  xmmword ptr [rcx+200h],xmm6 
000000007711B32D movaps  xmmword ptr [rcx+210h],xmm7 
000000007711B334 movaps  xmmword ptr [rcx+220h],xmm8 
000000007711B33C movaps  xmmword ptr [rcx+230h],xmm9 
000000007711B344 movaps  xmmword ptr [rcx+240h],xmm10 
000000007711B34C movaps  xmmword ptr [rcx+250h],xmm11 
000000007711B354 movaps  xmmword ptr [rcx+260h],xmm12 
000000007711B35C movaps  xmmword ptr [rcx+270h],xmm13 
000000007711B364 movaps  xmmword ptr [rcx+280h],xmm14 
000000007711B36C movaps  xmmword ptr [rcx+290h],xmm15 
000000007711B374 stmxcsr  dword ptr [rcx+34h] 
000000007711B378 mov   rax,qword ptr [rsp+8] 
000000007711B37D mov   qword ptr [rcx+0F8h],rax 
000000007711B384 mov   eax,dword ptr [rsp] 
000000007711B387 mov   dword ptr [rcx+44h],eax 
000000007711B38A mov   dword ptr [rcx+30h],10000Fh 
000000007711B391 add   rsp,8 
000000007711B395 ret 
000000007711B396 int   3 
000000007711B397 int   3 
000000007711B398 int   3 
000000007711B399 int   3 
000000007711B39A int   3 
000000007711B39B int   3 
000000007711B39C nop   dword ptr [rax] 
000000007711B39F push  rbp 
000000007711B3A0 push  rsi 
000000007711B3A1 push  rdi 
000000007711B3A2 sub   rsp,30h 
000000007711B3A6 mov   rbp,rsp 
000000007711B3A9 test  rdx,rdx 
000000007711B3AC je   000000007711B4E4 
000000007711B3B2 cmp   dword ptr [rdx],80000029h 
000000007711B3B8 jne   000000007711B3C4 
000000007711B3BA cmp   dword ptr [rdx+18h],1 
000000007711B3BE jae   000000007711B634 
000000007711B3C4 cmp   dword ptr [rdx],80000026h 
000000007711B3CA jne   000000007711B4E4 
000000007711B3D0 mov   rax,qword ptr [rdx+20h] 
000000007711B3D4 mov   r8,qword ptr [rax+8] 
000000007711B3D8 mov   qword ptr [rcx+90h],r8 
000000007711B3DF mov   r8,qword ptr [rax+10h] 
000000007711B3E3 mov   qword ptr [rcx+98h],r8 
000000007711B3EA mov   r8,qword ptr [rax+18h] 
000000007711B3EE mov   qword ptr [rcx+0A0h],r8 
000000007711B3F5 mov   r8,qword ptr [rax+20h] 
000000007711B3F9 mov   qword ptr [rcx+0A8h],r8 
000000007711B400 mov   r8,qword ptr [rax+28h] 
000000007711B404 mov   qword ptr [rcx+0B0h],r8 
000000007711B40B mov   r8,qword ptr [rax+30h] 
000000007711B40F mov   qword ptr [rcx+0D8h],r8 
000000007711B416 mov   r8,qword ptr [rax+38h] 
000000007711B41A mov   qword ptr [rcx+0E0h],r8 
000000007711B421 mov   r8,qword ptr [rax+40h] 
000000007711B425 mov   qword ptr [rcx+0E8h],r8 
000000007711B42C mov   r8,qword ptr [rax+48h] 
000000007711B430 mov   qword ptr [rcx+0F0h],r8 
000000007711B437 mov   r8,qword ptr [rax+50h] 
000000007711B43B mov   qword ptr [rcx+0F8h],r8 
000000007711B442 mov   r8d,dword ptr [rax+58h] 
000000007711B446 mov   dword ptr [rcx+34h],r8d 
000000007711B44A mov   dword ptr [rcx+118h],r8d 
000000007711B451 mov   r8w,word ptr [rax+5Ch] 
000000007711B456 mov   word ptr [rcx+100h],r8w 
000000007711B45E movaps  xmm0,xmmword ptr [rax+60h] 
000000007711B462 movaps  xmmword ptr [rcx+200h],xmm0 
000000007711B469 movaps  xmm0,xmmword ptr [rax+70h] 
000000007711B46D movaps  xmmword ptr [rcx+210h],xmm0 
000000007711B474 movaps  xmm0,xmmword ptr [rax+80h] 
000000007711B47B movaps  xmmword ptr [rcx+220h],xmm0 
000000007711B482 movaps  xmm0,xmmword ptr [rax+90h] 
000000007711B489 movaps  xmmword ptr [rcx+230h],xmm0 
000000007711B490 movaps  xmm0,xmmword ptr [rax+0A0h] 
000000007711B497 movaps  xmmword ptr [rcx+240h],xmm0 
000000007711B49E movaps  xmm0,xmmword ptr [rax+0B0h] 
000000007711B4A5 movaps  xmmword ptr [rcx+250h],xmm0 
000000007711B4AC movaps  xmm0,xmmword ptr [rax+0C0h] 
000000007711B4B3 movaps  xmmword ptr [rcx+260h],xmm0 
000000007711B4BA movaps  xmm0,xmmword ptr [rax+0D0h] 
000000007711B4C1 movaps  xmmword ptr [rcx+270h],xmm0 
000000007711B4C8 movaps  xmm0,xmmword ptr [rax+0E0h] 
000000007711B4CF movaps  xmmword ptr [rcx+280h],xmm0 
000000007711B4D6 movaps  xmm0,xmmword ptr [rax+0F0h] 
000000007711B4DD movaps  xmmword ptr [rcx+290h],xmm0 
000000007711B4E4 mov   eax,dword ptr [rcx+30h] 
000000007711B4E7 and   eax,100040h 
000000007711B4EC cmp   eax,100040h 
000000007711B4F1 jne   000000007711B519 
000000007711B4F3 mov   r8d,dword ptr [rcx+34h] 
000000007711B4F7 movsxd  rax,dword ptr [rcx+4E0h] 
000000007711B4FE lea   rbx,[rcx+2D0h] 
000000007711B505 add   rbx,rax 
000000007711B508 xchg  r8d,dword ptr [rbx+18h] 
000000007711B50C mov   eax,0FFFFFFFCh 
000000007711B511 cdq 
000000007711B512 xrstor  [rbx] 
000000007711B515 mov   dword ptr [rbx+18h],r8d 
000000007711B519 fxrstor  [rcx+100h] 

「RCX」のデバッガで値が0x30e340で、Visual Studioで例外メッセージを読み取ります。 " fptest.exeの0x7711b519での初回例外:0xC0000005:アクセス違反が0xffffffffffffffffの場所を読み取っています。 "

なぜVSは0xffffffffffffffffを読み込もうとしていると報告しますか?

答えて

0

最後に私自身の質問に対する答えを得ました。私は_clearfpと_controlfpを呼び出した後浮動小数点レジスタのための私の希望する値を持っていた現在のコンテキストをキャプチャするために、GetThreadContext(GetCurrentThread(),&myContext)行を使用していた。しかし、私はGetThreadContextのヘルプに気づきませんでした:現在のスレッドでGetThreadContextを呼び出すと、関数は正常に戻ります。ただし、返されるコンテキストは無効です。

現在のスレッドコンテキストを取得する正しい方法は、RtlCaptureContextです。オリジナルのコードを編集して反映させます。

関連する問題