64ビットなrubyを野良ビルドする
何も考えずに本家からソースをダウンロードしてコンパイル&インストールすると
MyMac:~ chcoopu$ file /usr/local/ruby/bin/ruby /usr/local/ruby/bin/ruby: Mach-O executable i386 MyMac:~ chcoopu$
といった感じでi386アーキテクチャ用バイナリ、つまり32ビット環境でも動作するバイナリが作成される。PowerPC、SPARC、EM64T/AMD64なら64ビットモードで動作している時は32/64ビットのバイナリ両方を同時に実行できるので特に問題ないし不思議でもない。
Max OS X付属のrubyなら
MyMac:~ chcoopu$ file /usr/bin/ruby /usr/bin/ruby: Mach-O universal binary with 2 architectures /usr/bin/ruby (for architecture ppc7400): Mach-O executable ppc /usr/bin/ruby (for architecture i386): Mach-O executable i386 MyMac:~ chcoopu$
とPowerPC、Intel両方で動作するユニバーサルバイナリだとわかる。
ppc7400ということは、おそらくPowerPC 7400シリーズのこと。ということはG3かG4以降のバイナリということか。まぁ自分で使わないからどっちでもいいけど。
でも、実行バイナリではなくライブラリを調べてみると
MyMac:~ chcoopu$ file /System/Library/Frameworks/Ruby.framework/Versions/Current/usr/lib/libruby.1.dylib /System/Library/Frameworks/Ruby.framework/Versions/Current/usr/lib/libruby.1.dylib: Mach-O universal binary with 4 architectures /System/Library/Frameworks/Ruby.framework/Versions/Current/usr/lib/libruby.1.dylib (for architecture ppc7400): Mach-O dynamically linked shared library ppc /System/Library/Frameworks/Ruby.framework/Versions/Current/usr/lib/libruby.1.dylib (for architecture ppc64): Mach-O 64-bit dynamically linked shared library ppc64 /System/Library/Frameworks/Ruby.framework/Versions/Current/usr/lib/libruby.1.dylib (for architecture i386): Mach-O dynamically linked shared library i386 /System/Library/Frameworks/Ruby.framework/Versions/Current/usr/lib/libruby.1.dylib (for architecture x86_64): Mach-O 64-bit dynamically linked shared library x86_64 MyMac:~ chcoopu$
PowerPCも32ビット(ppc7400)、64ビット(ppc64)バイナリの2種類入っており、Intelの方も32ビット(i386)、64ビット(x86_64)バイナリの両方入った、合計4種類のバイナリの入ったユニバーサルバイナリだとわかる。実行バイナリに両アーキテクチャの64ビット版が入っていないのは何か理由があるのかな?
とゆーわけで、普通に使える64ビットなrubyバイナリはない訳だ。
なので、自分で作ってみようという話。
x86_64向けにバイナリを作れば64ビットバイナリになるので、そのようにする。特に-m64とかは必要ではないみたい。
gccへのオプションとして-arch x86_64を付けるだけ。x86_64の代わりにi386を指定すれば今までと同じバイナリになる……が、指定する意味はないな。ppcやppc64も選択できるが、まぁ必要な人だけ、ドゾー。
で、大抵のソフトはMakefileの中でCC、CXXでCコンパイラやC++コンパイラの指定をすることが多い。同時にCFLAGS、CXXFLAGSでCコンパイラ、C++コンパイラに対するオプションを指定することになる。
rubyのコンパイルする時にもCFLAGSに-arch x86_64を指定すればOKかなと。
指定してconfigure、makeすると見事64ビット対応のrubyができる。
MyMac:~ chcoopu$ file /usr/local/ruby/bin/ruby /usr/local/ruby/bin/ruby: Mach-O 64-bit executable x86_64 MyMac:~ chcoopu$ MyMac:~ chcoopu$ /usr/local/ruby/bin/ruby -v ruby 1.8.6 (2008-08-11 patchlevel 287) [i686-darwin9.5.0] MyMac:~ chcoopu$
できるのだが、作っている途中で部分的に文句を言われる。
ld warning: in bytecode.o, file is not of required architecture
その結果何も拡張していないruby本体は動作するものの、添付ライブラリ辺りでこけてしまう。
MyMac:rubygems-1.3.1 chcoopu$ /usr/local/ruby/bin/ruby setup.rb /usr/local/ruby/lib/ruby/1.8/i686-darwin9.5.0/thread.bundle: Failed to load /usr/local/ruby/ruby/lib/ruby/1.8/i686-darwin9.5.0/thread.bundle (LoadError) from /usr/local/ruby/lib/ruby/1.8/thread.rb:5 from ./lib/rubygems.rb:10:in `require' from ./lib/rubygems.rb:10 from setup.rb:22:in `require' from setup.rb:22 MyMac:rubygems-1.3.1 chcoopu$
というわけで、どこが問題なのかを確認してみる。
ソースの中の$RUBY_SOURCE/extディレクトリ以下にある拡張ライブラリのソースをコンパイルしてできたオブジェクトファイル( .o )は64ビットバイナリ。
MyMac:thread chcoopu$ file thread.o
thread.o: Mach-O 64-bit object x86_64
MyMac:thread chcoopu$
それに対して$RUBY_SOURCE/.ext/i686-darwin9.5.0ディレクトリ以下にあるのは何故かi386な32ビットバイナリ。
MyMac:i686-darwin9.5.0 chcoopu$ file thread.bundle thread.bundle: Mach-O bundle i386 MyMac:i686-darwin9.5.0 chcoopu$
つまりオブジェクトファイルをリンクする時に64ビットバイナリではなくなっているみたい。
ここでどうやってリンクしているかMakefileを確認してみると
LDSHARED = cc -dynamic -bundle -undefined suppress -flat_namespace
となっている。この呼び出されているccというのは/usr/bin/ccであり実体はgccである。
MyMac:~ chcoopu$ which cc /usr/bin/cc MyMac:~ chcoopu$ ls -l /usr/bin/cc lrwxr-xr-x 1 root wheel 7 10 22 00:14 /usr/bin/cc@ -> gcc-4.0 MyMac:~ chcoopu$
gccなら、コンパイル時と同じにオプションを渡してやればいいわけ。
今回はMakefileを変更するのではなく、Makefileを生成するためのconfigureを変更。しかも手抜き変更。
ccをgcc-4.0に固定しないように変更+x86_64用の設定。
※そのままMakefileに展開されるので、変数の書き方はMakefile形式で。
MyMac:ruby-1.8.6-p287 chcoopu$ diff configure configure.ORG 16520c16520 < darwin*) : ${LDSHARED='$(CC) -arch x86_64 -dynamic -bundle -undefined suppress -flat_namespace'} --- > darwin*) : ${LDSHARED='cc -dynamic -bundle -undefined suppress -flat_namespace'} MyMac:ruby-1.8.6-p287 chcoopu$
この状態でconfigure、makeをし直し。一度makeした後ならmake distcleanしてからconfigure、makeをすると。
そうすると、今度はちゃんと64ビットバイナリになった。
MyMac:i686-darwin9.5.0 chcoopu$ file thread.bundle
thread.bundle: Mach-O 64-bit bundle x86_64
MyMac:i686-darwin9.5.0 chcoopu$
そして、拡張ライブラリでもこけなくなった。めでたしめでたし。