Leopard でビルドした DBD::SQLite の続きの続き

amatubu2008-08-10


前回、5.8.6 の CORE フォルダにあるヘッダファイルを参照するようにすれば Tiger/Panther でも動くことがわかったので、具体的にどこが違うのかを調べてみた。単純に diff をとると(-bu オプションつきで)21,257 行とかになって気が遠くなるが、大物の config.h や uconfig.h を 5.8.8 のものに置き換えたり(ここで 10,000 行あまり)、あまり関係なさそうな embed.h とかを置き換えていき、どのファイルを置き換えると問題が起こるかを調べていく。いくつか進み、sv.h を置き換えるたところでエラーが起こることが判明。
ということで、今度は逆に sv.h のみを置き換えた状態で試してみると、ビルド時に

DBI.c: In function 'boot_DBI':
DBI.c:5536: warning: implicit declaration of function 'SvPV_nolen_const'
DBI.c:5536: warning: initialization makes pointer from integer without a cast
DBI.c:5536: warning: passing argument 2 of 'strcmp' makes pointer from integer without a cast

という警告が出てしまい、実行時にも

/path/to/DBI.bundle undefined reference to _SvPV_nolen_const expected to be defined in a dynamic image

というエラーが出て、だめ。どうやらほかにも元に戻さなければいけないファイルがあるようだ。エラーの内容をもとにヘッダファイルを grep してみると、どうやら XSUB.h も元に戻す必要があるようだ。
ということで、sv.h と XSUB.h を 5.8.6 のものに戻して再挑戦(この時点で diff の結果は 412 行)。今度はうまくいった。これで、この 2 つのファイルのいずれかを調べれば原因がつかめそうだということがわかった。
ここで、Panther で動かしたときのクラッシュログに、sv なんちゃらという関数名が記録されていたのを思い出した。ログには、

Exception:  EXC_BAD_ACCESS (0x0001)
Codes:      KERN_PROTECTION_FAILURE (0x0002) at 0x00000000

Thread 0 Crashed:
0   libperl.dylib 	0x87e32f9c Perl_sv_2pv_flags + 0x938
1   SQLite.bundle 	0x00621c24 sqlite_bind_ph + 0x1d0
2   SQLite.bundle 	0x00606d90 dbdxst_bind_params + 0x1a4
3   SQLite.bundle 	0x0060a69c XS_DBD__SQLite__st_execute + 0x134
4   DBI.bundle    	0x000480c0 XS_DBI_dispatch + 0x1a74
5   libperl.dylib 	0x87e2ddac Perl_pp_entersub + 0x4fc
6   libperl.dylib 	0x87e2695c Perl_runops_standard + 0x40
7   libperl.dylib 	0x87dd32fc S_run_body + 0x134
8   libperl.dylib 	0x87dd2f58 perl_run + 0xa4
9   perl          	0x000027b0 0x1000 + 0x17b0
10  perl          	0x00002420 0x1000 + 0x1420
11  perl          	0x00002294 0x1000 + 0x1294

とある。Perl_sv_2pv_flags でエラーが起こっているようだ。sv.h と XSUB.h の diff からこれが呼ばれているところを探すと、5.8.8 の SvPV_nolen というマクロで使われていることが判明(5.8.6 では定義が異なり、別の関数 sv_2pv_nolen が使われている)。
今度は 5.8.8 の sv.h と XSUB.h を 5.8.6 フォルダにコピーし、sv.h のこの部分を 5.8.6 と同じように書き換えてみる。

diff -rbu /System/Library/Perl/5.8.8/darwin-thread-multi-2level/CORE/sv.h /System/Library/Perl/5.8.6/darwin-thread-multi-2level/CORE/sv.h
--- /System/Library/Perl/5.8.8/darwin-thread-multi-2level/CORE/sv.h	2008-02-19 22:25:50.000000000 +0900
+++ /System/Library/Perl/5.8.6/darwin-thread-multi-2level/CORE/sv.h	2008-08-10 01:48:26.000000000 +0900
@@ -1092,7 +1092,7 @@
 
 #define SvPV_nolen(sv) \
     ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \
-     ? SvPVX(sv) : sv_2pv_flags(sv, 0, SV_GMAGIC))
+     ? SvPVX(sv) : sv_2pv_nolen(sv))
 
 #define SvPV_nolen_const(sv) \
     ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \</tt>

こんな感じ。
すると、今度はうまくいった。めでたくエラー解消である。
あとは、ソースファイルを変更せずになんとかこの違いを吸収できるとよいのだけど。先に SvPV_nolen を定義しておいてもあとから出てきた define で上書きされてしまってうまくいかないし、何かいい方法はないだろうか。とりあえずは 5.8.8 フォルダの sv.h を無理矢理書き換えてしまえばいけそうだけれどあまりスマートとは言えないしなぁ。
ところでこの sv_2pv_flags という関数、2 つめのパラメータは何か長さを返すものらしい。5.8.8 ではここに 0 を指定している。おそらく、5.8.6 以前のこの関数は、0 が渡されることは想定していないのだろう。0 かどうかをチェックせずに長さを書き込もうとしたところで KERN_PROTECTION_FAILURE (0x0002) at 0x00000000 が起こったというところだろうか。