macOSでの動的ロード問題まとめ
主な問題
- dynamic_lookupがXcode 14で警告が出る
- two-level namespace vs. flat namespace
- 別の拡張ライブラリのシンボルを参照できない
- disable-sharedなrubyがfat binaryを読めない
dynamic_lookupがXcode 14で警告が出る
https://bugs.ruby-lang.org/issues/19005 Xcode 14にて以下の警告が出て、拡張ライブラリのコンパイルに失敗する。
ld: warning: -undefined dynamic_lookup may not work with chained fixups
- https://openradar.appspot.com/radar?id=5536824084660224
- WWDC22: Link fast: Improve build and launch times
Ventura同梱のRubyは警告を無視している: https://ruby.slack.com/archives/C02A3SL0U/p1667539569008269?thread_ts=1663959379.534979&cid=C02A3SL0U 警告を無視する案: https://github.com/nobu/ruby/tree/cc-wrap
chained fixupsが問題になるのは、シンボル参照時に参照先のシンボルがまだ読み込まれていないとき = アプリ起動時で複数のオブジェクトを一気に読むときだけなので、dynamic_lookupをdlopenによる動的ロードでのみ用いるRubyの場合は問題にならない: https://www.emergetools.com/blog/posts/iOS15LaunchTime https://github.com/ruby/ruby/pull/6193
使わなくする場合には bundle_loader で ruby/librubyを指定すればよい → fat binary問題と別の拡張ライブラリ問題
two-level namespace vs. flat namespace
- two level namespaceだと、他のライブラリと名前が衝突しても死ぬことがなくなる
- その代わり、-undefined dynamic_lookupをつけないと他のライブラリの関数を呼べなくなる
- flat namespaceだと他のシンボルと衝突
- rbsがOSのシンボルと当たって死んだ
- しかし、これが問題となる場合、どちらにせよ他のOSで問題になるのでは?
別の拡張ライブラリのシンボルを参照できない
- BigDecimalがやっていた
- 他にlibv8.gemとかがまだやってると思う -undefined dynamic_lookup https://bugs.ruby-lang.org/issues/18912
disable-sharedなrubyがfat binaryを読めない
--enable-sharedついてない環境でビルドした拡張ライブラリが--enable-shared付きの環境で動かないよ〜 fat gemで困るよ〜という話が :cry: https://github.com/rake-compiler/rake-compiler-dock/issues/87 https://ruby.slack.com/archives/C02A3SL0U/p1671524923910499
--disable-shared な ruby 実行ファイルには rb_cObject などのシンボルがあるので -bundle_loader でビルドできるが、それでできた .so を --enable-shared な ruby 実行ファイルには rb_cObject などがない(libruby.so にある)ので require に失敗する、みたいな? https://ruby.slack.com/archives/C02A3SL0U/p1671527703349829
そもそもfat binaryは遅い https://ruby.slack.com/archives/C02A3SL0U/p1671533399524569 https://www.clear-code.com/blog/2019/11/22.html https://github.com/ruby/bigdecimal/pull/148
https://bugs.ruby-lang.org/issues/18912
歴史的には様々なパッケージマネージャがバイナリ配布へと舵を切っていたので、fat binaryはやるしかないと思っているが…git
まとめ
解決案としては代替以下の通りになるんじゃないでしょうか
- dynamic_lookupがXcode 14で警告が出る → 警告を無視する
- two-level namespace vs. flat namespace → flat namespace
- 別の拡張ライブラリのシンボルを参照できない → 用途としてはレアなので、そういうgemにはもうちょっと手間をかけてもらってもいい気がする
- disable-sharedなrubyがfat binaryを読めない → enable sharedのみにする?
あと、前回の記事も考慮すると、
- 拡張ライブラリ読み込み用の dlopen に RTLD_GLOBAL でなく RTLD_LOCAL を渡す もやっていい気がする
追記
kateiさん曰く
これ思い出して最新のCLT(for Xcode 14.3)で試したら dynamic_lookup 使っても警告でなくなってました。バイナリ見る限りdynamic_lookupを指定するとchained-fixup自体が無効になるっぽいです。
とのこと。hsbtさんも Xcode 14.3 では Apple Silicon なマシンで Ruby 2.4 以降もビルドできるようになっているぽい と書かれており、どうやら解決したっぽい。Python側で大騒ぎしてくれたおかげなようなので感謝…。