Invalid library (maybe not a PHP library)
phpをデバッグビルドしたのは後々に役立つだろうということの他に、当面の問題として"Invalid library (maybe not a PHP library)"が何なのかを調べようという趣旨があったりしました。
php.iniに"extension=libmysqlclinet.so"と書いて、"/usr/lib/php/modules"にlibmysqlclient.soを放りこめばウマクイクと思ったのに、"php -v"すると以下のように怒られるわけです。
mir@t43:~/php$ php -v PHP Warning: PHP Startup: Invalid library (maybe not a PHP library) 'libmysqlclient.so' in Unknown on line 0
"Invalid library"でgrepすると、ext/standard/dl.cのphp_dl関数の中でエラーとなっているみたい。
if (!get_module) { DL_UNLOAD(handle); php_error_docref(NULL TSRMLS_CC, error_type, "Invalid library (maybe not a PHP library) '%s' ", Z_STRVAL_P(file)); RETURN_FALSE; }
というわけでphp_dl関数にbreakpointを突っ込んでデバッガを動かしてみる。
まず気になるポイント1個目。DL_LOADはLinuxだとシステムコールdlopenが実行されるマクロ。libpathの型はchar*で、値は"/usr/lib/php/modules/libmysqlclient.so"。ここでdlopenの戻り値のhandleは値が設定されていたのでこの時点では問題無し。
/* load dynamic symbol */ handle = DL_LOAD(libpath); if (!handle) { php_error_docref(NULL TSRMLS_CC, error_type, "Unable to load dynamic library '%s' - %s", libpath, GET_DL_ERROR()); GET_DL_ERROR(); /* free the buffer storing the error */ efree(libpath); RETURN_FALSE; }
次に気になるポイント。DL_FETCH_SYMBOLはLinuxだとシステムコールdlsymが実行されるマクロ。最初に"get_module"というシンボルを取得しようとし、それがダメなら"_get_module"というシンボル名でリトライ。しかしこれらの行を実行した後に変数get_moduleを見たらnullだった。
get_module = (zend_module_entry *(*)(void)) DL_FETCH_SYMBOL(handle, "get_module"); if (!get_module) get_module = (zend_module_entry *(*)(void)) DL_FETCH_SYMBOL(handle, "_get_module");
というわけで、get_module=0x0のまま処理が進んで、ここでエラーになっていたということですな。
if (!get_module) { DL_UNLOAD(handle); php_error_docref(NULL TSRMLS_CC, error_type, "Invalid library (maybe not a PHP library) '%s' ", Z_STRVAL_P(file)); RETURN_FALSE; }
つまり、dlopenしたlibmysqlclient.soにシンボルget_moduleが無かったのが今回のエラーの原因ということですね。で、PHPの仕様を確認したわけではないけれども、拡張モジュールとしてロード可能なdynamic linkライブラリはシンボル"get_module"が含まれていないといけないということらしい。
この"get_module"だけれども、以下のように定義されているので、引数無しで戻り値がzend_module_entryへのポインタである関数ということになる。
zend_module_entry *(*get_module)(void);
zend_module_entryは"_zend_module_entry"という名前でZend/zend_modules.hに定義されている構造体。ふむ。要するにget_module関数がないのが原因と。
get_module関数がある場合には、次のステップで以下のようにzend_module_entryへのポインタを取得する。
module_entry = get_module();
で、まとめると、自分でビルドしたlibmysqlclient.soとかはextensionsでPHPに読み込ませて使うのは無理ということですな。これphp界隈の人には常識? orz
ついでに
もうちょい調べてみる。
libmysqlclient.soに対してnmとかstringsとかをやって"get_module"でgrepしてももちろん入っていない。
続いてMySQLのソースディレクトリに対して"get_module"でgrepしても、やはり入っていない。
ということはMySQLのソースからだけではphp用のモジュールは作れないってことかなぁ。phpに詳しいnkjm氏曰く、通常はphpのビルド時に--with-mysqlでsharedは指定しないのでlibmysqlclientはstatic linkされるから無問題、ということなので自分で作ったMySQLのクライアントライブラリをphpで使おうとおもったらその方法になるのかなあ。
mysql.soってPHPのソースに含まれてるのね
phpのソースをfindしたらでてきた。
mir@t43:~/php/php-5.2.3$ find . -name "mysql.so" ./ext/mysql/.libs/mysql.so ./modules/mysql.so mir@t43:~/php/php-5.2.3/modules$ nm mysql.so | grep get_module 0000408c T get_module
あ、そっか・・・。mysql_connectというPHP関数を実装しているのはphpのソースに含まれるmysql.soとかmysqli.soとかで、こいつらがさらにlibmysqlclient.soに依存する、というフォーメーションなのねん。
mysql.soをコピればok
/usr/lib/php/modulesに放りこんで、/usr/local/php.iniにextention=mysql.soをついかしたらphpスクリプトが動くようになった。
mysql.soをlddする
とりあえず/usr/lib/mysql/libmysqlclient.so.15を見ているらしい。
mir@t43:~/php/php-5.2.3/modules$ ldd mysql.so linux-gate.so.1 => (0x00bf7000) libmysqlclient.so.15 => /usr/lib/mysql/libmysqlclient.so.15 (0x001a3000) libc.so.6 => /lib/i686/nosegneg/libc.so.6 (0x00305000) libcrypt.so.1 => /lib/libcrypt.so.1 (0x00113000) libnsl.so.1 => /lib/libnsl.so.1 (0x00dc5000) libm.so.6 => /lib/i686/nosegneg/libm.so.6 (0x00f3b000) libssl.so.6 => /lib/libssl.so.6 (0x00141000) libcrypto.so.6 => /lib/libcrypto.so.6 (0x00446000) libz.so.1 => /usr/lib/libz.so.1 (0x00d83000) /lib/ld-linux.so.2 (0x00b74000) libgssapi_krb5.so.2 => /usr/lib/libgssapi_krb5.so.2 (0x00f93000) libkrb5.so.3 => /usr/lib/libkrb5.so.3 (0x00929000) libcom_err.so.2 => /lib/libcom_err.so.2 (0x00c23000) libk5crypto.so.3 => /usr/lib/libk5crypto.so.3 (0x00578000) libresolv.so.2 => /lib/libresolv.so.2 (0x00186000) libdl.so.2 => /lib/libdl.so.2 (0x00c39000) libkrb5support.so.0 => /usr/lib/libkrb5support.so.0 (0x00c91000)
ということはこれを新しく作成したlibmysqlclient.so.15で上書きするかあるいは環境変数LD_LIBRARY_PATHを使えばいけそう。/home/mir/phpに新しくつくったlibmysqlclient.soを置いているとして、、
mir@t43:~/php$ LD_LIBRARY_PATH=/home/mir/php ldd php-5.2.3/modules/mysql.so linux-gate.so.1 => (0x004ee000) libmysqlclient.so.15 => /home/mir/php/libmysqlclient.so.15 (0x00d11000) libc.so.6 => /lib/i686/nosegneg/libc.so.6 (0x00110000) libcrypt.so.1 => /lib/libcrypt.so.1 (0x00685000) libnsl.so.1 => /lib/libnsl.so.1 (0x0056f000) libm.so.6 => /lib/i686/nosegneg/libm.so.6 (0x00251000) /lib/ld-linux.so.2 (0x00b74000)
イケター! これでphp環境でのlibmysqlclient.soのリビルドし放題ですねぃ。