2017-03-06 5 views
1

여기서 내 마지막 목표는 부동 소수점 예외를 catch하고 스택 추적을 인쇄하고 부동 소수점 예외를 사용하여 실행을 다시 시작하는 방법을 제공하는 것입니다 (결과가 아닌 유한/비 - 값을 사용). 나는 내 original question 이후로 약간 진전을 보였습니다. SSE (기본값은 x64)를 사용할 때 부동 소수점 단위를 지우거나 구성하기 위해 조정해야하는 레지스터가 더 많이 있다는 것을 깨달았습니다.예외를 처리 한 후 컨텍스트를 복구하는 작업은 VS/RTC (런타임 검사)에서만 작동합니다.

나는 아주 간단한 예제를 만들었지 만, 릴리스 빌드로 가면 x64에서 문제가 발생합니다. 디버그/릴리스 빌드는 x86 대상에 대해 잘 작동합니다. 난 "Run-Time-Checks" option of Visual-Studio, 구체적으로 "스택 프레임 런타임 오류 검사를 활성화"RTCs 아래로 문제를 좁혔습니다. (내가 기대하는 것입니다)의 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; 
} 

는,이 코드는 출력을 생성합니다 : 여기

은 예제 프로그램의

RTC의 비활성화와
#########Caught Ya: EXCEPTION_FLT_DIVIDE_BY_ZERO 
ExceptionAddr = 0x000000013F7A1638 
a = 1.#INF00 
b = 5.000000 
c = 1.#INF00 
d = 1.#INF00 
e = 1.#INF00 

,이 코드가 생성하는 출력 :

#########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 대상 (WIN64) : RTC가 비활성화 된 경우에만 부동 소수점 예외로부터 복구를 시도한 후 액세스 위반이 발생합니다.

RTC가 수행하는 작업에 대한 생각과 부동 소수점 예외로부터 복구하는 동작에 영향을주는 이유는 무엇입니까?

편집 : 어셈블리에서 조금 더 디버깅했습니다. 위반은 필터 기능에서 복귀 한 후에 발생하지만 0으로 나누기 재개 전에 발생합니다. 다음은 어셈블리가 액세스 위반까지 선도 (조립의 마지막 줄은 범인입니다) :

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

마지막으로 내 자신의 질문에 대한 답변을 얻었습니다. 나는 GetThreadContext(GetCurrentThread(),&myContext) 라인을 사용하여 _clearfp 및 _controlfp를 호출 한 후 부동 소수점 레지스터에 원하는 값을 가진 현재 컨텍스트를 캡처했습니다. 그러나 GetThreadContext에 대한 도움말에서 알 수 없었습니다. 현재 스레드에 대해 GetThreadContext를 호출하면 함수가 성공적으로 반환됩니다. 그러나 반환 된 컨텍스트가 유효하지 않습니다.

현재 스레드 컨텍스트를 얻는 올바른 방법은 RtlCaptureContext입니다. 반영 할 원래 코드를 수정하겠습니다.