VC++の例外処理の仕組みを追う

昨日はg++の例外処理を追ったので、今日はVC++も調べてみました。
適当な例外を送出するコードを逆アセンブルすると、_CxxThrowException()という関数が呼ばれていたので、これをキーワードに調べてみたところ、CRunTinyというプロジェクトがVC++用簡易ランタイムを作成しているのを見つけました。
そして、そこからリンクされているDDJの記事「Visual C++ Exception-Handling Instrumentation」に詳細が書かれていました。


なんで構造体のメンバまで分かるのかと思って読んでみたら、デバッガで見たそうです。
試してみたら確かに見えました。ちゃんとデバッグ情報が残っているんですね。
というわけで、以下が実際に調べた_CxxThrowException()に関する宣言です。

typedef void (__cdecl *_PMFN)(void);

struct _TypeDescriptor
{
    const void* pVFTable;
    void* spare;
    char name[];
};

struct _PMD
{
    int mdisp;
    int pdisp;
    int vdisp;
};

struct _s__CatchableType
{
    unsigned properties;
    _TypeDescriptor* pType;
    _PMD thisDisplacement;
    int sizeOrOffset;
    _PMFN copyFunction;
};

struct _s__CatchableTypeArray
{
    int nCatchableTypes;
    const _s__CatchableType* arrayOfCatchableTypes[];
};

struct _s__ThrowInfo
{
    unsigned attributes;
    _PMFN pmfnUnwind;
    int (__cdecl *pForwardCompat)(...);
    const _s__CatchableTypeArray* pCatchableTypeArray;
};

extern "C" __stdcall
_CxxThrowException(void* pExceptionObject, const _s__ThrowInfo* pThrowInfo);

typedefは代入エラーでコンパイラが出力するメッセージからの推定です。
なお、これらの宣言はコンパイラにとって既知なので何もインクルードせずに自前で宣言することもなく使えます。


で、ここまで分かったものの、まだ例外の投げ方すら解明できていません。
_CxxThrowException()を呼んでいるコードを見ても例外オブジェクト用のメモリを確保している様子がなく、g++の様に簡単にはいかないかもしれません。