0

失敗した場合に例外が発生するコードを単体テストする必要があります。簡単に言えば、スタック フレームをアンワインドするか、エラーをローカル ジャンプして、例外を処理する必要があります。MSVC の使用はオプションではありません。

AddVectoredExceptionHandlerのMSDN の例は、eip を変更してから EXCEPTION_CONTINUE_EXECUTION を返してローカル ジャンプを実行できることを示しています。明らかな問題は、どのアドレスにジャンプするかです。GCC のLabel as Value機能は、まさにそのように思われます。

以下の例は、エラーが発生した関数から単一の戻り値がある場合に機能します。ただし、2 番目の return ステートメントが追加されると、ジャンプのオフセットが疑わしいほど小さくなり、ジャンプは失敗します。なんで?

#include <assert.h>
#include <stdbool.h>
#include <stdio.h>

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0502
#endif

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

static bool handler_called;
static DWORD handler_eip;

static LONG WINAPI local_handler(struct _EXCEPTION_POINTERS* ExceptionInfo) {
   handler_called = true;
   PCONTEXT Context = ExceptionInfo->ContextRecord;
   Context->Eip = handler_eip;
   return EXCEPTION_CONTINUE_EXECUTION;
}

static void badcall(void) {
   handler_called = false;
   handler_eip = &&fail;

   int value = 100;
   int zero = 0;
   value = value/0;
   printf("not handled.\n");
   assert(false);
   //return; // Uncomment this to break the handler

   fail:
   printf("error handled.\n");
   return;
}

int main(int argc, char* argv[]) {
   void* old_handler = SetUnhandledExceptionFilter(&local_handler);
   badcall();
   SetUnhandledExceptionFilter(old_handler);
   return(0);
}
4

1 に答える 1

1

GCC のデッド コードの削除が原因のようです。SEH とは関係ありません。関数の一番下のブロックに到達できない場合、それらのステートメントは削除されます。次に、GCC は、アドレスを取得した場所に任意にラベルを作成します。

プログラム フローを直接操作しているため、C の規則では、コンパイラがこのような奇抜なことを行った場合に文句を言ってはいけません。

より最小限の例を次に示します。

static void* handler_eip;
static void badcall(void) {
   handler_eip = &&fail;

   int value = 100/0;
   printf("not handled.\n");
   //goto fail;
   return;

   fail:
   printf("error handled.\n");
   return;
}

goto を配置した L2 の場所に注意してください。

_badcall:
    pushl    %ebp
    movl    %esp, %ebp
    subl    $56, %esp
    movl    $L2, _handler_eip
    movl    $100, %eax
    movl    $0, -28(%ebp)
    movl    %eax, %edx
    sarl    $31, %edx
    idivl    -28(%ebp)
    movl    %eax, -12(%ebp)
    movl    $LC0, (%esp)
    call    _puts
    nop
L2:
    movl    $LC1, (%esp)
    call    _puts
    nop
    leave
    ret

そしてなし:

_badcall:
    pushl    %ebp
    movl    %esp, %ebp
    subl    $56, %esp
L2:
    movl    $L2, _handler_eip
    movl    $100, %eax
    movl    $0, -28(%ebp)
    movl    %eax, %edx
    sarl    $31, %edx
    idivl    -28(%ebp)
    movl    %eax, -12(%ebp)
    movl    $LC0, (%esp)
    call    _puts
    nop
    leave
    ret
于 2013-04-25T18:40:07.723 に答える