浮動小数点例外をキャッチし、スタックトレースを出力し、浮動小数点例外を無効にして実行を再開する手段を提供します(得られた非有限/非数の値を使用して)。私は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を読み込もうとしていると報告しますか?