mysql2 gem を mysql5.6 の libmysqlclient.a と static link させたくて奮闘してた。
なぜ、そんなことをしたいのかというと、capistrano-bundle_rsync なるものを使って、デプロイサーバで mysql2 gem をビルドして、それを production サーバに rsync して撒くという運用をしているためである。
静的リンクできるようになれば、production サーバの OS の違いとか、入っている rpm のバージョン違いとか気にしなくてよくなるのでうれしい。
使った rpm は Oracle が配布しているオフィシャルなやつ で、こいつがおかしい。
$ sudo rpm -qa | grep -i mysql
MySQL-client-5.6.21-1.el6.x86_64
MySQL-server-5.6.21-1.el6.x86_64
MySQL-devel-5.6.21-1.el6.x86_64
MySQL-shared-5.6.21-1.el6.x86_64
問題1. mysql_config --libs が示すパスがおかしい
$ ls /usr/lib64/libmysqlclient.so
/usr/lib64/libmysqlclient.so
$ ls /usr/lib64/mysql/libmysqlclient.a
/usr/lib64/mysql/libmysqlclient.a
$ mysql_config --libs
-L/usr/lib64 -lmysqlclient -lpthread -lm -lrt -ldl
mysql_config --libs
の結果に -L/usr/lib64/mysql が含まれておらず、おかしい。これでは static link されない。 こちらにバグ報告がある => http://bugs.mysql.com/bug.php?id=67851
実際、mysql2 gem は mysql_config --libs
の出力結果 を元に extconf.rb で Makefile 中の LIBS を生成しているが、-L/usr/lib64/mysql
が含まれていないので、gem install mysql2 で静的リンクしてくれない。
対応方法として二つ考えられたので2番をやってみた。
- /usr/bin/mysql_config をいじる
- mysql2 gem ビルド時に
--with-mysql-lib
オプションを使って/usr/lib64/mysql
を指定する。
補足: --with-mysql-lib
オプションは mysql2 の README には書かれていないやつなので要チェックな。
$ gem install mysql2 -- --with-mysql-lib=/usr/lib64/mysql
# bundler を使いたい場合は以下のようにする。
# $ bundle config build.mysql2 --with-mysql-lib=/usr/lib64/mysql
# $ bundle
$ find ~/.rbenv/versions/2.1.3 -name 'mysql2.so'
/home/vagrant/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/mysql2-0.3.16/ext/mysql2/mysql2.so
/home/vagrant/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/mysql2-0.3.16/lib/mysql2/mysql2.so
/home/vagrant/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/extensions/x86_64-linux/2.1.0-static/mysql2-0.3.16/mysql2/mysql2.so
$ ldd /home/vagrant/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/mysql2-0.3.16/lib/mysql2/mysql2.so
linux-vdso.so.1 => (0x00007fff5bbe8000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fdacb621000)
libm.so.6 => /lib64/libm.so.6 (0x00007fdacb39c000)
librt.so.1 => /lib64/librt.so.1 (0x00007fdacb194000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007fdacaf90000)
libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007fdacad58000)
libc.so.6 => /lib64/libc.so.6 (0x00007fdaca9c4000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fdaca7ae000)
/lib64/ld-linux-x86-64.so.2 (0x00000035d6200000)
libfreebl3.so => /usr/lib64/libfreebl3.so (0x00007fdaca536000)
ちなみに、静的リンクできていないときはこうなる
$ gem install mysql2
$ find ~/.rbenv/versions/2.1.3 -name 'mysql2.so'
/home/vagrant/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/mysql2-0.3.16/ext/mysql2/mysql2.so
/home/vagrant/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/mysql2-0.3.16/lib/mysql2/mysql2.so
/home/vagrant/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/extensions/x86_64-linux/2.1.0-static/mysql2-0.3.16/mysql2/mysql2.so
$ ldd /home/vagrant/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/mysql2-0.3.16/lib/mysql2/mysql2.so
linux-vdso.so.1 => (0x00007fff38bf2000)
### libmysqlclient.so への依存が出ていて静的リンクされていないことがわかる
libmysqlclient.so.18 => /usr/lib64/libmysqlclient.so.18 (0x00007fc893f82000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fc893d5b000)
libm.so.6 => /lib64/libm.so.6 (0x00007fc893ad7000)
librt.so.1 => /lib64/librt.so.1 (0x00007fc8938cf000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007fc8936ca000)
libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007fc893493000)
libc.so.6 => /lib64/libc.so.6 (0x00007fc8930ff000)
libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00007fc892df8000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fc892be2000)
/lib64/ld-linux-x86-64.so.2 (0x00000035d6200000)
libfreebl3.so => /usr/lib64/libfreebl3.so (0x00007fc89296b000)
問題2. undefined symbol: __cxa_pure_virtual と出る
静的リンクできた!!と思って irb を起動して require mysql2
とすると、
irb(main):001:0> require 'mysql2'
LoadError: /home/vagrant/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/extensions/x86_64-linux/2.1.0-static/mysql2-0.3.16/mysql2/mysql2.so:
undefined symbol: __cxa_pure_virtual - /home/vagrant/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/extensions/x86_64-linux/2.1.0-static/mysql2-0.3.16/mysql2/mysql2.so
のようなエラーが出る。これはどうやら libmysqlclient.a が stdc++ とリンクできていなくておこっているらしい。こちらにバグ報告がある => http://bugs.mysql.com/bug.php?id=51642
また、なにか mysql2 gem にオプションがないかと ext/mysql2/extconf.rb を読んだが、どうも指定の方法がないっぽい(あったら教えてください)
仕方がないので /usr/bin/mysql_config をいじることにした(社内ではいじった状態で rpm を作り直してそれを利用することにした)
diff -u /usr/bin/mysql_config.bak /usr/bin/mysql_config
]--- /usr/bin/mysql_config.bak 2014-10-21 15:52:12.527601338 +0900
+++ /usr/bin/mysql_config 2014-10-21 15:50:56.886736049 +0900
@@ -84,7 +84,7 @@
bindir='/usr/bin'
# If installed, search for the compiled in directory first (might be "lib64")
-pkglibdir='/usr/lib64'
+pkglibdir='/usr/lib64/mysql'
pkglibdir_rel=`echo $pkglibdir | sed -e "s;^$basedir/;;"`
fix_path pkglibdir $pkglibdir_rel lib64/mysql lib64
@@ -111,7 +111,7 @@
# Create options
# We intentionally add a space to the beginning and end of lib strings, simplifies replace later
-libs=" $ldflags -L$pkglibdir -lmysqlclient -lpthread -lm -lrt -ldl"
+libs=" $ldflags -L$pkglibdir -lmysqlclient -lpthread -lm -lrt -ldl -lstdc++"
libs="$libs "
libs_r=" $ldflags -L$pkglibdir -lmysqlclient_r -lpthread -lm -lrt -ldl "
embedded_libs=" $ldflags -L$pkglibdir -lmysqld -lpthread -lm -lrt -lcrypt -ldl -laio "
この状態で、gem uninstall mysql2 して、改めて gem install mysql2
したところ、静的リンクされた壊れていない mysql2 gem のビルドが出来た。