RubyはともかくCで書かれた拡張ライブラリになるとかなりOS固有の部分がでてきます。
例えばfoo_ext.bundleとbar_ext.bundleが同名の関数fを定義していると、シンボルの束縛がうまくいかず、意図したfが呼ばれないことがあります。
確認方法
DYLD_PRINT_BINDINGS
環境変数をYES
にして実行すると、各シンボルがどのように解決されているかを確認できます。
$ DYLD_PRINT_BINDINGS=YES ruby app.rb 2>&1 | grep _f
dyld: lazy bind: foo_ext.bundle:0x105C55010 = foo_ext.bundle:_f, *0x105C55010 = 0x105C54E90
dyld: lazy bind: bar_ext.bundle:0x105C88010 = foo_ext.bundle:_f, *0x105C88010 = 0x105C54E90
上記の例だと、foo_ext
内のf
もbar_ext
内のf
も、foo_ext
のf
に束縛されていることが分かります。
なぜ発生するのか
MacOS Xの利用しているMach-Oファイルフォーマットにはflat namespaceとtwo-level namespaceがある。
Rubyがデフォルトで利用しているのはflat namespaceだが、これには複数のライブラリが同名のシンボルを含めれない制限がある。
In the flat namespace, two or more libraries cannot contain symbols with different implementations that share the same name because the dynamic linker cannot know which library contains the preferred implementation.
-- 引用元: Executing Mach-O Files
ちなみにflat namespaceに含まれているシンボル一覧はdyldinfo
コマンドで確認できる。
$ dyldinfo -lazy_bind foo_ext.bundle
lazy binding information (from lazy_bind part of dyld info):
segment section address index dylib symbol
__DATA __la_symbol_ptr 0x00001010 0x0000 flat-namespace _f
解決方法
flat namespaceではなくtwo-level namespaceを使うようにextconf.rb
を修正する。
case RbConfig::CONFIG['host_os'].downcase
when /darwin/
$DLDFLAGS.gsub!("-Wl,-flat_namespace","")
end