私はかなり複雑ですが、さまざまな x86-32 および x86-64 ボックスで実行されている非常によくテストされたアセンブリ言語 x86-32 アプリケーションを持っています。これは言語コンパイラのランタイム システムであるため、別のコンパイル済みバイナリ プログラムである「オブジェクト コード」の実行をサポートします。
Windows SEH を使用して、さまざまな種類のトラップ (ゼロ除算、不正アクセスなど) をキャッチし、Windows によって提供されるコンテキスト情報を使用して、トラップ時のマシンの状態を示すレジスタ ダンプを出力します。(関数のバックトレースを出力したり、必要に応じてゼロ除算から回復したりするなど、質問に関係のない他の多くのことを行います)。これにより、「オブジェクト コード」の作成者は、自分のプログラムで何が問題になったのかを把握できます。
2 つの Windows 7-64 システムでは動作が異なりますが、これは多かれ少なかれ同一ですが、不正なメモリ アクセスであると思われます。具体的な問題は、「オブジェクト コード」(十分にテストされたランタイム システムではない) が愚かにも 0x82 を EIP にロードすることです。これは、アドレス空間のAFAIKに存在しないページです。SEH を介した Windows トラップが予想され、EIP=00000082 などのレジスタ ダンプが予想されます。
あるシステムでは、まさにそのレジスタ ダンプが得られます。ここで表示できますが、私の質問には何も追加されません。したがって、ランタイム システムの SEH がこれをキャッチして状況を表示できることは明らかです。このマシンには、MS 開発ツールが搭載されていません。
ランタイム システムとオブジェクト コードのまったく同じバイナリを使用するもう 1 つの (「謎」) システムでは、コマンド プロンプトしか表示されません。それ以上の出力はありません。FWIW、このマシンには MS Visual Studio 2010 が搭載されています。ミステリーマシンは他の目的で頻繁に使用されており、通常の使用では他のおかしな動作は見られません.
動作の違いは、どこかの Windows 構成、または Visual Studio が制御する何かによって引き起こされると思います。システム メニューの DEP 構成ではありません。どちらも「標準システムプロセスのDEP」として構成されています(バニラ)。また、ランタイム システムの実行可能ファイルには「いいえ (/NXCOMPAT:NO)」が構成されています。
どちらのマシンも i7 ですが、チップ、4 コア、大量のメモリ、マザーボードが異なります。これは関係ないと思います。確かに、これらの CPU は両方とも同じ方法でトラップを取得します。
ランタイム システムには、起動時に次の行が含まれます。
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); // クラッシュ時の Windows ポップアップを停止します
これは最近、クラッシュが発生したときに「謎の」システムがポップアップ ウィンドウ「xxx.exe が動作を停止しました」を表示しないようにするために追加されました。ポップアップ ボックスの動作は最初のシステムでは発生しないため、「ミステリー」マシンの別のコーナーに問題を押し込むだけでした。
これを構成/制御する手がかりはありますか?
私が使用しているSEHコードをここに提供します。このコードで見られる明らかな状態に影響を与えないと私が主張する、かなりの量の健全性チェック コードを削除するように編集されています。
ランタイム システムのトップ レベルは、(CreateThread を使用して) 一連のワーカー スレッドを生成し、ASMGrabGranuleAndGo を実行するようにポイントします。各スレッドは独自の SEH を設定し、ワークスティーリング スケジューラである RunReadyGranule に分岐します。私の知る限りでは、その後 SEH は変更されていません。少なくとも、ランタイム システムと「オブジェクト コード」はこれを行いませんが、基礎となる (標準の「C」などの) ライブラリが何を行うかはわかりません。
さらに下に、トラップ ハンドラー TopLevelEHFilter を提供します。はい、レジスター印刷機自体が爆発し、2 番目の例外が発生する可能性があります。すぐにもう一度確認してみますが、IIRC で謎のマシンのデバッガーでこれをキャッチしようとした最後の試みでは、デバッガーに制御が渡されず、ポップアップ ウィンドウが表示されました。
public ASMGrabGranuleAndGo
ASSUME FS:NOTHING ; cancel any assumptions made for this register
ASMGrabGranuleAndGo:
;Purpose: Entry for threads as workers in PARLANSE runtime system.
; Each thread initializes as necessary, just once,
; It then goes and hunts for work in the GranulesQ
; and start executing a granule whenever one becomes available
; install top level exception handler
; Install handler for hardware exceptions
cmp gCompilerBreakpointSet, 0
jne HardwareEHinstall_end ; if set, do not install handler
push offset TopLevelEHFilter ; push new exception handler on Windows thread stack
mov eax, [TIB_SEH] ; expected to be empty
test eax, eax
BREAKPOINTIF jne
push eax ; save link to old exception handler
mov fs:[TIB_SEH], esp ; tell Windows that our exception handler is active for this thread
HardwareEHinstall_end:
;Initialize FPU to "empty"... all integer grains are configured like this
finit
fldcw RTSFPUStandardMode
lock sub gUnreadyProcessorCount, 1 ; signal that this thread has completed its initialization
@@: push 0 ; sleep for 0 ticks
call MySleep ; give up CPU (lets other threads run if we don't have enuf CPUs)
lea esp, [esp+4] ; pop arguments
mov eax, gUnreadyProcessorCount ; spin until all other threads have completed initialization
test eax, eax
jne @b
mov gThreadIsAlive[ecx], TRUE ; signal to scheduler that this thread now officially exists
jmp RunReadyGranule
ASMGrabGranuleAndGo_end:
;-------------------------------------------------------------------------------
TopLevelEHFilter: ; catch Windows Structured Exception Handling "trap"
; Invocation:
; call TopLevelEHFilter(&ReportRecord,&RegistrationRecord,&ContextRecord,&DispatcherRecord)
; The arguments are passed in the stack at an offset of 8 (<--NUMBER FROM MS DOCUMENT)
; ESP here "in the stack" being used by the code that caused the exception
; May be either grain stack or Windows thread stack
extern exit :proc
extern syscall @RTSC_PrintExceptionName@4:near ; FASTCALL
push ebp ; act as if this is a function entry
mov ebp, esp ; note: Context block is at offset ContextOffset[ebp]
IF_USING_WINDOWS_THREAD_STACK_GOTO unknown_exception, esp ; don't care what it is, we're dead
; *** otherwise, we must be using PARLANSE function grain stack space
; Compiler has ensured there's enough room, if the problem is a floating point trap
; If the problem is illegal memory reference, etc,
; there is no guarantee there is enough room, unless the application is compiled
; with -G ("large stacks to handle exception traps")
; check what kind of exception
mov eax, ExceptionRecordOffset[ebp]
mov eax, ExceptionRecord.ExceptionCode[eax]
cmp eax, _EXCEPTION_INTEGER_DIVIDE_BY_ZERO
je div_by_zero_exception
cmp eax, _EXCEPTION_FLOAT_DIVIDE_BY_ZERO
je float_div_by_zero_exception
jmp near ptr unknown_exception
float_div_by_zero_exception:
mov ebx, ContextOffset[ebp] ; ebx = context record
mov Context.FltStatusWord[ebx], CLEAR_FLOAT_EXCEPTIONS ; clear any floating point exceptions
mov Context.FltTagWord[ebx], -1 ; Marks all registers as empty
div_by_zero_exception: ; since RTS itself doesn't do division (that traps),
; if we get *here*, then we must be running a granule and EBX for granule points to GCB
mov ebx, ContextOffset[ebp] ; ebx = context record
mov ebx, Context.Rebx[ebx] ; grain EBX has to be set for AR Allocation routines
ALLOCATE_2TOK_BYTES 5 ; 5*4=20 bytes needed for the exception structure
mov ExceptionBufferT.cArgs[eax], 0
mov ExceptionBufferT.pException[eax], offset RTSDivideByZeroException ; copy ptr to exception
mov ebx, ContextOffset[ebp] ; ebx = context record
mov edx, Context.Reip[ebx]
mov Context.Redi[ebx], eax ; load exception into thread's edi
GET_GRANULE_TO ecx
; This is Windows SEH (Structured Exception Handler... see use of Context block below!
mov eax, edx
LOOKUP_EH_FROM_TABLE ; protected by DelayAbort
TRUST_JMP_INDIRECT_OK eax
mov Context.Reip[ebx], eax
mov eax, ExceptionContinueExecution ; signal to Windows: "return to caller" (we've revised the PC to go to Exception handler)
leave
ret
TopLevelEHFilter_end:
unknown_exception:
<print registers, etc. here>