Symbian C++開発・DLL内のstatic変数 (2005-04-01)
「DLLは書き込み可能なstaticデータを持たない」
702NKを購入したので、さっそく実機で自作プログラムを動かしてみたいところです。なので、以前iアプリのJavaで作った「Rev.」をSymbian OSに移植してみることにします。 以前の記事(2005-02-22)に書きましたが、ゲームのコア部分はすでにSymbian C++で実装していて、表示回りはコンソールアプリケーションとして実装するところまではできています。
しかしここで問題発生。エミュレータ向けのビルドは正常に完了し、エミュレータ上では正常動作するのですが、実機向けのビルドが成功しません。以下のようなエラーメッセージが出てしまいます。
ERROR: Dll 'REVENGINE.DLL' has initialised data.
("RevEngine.dll"は、ゲームのコア部分を独立させたDLLです)
これはすなわち「DLLは書き込み可能なstaticデータを持たない(『Symbian OS C++プログラミング』p.41)」という制限にひっかかったということです。というわけで、プログラム内で静的変数を使っていないかチェックしてみますが、それらしい箇所は見つかりません。静的データ領域はたくさんありますが、どれも"static const"で宣言しているので定数領域に配置されるはずなのです。ところが、mapファイルを見てみると、dataセクションにもbssセクションにも領域がちょっとずつ割り当てられている模様。おかしいなあ。
constの落とし穴
あちこち調べたところ、どうも以下のコードがまずかったようです。
static const TPatternBits* table[] = { &KPatternBitsSquare, // (中略) };
ポインタ変数に対してconstを指定する場合、"const"を記述する位置によって意味がまったく変わってきます。上記のコードだと、「ポインタ変数の指す先がconst(ポインタ変数自体は非const)」という意味になってしまいます。ポインタ変数自体をconstにするためには、以下のように"*"の右側に"const"を記述しなくてはなりません。
static const TPatternBits* const table[] = { &KPatternBitsSquare, // (中略) };
ところが、この問題を修正してもまだビルドに成功しません。仕方がないので、mapファイルを見ながら怪しげなテーブルを部分的にコメントアウトしてビルドを繰り返したところ、ようやくもう一つの原因を発見できました。
static const TPoint KInitPointsSquare[] = { TPoint(11, 5), TPoint(13, 5), TPoint(13, 6), TPoint(11, 6), };
要するに、コンパイル時に値が確定しない(実行時にコンストラクタが動作したときに初めて値が確定する)ため、定数領域に確保されないわけです。(ちなみに、"TPoint"というのはSymbianで用意している座標クラスです)
これらの問題を修正して、ようやくビルドが成功するようになりました。やれやれ。
エミュレータの落とし穴
それにしても、エミュレータ用と実機用とで実行バイナリが異なるというのは頭の痛いところです。エミュレータ向けのビルドを行うとWindows用のバイナリが生成されるわけですが、WindowsのDLLでは書き込み可能な静的領域うんぬんといった制限はないわけで、正常にビルドが完了してしまいます。そして、(実機環境ではビルドすらできないようなプログラムでも)エミュレータ上では何の問題もなく動作してしまうわけです。
まあ、今回は実機用のビルド時に問題が発覚したのでまだましなのですが、エミュレータと実機との違いによる落とし穴には、今後も幾度となくはまりそうです。
後日談
一連の問題が収束した後で、こんな文書を見つけました。
How can I find the "uninitialised data" in my DLL?
まさに今回の問題にそのまま当てはまる話です。最初からこれを読んでおけばよかった。英語だからといってスルーしていてはいけませんね。
というわけで、Symbian開発関連の(英語の)ウェブサイトをいろいろ回っていると、「.mmpファイルに"EPOCALLOWDLLDATA"の指定を追加すれば、DLL内の書き込み可能staticデータが使えるようになる」という情報を拾いました。調べてみると、こんな文書が見つかりました。
Symbian OS support for writeable static data in DLLs (PDF)
と思いきや、この機能をサポートしているのはSymbian OS v8.0bとv8.1bのみとのこと。702NK(Nokia 6630)のOSのバージョンはv8.0aなんですね……。
"v8.0a"と"v8.0b"なんて、いかにもマイナーバージョンアップのような感じなのに、そんなに大きい変更があるものなのか? と思って調べてみたところ、どうやら"a"と"b"とでカーネルが違うのだそうで。
Symbian OS Version 8.0 functional description (日本語版)
結論としては、「702NK(Nokia 6630)用アプリケーションを作るのであれば、DLL内の書き込み可能staticデータはやっぱり不可」ということになるようです。