8

VC++ の try-except ステートメントを使用して、いくつかのコードを MinGW に移植しようとしています。

bool success = true;

__try {
    //...
} __except ((EXCEPTION_STACK_OVERFLOW == GetExceptionCode())
            ? EXCEPTION_EXECUTE_HANDLER
            : EXCEPTION_CONTINUE_SEARCH) {
    success = false;
    _resetstkoflw();
}
return success;

MinGW g++ を使用してスタック オーバーフロー例外をキャッチするコードを書くことは可能ですか?

4

4 に答える 4

10

例外処理を登録するWindowsAPI関数を手動で呼び出す必要があります。つまり、AddVectoredExceptionHandlerです。SEH例外を尊重しないMinGWを使用することにより、SEH例外をスローするか、そのような例外をキャッチしようとすると、通常のC ++スタック巻き戻しセマンティクスが実行されないため、未定義の動作が発生することに注意してください。std::string(Windowsは、スタック上のすべてのものを削除することをどのように知っていますか?)

RemoveVectoredExceptionHandlerまた、SEH例外ハンドラーを呼び出す必要がある時間の最後に呼び出す必要があります。

一般的に、MinGWはSEHやCOMなどのWindows機能のサポートを欠いています。MSVC ++の代わりにそれを使おうとしている理由は何ですか(両方のコンパイラが無料であると仮定しますか?)

于 2011-08-30T14:29:20.293 に答える
9

これはあまり知られていませんが、<excpt.h>MinGW と MinGW-w64 のヘッダー ファイルはマクロ__try1を提供し__except1、例外を処理するための gcc インライン アセンブリを生成します。これらのマクロは文書化されておらず、使いにくいです。ひどくなる。および の x86_64 エディションは、32 ビット エディションと互換性がありません__try1__except1これらは、異なる引数と異なる戻り値を持つ異なるコールバックを使用します。

数時間後、x86_64 でほとんどコードが動くようになりました。_gnu_exception_handlerMinGW の runtimeと同じプロトタイプでコールバックを宣言する必要がありました。私のコールバックは

long CALLBACK
ehandler(EXCEPTION_POINTERS *pointers)
{
    switch (pointers->ExceptionRecord->ExceptionCode) {
    case EXCEPTION_STACK_OVERFLOW:
        return EXCEPTION_EXECUTE_HANDLER;
    default:
        return EXCEPTION_CONTINUE_SEARCH;
    }
}

そして、私のtry-exceptコードは

    __try1 (ehandler) {
        sum = sum1to(n);
        __asm__ goto ( "jmp %l[ok]\n" :::: ok);
    } __except1 {
        printf("Stack overflow!\n");
        return 1;
    }
ok:
    printf("The sum from 1 to %u is %u\n", n, sum);
    return 0;

で最適化を有効にするまでは機能していましたgcc -O2。これによりアセンブラーエラーが発生したため、プログラムがコンパイルされなくなりました。__try1および__except1マクロは、関数を別のセクションに移動する gcc 5.0.2 の最適化によって壊れています.text

マクロが機能したときの制御フローはばかげていました。スタック オーバーフローが発生した場合、プログラムは を飛び越えまし__except1た。スタック オーバーフローが発生しなかった場合、プログラムは__except1同じ場所に落ちました。__asm__ gotoジャンプしok:てフォールスルーを防ぐには、奇妙なものが必要でした。goto ok;gccが到達不能で削除してしまうので使えません__except1

さらに数時間後、プログラムを修正しました。アセンブリ コードをコピーして変更し、制御フローを改善して ( にジャンプする必要がなくなりましたok:)、gcc -O2最適化を乗り切りました。このコードは醜いですが、私にとってはうまくいきます:

/* gcc except-so.c -o except-so */
#include <windows.h>
#include <excpt.h>
#include <stdio.h>

#ifndef __x86_64__
#error This program requires x86_64
#endif

