2015年6月8日月曜日

MySQLプラグイン作成時のWindows版だけにある「シンボルの壁」

また、需要が無いかも知れない情報かもですけど、嵌ったのでメモ代わりに残しておきます。ちなみに、コンパイラの種類、バージョン、オプションをちゃんと合わせないとアライメントずれでバイナリ互換が無くなる等のプラグイン作成基礎知識には触れません。普段Linuxの人が、気楽にWindows版も対応しようとした場合の落とし穴についてのみです。あと、私のon Windows開発歴はほぼ0なので間違っていたらごめんなさい。

MySQLプラグインは基本的に、プラグイン側からmysqld側のシンボルを参照することが多いです。例えばストレージエンジンプラグインならmysqldの中のhandlerクラスを継承して実装する必要があったり、プラグインからmysqldの状態を知ろうとすれば、そのグローバル変数やら、保護しているmutexやらも必要になります。

基本的にLinuxでは、同じヘッダファイルさえ読めば公式配布バイナリでも大概のものにアクセスできますが、Windowsではそうはいきません。Windowsの実行形式(exeとかdllとか)では、ダイナミックリンク時にどのシンボルを見せるか(EXPORT)、見に行くか(IMPORT)を明示的に指定する必要があります。デバッグ用のシンボル(.pdb)はプラグイン読み込み時には関係ありません。

mysqld.exeからプラグインdllを読みに行く場合は、プラグインの情報を記述した構造体を見に行くだけですから問題にはあまりならないでしょう。(プラグインのサンプルに則ればプラグイン側の必要な情報はEXPORTされるはずで、変更の必要はまず無いはず。)

問題はその逆です。mysqld.exeのどのシンボルがプラグインから見えるかは、mysqld.exe のリンク時に決まります。ソースツリーを見ると、mysqld.exe のリンク時に mysqld.def を作って読ませています。win/create_def_file.js はその時に利用される、.libファイルからシンボルを抽出して .def ファイル形式に変換するもので、それは sql/CMakeLists.txt の中で使用されています。その対象は、

"FOREACH (CORELIB sql binlog slave mysys mysys_ssl dbug strings binlogevents_static)"

となっており、innobase.lib は含まれていないので、通常は mysqld.exe のInnoDB関連にはアクセスできません。この行に 'innobase' を加えると、InnoDBのシンボルも参照可能な mysqld.exe がビルドされます。

また、プラグイン側も適切にIMPORT設定しなければいけません。mysqld.exe のリンクの際に、リンクライブラリmysqld.lib(EXPORTの確認用ライブラリ)が出力され、プラグインの作成時にリンクされるようになっていて、エラーが出るので取りこぼすことは無いと思います。リンク時にエラーが出た参照は、インクルードファイルで 'extern' で読むところを、'extern MYSQL_PLUGIN_IMPORT' として明示的にIMPORTする必要があります(実体は"__declspec(dllimport)")。これはプラグイン側だけの問題で、mysqld.exeのビルドには関係しません。

という以上の事情から、たとえば「InnoDBの内部が見れる便利なinformation_schema作ったよ。みんな使ってみて!」と言っても(いや、まだ作ってないですが…)、Windows版だけは折角プラグインとしてビルドしても、mysqld.exeもビルドしなおさなければいけないわけです。

一応バグとして上げましたが、需要が無ければ直るかどうか。。。(Bug#77251)

直れば色々某プラグインとかもWindows用InnoDBダイレクトアクセス版とか作れるようになるのと思うのですががが。(handlerから使うとMySQL形式-InnoDB形式変換が意外と重かったと思うので。) いや、Linux版だけなら作れるわけですががが。