調査
C/Migemo(32bit版)が落ちるというので調査開始。確かに落ちる。 デバッグ版を作成して確認したが落ちない。一般的には未初期化変数が原因であるケースだが… 64bitリリース版で確認、落ちない。ということはあまり例の無いケース。 念のため64bitデバッグ版で確認、落ちない。
考察
- 32bit rel NG
- 32bit dbg OK
- 64bit rel OK
- 64bit dbg OK
症状の再現条件を整理するとこんな感じ…ちょっと原因の想像が付かない。 とりあえずprintfデバッグ的な方向しかなさそう。migemoに入ってるかどうかだけでも確認しよう。 デバッガで拾ってみると実行ポインタが0になってるから、不正な関数ポインタへのジャンプと予想。 しかし実際にprintfデバッグしてみると、init_migemo()からのリターンができてない…スタック破壊系、なのか?
到達
64bit版で問題が起きてないことから、関数ポインタの呼び出し規約の選択が32bitリリース版だけ異なるのではないかと推測した。 何も指定しないとデフォルトの規約が適用されるが、32bitリリース版だけそのデフォルトが違うのでは? ということでmigemo_open()の規約を明示的に指定して確認。 ビンゴ! (映画に出てくるハッカー風に)
さらに考察/保留課題
C/Migemo 1.3では呼び出し規約がstdcallになっていた。1.2まではcdeclだったぽい。 VimはC/Migemoを関数ポインタ経由で呼び出す。 特に指定しなければ32bitリリース版では関数ポインタの呼び出し規約はcdeclになるらしい。 32bit版ではスタックにバッファがある、と仮定すれば発生しない理由として納得。 64bit版では呼び出し規約の事情が異なるようだ。 まだ詳しくは読んでないが http://msdn.microsoft.com/en-us/library/h2k70f3s.aspx ここらあたりが参考になりそう。 というわけで以下はいずれキッチリ調べたほうが良い。
- 32bitデバッグ版で動く理由(スタックにバッファがあると仮定)
- 64bitリリース版で動く理由(スタックにバッファがあると仮定)
関連して
最近メールで似たような指摘&パッチをもらったので、それを試した時に間違えて取り込んだかとも考えたが、履歴を確認すると該当する修正は3年以上前だったので関係ない。 いや正確にはこの修正の結果としてあのパッチが出てきた(ずいぶん時間がかかったが)とも捉えられる。 あのパッチはコールバックの呼び出し規約をstdcallへ変更するものだった。 公開しているAPIがstdcallである以上それにあわせるというのは根拠として十分に思える。 ただVimのようにもうこれを使って書かれたアプリがあるのでリジェクトしてしまったが、1.4とかバージョンが上がるときには入れるべきだろう。
結論
というわけでVimのほうだけアップデートすればC/Migemoを使えるようになることがわかった。