/* This function can overflow the call stack. */
unsigned int
sum1to(unsigned int n)
{
    if (n == 0)
        return 0;
    else {
        volatile unsigned int m = sum1to(n - 1);
        return m + n;
    }
}

long CALLBACK
ehandler(EXCEPTION_POINTERS *pointers)
{
    switch (pointers->ExceptionRecord->ExceptionCode) {
    case EXCEPTION_STACK_OVERFLOW:
        return EXCEPTION_EXECUTE_HANDLER;
    default:
        return EXCEPTION_CONTINUE_SEARCH;
    }
}

int main(int, char **) __attribute__ ((section (".text.startup")));

/*
 * Sum the numbers from 1 to the argument.
 */
int
main(int argc, char **argv) {
    unsigned int n, sum;
    char c;

    if (argc != 2 || sscanf(argv[1], "%u %c", &n, &c) != 1) {
        printf("Argument must be a number!\n");
        return 1;
    }

    __asm__ goto (
        ".seh_handler __C_specific_handler, @except\n\t"
        ".seh_handlerdata\n\t"
        ".long 1\n\t"
        ".rva .l_startw, .l_endw, ehandler, .l_exceptw\n\t"
        ".section .text.startup, \"x\"\n"
        ".l_startw:"
            :::: except );
    sum = sum1to(n);
    __asm__ (".l_endw:");
    printf("The sum from 1 to %u is %u\n", n, sum);
    return 0;

except:
    __asm__ (".l_exceptw:");
    printf("Stack overflow!\n");
    return 1;
}

ehandler()Windows がフル スタックでどのように呼び出すことができるのか疑問に思うかもしれません。sum1to()ハンドラーが何をすべきかを決定するまで、これらの再帰呼び出しはすべてスタックに残る必要があります。Windows カーネルにはいくつかの魔法があります。スタック オーバーフローが報告されると、ntdll.exe がハンドラーを呼び出せるように、スタックの余分なページもマップします。ハンドラーにブレークポイントを設定すると、gdb でこれを確認できます。私のマシンでは、スタックがアドレス 0x54000 まで成長します。スタック フレームsum1to()は 0x54000 で停止しますが、例外ハンドラは 0x53000 から 0x54000 までのスタックの余分なページで実行されます。Unix シグナルにはそのような魔法はありません。そのため、Unix プログラムsigaltstack()はスタック オーバーフローを処理する必要があります。

于 2015-09-17T03:30:36.757 に答える
2

MinGW の構造化例外処理の互換性を追加するために、 LibSEHを調べることをお勧めします。

于 2011-08-30T14:34:45.550 に答える
1

MinGW は構造化例外のキーワードをサポートしていません。ただし、Billy O'Neal が彼の回答で述べているように、特定のネイティブ関数を呼び出して同じ効果を得ることができます。

問題は、同じ効果が必要かどうかです。私は、構造化された例外は間違いだと強く信じています。オペレーティング システムが通知する構造化例外のリストには、「整数を 0 で除算しようとした」HANDLE、「関数に渡されたパラメーターを使用できなかった」、「不正なマシン コード命令を実行しようとした」などがあります。 「許可なくメモリにアクセスしようとしました。」これらのエラーについて賢明なことは何もできませんが、構造化された例外を使用すると、(1) エラーが発生したことを主張し、(2) プログラムをもう少しよろめかせることができます。HANDLEコードが 0 で除算しようとした理由、無効なパラメーターを渡した理由を突き止めた方がはるかに適切です。それをしないようにコードを修正してください

構造化された例外を使用して問題を検出し、ダイアログ ボックスを表示して終了できるという議論があります。オペレーティング システムにダイアログ ボックスを表示させてプログラムを終了させるよりも、これがどのように優れているかはわかりません (特に、オペレーティング システムが処理中にミニダンプを送信する場合)。これは、ハンドルされない例外の既定の動作です。

一部のエラーは回復できません

于 2011-08-30T14:31:45.690 に答える