例外

とある機会に謎の人から、「なぜMonaではC++の例外機構が使えないのか?使えるようにした方が良いのではないか?」とご指摘を頂きました。
たしかにMonaで動くアプリケーションはg++のオプションである -fno-exception を使用して例外機構を使用しないようにコンパイルしなければいけません。


なぜMonaで例外が使えないか?

  • 例外をずっとオフにしていて特に気にしていなかった。
  • 例外をサポートをするのは難しそうだ。(カーネルのサポートが必要との噂)


というわけなので久しぶりにまじめな技術的な話題へと突入。
敵を知ることから始めようということで、g++@cygwinに簡単な例外のコードを食わせてみます。

class Hoge
{
};

int main(int argc, char *argv[])
{
    throw Hoge();
    return 0;
}
  • Sオプションでの出力抜粋はこんな感じ。
〜略〜
	call	__alloca
	call	___main
	movl	$1, (%esp)
	call	___cxa_allocate_exception
L2:
	movl	$0, 8(%esp)
	movl	$__ZTI4Hoge, 4(%esp)
	movl	%eax, (%esp)
	call	___cxa_throw

〜略〜
__ZTS4Hoge:
	.ascii "4Hoge\0"
	.def	___cxa_throw;	.scl	3;	.type	32;	.endef
	.def	___cxa_allocate_exception;	.scl	3;	.type	32;	.endef

ふむ。___cxa_allocate_exceptionで例外オブジェクト作成して、___cxa_throwで throw しているようですね。
Googleで調べたところ、二つの関数はlibstdc++で定義・実装されているようです。

  • __cxa_allocate_exceptionは
    • eh_alloc.ccで定義されている。要約するとstd::mallocで領域を確保しているだけっぽい。
    • eh_throw.ccで定義されている。std:terminateが更に呼び出されて・・・。うーむ分からなくなってきた。


さていろいろ調べたら同じはてなダイアリー内に例外について詳しく書いている記事を見つけた。
id:yupo5656:20040808:p2


非常に分かりやすい。更にリンク元のサイト(英文)http://www.codeproject.com/cpp/Exceptionhandler.aspを読めば例外の仕組みが分かる。
ざっと読んだ感じだとカーネルの助けはいらないかなぁという印象を受けた。
例外発生時にスタック(と事前に生成したテーブル)を辿っていくだけっぽい。


ちなみにid:yupo5656:20040808:p2で説明されている

このときコンパイラは、 (X, 掃除コードのアドレス) の組をテーブル化してオブジェクトコードに埋めておく。「掃除コード」もコンパイラが生成したもので、具体的には「bar()呼び出し中に例外が throwされた場合に行わなければならない処理(foo()内でスタックに置いたオブジェクトのデストラクタ呼びなど)」が書かれる。こういうコードは g++ -S でアセンブリリストを吐かせれば目視で確認できる。

int main(int argc, char *argv[])
{
    try {
        throw Hoge();
    } catch (Hoge e)
    {
    }
    return 0;
}

コンパイルした結果でみる事ができる。

〜略〜
	.section	.gcc_except_table,"dr"
	.align 4
LLSDA112:
	.byte	0xff
	.byte	0x0
	.uleb128 LLSDATT112-LLSDATTD112
LLSDATTD112:
	.byte	0x1
	.uleb128 LLSDACSE112-LLSDACSB112
LLSDACSB112:
〜略〜

というわけで、libstdc++の例外関係の一部のコードを移植すればMonaでも例外が使えるかもしれないというのが今日のところの結論。
ちなみにVC++は別の方法で例外を実装しているらしいですがまだちゃんとドキュメントを読んでいません。

※ここの話題はMonaに例外機構を組み込むのを目的としたものではありません。単なる技術的興味で書いたネタです。