0

以下を実行するコードを実行時に生成する必要があります。

auto v_cleanup = std::shared_ptr<void>(nullptr, [](void *){ cleanup(); });

//...
do_some_danger_thing();
//...

または同等の C:

__try {
    //...
    do_some_danger_thing();
    //...
} __finally {
    cleanup();
}

cleanup() 関数は例外がないことが保証されていますが、 do_some_danger_thing() は例外をスローする場合があります。このランタイム コードはスタックを使用してはなりません。つまり、do_some_danger_thing() を呼び出すとき、ランタイム コードに設定された戻りアドレスを除いて、スタックはランタイム コードに入るときと同じ状態でなければなりません (元の値は "jmp に保存されました)。 " ターゲット、呼び出し元に戻るため)。

動的マシン コードを使用しているため、ターゲット プラットフォームは x86 CPU 上の WIN32 に固定されており、x64 CPU は現在注目されていません。

これを行うには、例外を処理する必要があります。WIN32 では、C++ 例外は SEH ベースであるため、それに対応する必要があります。問題は、これを行う方法を見つけられず、他のコードと互換性がないことです。いくつかの解決策を試しましたが、どれも機能しません。ユーザーがインストールした例外ハンドラーが呼び出されなかったり、外部の例外ハンドラーがバイパスされたりして、「ハンドルされていない例外」エラーが発生することもありました。

アップデート:

SEH 例外ハンドラ チェーンが EXE イメージ内のコードのみをサポートしているようです。例外ハンドラーが生成されたコードを指している場合、それは決して呼び出されません。私がしなければならないことは、静的例外ハンドラー関数スタブを作成し、生成されたハンドラーを呼び出させることです。

4

1 に答える 1

1

私は今、上記とは少し異なる実装を持っています。実際、擬似コードは次のようになります(C ++ 11の場合)。

std::exception_ptr ex;
try {
    //...
    do_some_danger_things();
    //...
} catch (...) {
    ex = std::current_exception();
}
cleanup();
if(ex)rethrow_exception(ex);

スタックがほどける前にの呼び出しが発生するため、これは上記のCと100%同じでcleanup()はありません。通常、これは問題ではありませんが、正確な例外コンテキストが失われる可能性があります。

次のように、ヘルパー関数として内部例外ハンドラーを実装しました。

_declspec(thread) void *real_handler = nullptr;

void **get_real_handler_addr(){
    return &real_handler;
}

__declspec(naked) int exception_handler(...){
    __asm {
        call get_real_handler_addr;
        mov eax, [eax];
        jmp eax;
    }
}

ここでの秘訣は、このハンドラーを実行時に生成してはならないため、スタブは「実際の」ハンドラーがどこにあるかを検出する必要があることです。これを行うには、スレッドローカルストレージを使用します。

これで、生成されたコードはFS:[0]から例外ハンドラチェーンを取得します。ただし、チェーンはスタックベースである必要があるため、次のコードを使用してハンドラーを置き換えます。

void **exception_chain;
__asm {
    mov eax, fs:[0]
    mov exception_chain, eax
}
//...
void *saved_handler = exception_chain[1];
exception_chain[1] = exception_handler;
*get_real_handler_addr() = generated_code->get_exception_handler();

生成された例外ハンドラーは、クリーンアップを実行できます。ただし、現在の例外ハンドラーがEXCEPTION_CONTINUE_SEARCHを返す場合、ハンドラーは2回呼び出されます。私の戦略は、最初の呼び出しで元の例外ハンドラーを復元することです。

于 2011-11-17T23:43:38.207 に答える