OS Xの"DYLD_*"環境変数
先日のCocoa勉強会で話してきたので整理しとく。
まとめ
dyldと"DYLD_"ではじまる環境変数
dyldはOS X/iOSのダイナミックリンカで、ライブラリやフレームワークをロードするプログラムです。man DYLD(1)にあるように、"DYLD_"ではじまる環境変数で動作を変更することができます。UnixやLinuxのLDでいうところのLD_LIBRARY_PATHやLD_PRELOADの類ですね。
すっごく雑に分けると、これらの環境変数は
- ライブラリの探索方法を指示
- DYLD_*_PATH: 探す場所を指示する。
- DYLD_IMAGE_SUFFIX: デバッグ用などに用意したライブラリのsuffixを指示する。
- 実行中の情報を表示
- DYLD_PRINT_*: ロードしたものや実行環境の情報などを標準エラー出力に表示する。
- その他
- DYLD_INSERT_LIBRARIES: LD_PRELOAD
- DYLD_FORCE_FLAT_NAMESPACE, DYLD_BIND_AT_LAUNCH: リンカの解決方法を指定する。
という感じになります。
- DYLD_FRAMEWORK_PATH: 開発中のRubyCocoa.frameworkの場所を指示する。
- DYLD_PRINT_LIBRARIES_POST_LAUNCH: テストスクリプトが正しく開発中のRubyCocoa.frameworkやrubycocoa.bundleをロードしたかを確認する。
の2つを使っています。プログラム的には次のようなrubyスクリプトです。
ENV['DYLD_FRAMEWORK_PATH'] = path_to_devel_framework ENV['DYLD_PRINT_LIBRARIES_POST_LAUNCH'] = '1' libs = Open3.popen3("ruby -rosx/cocoa -e ''") do |stdin, stdout, stderr| # read stderr end # test loaded rubycocoa framework/bundle paths from stderr
DYLD_PRINT_LIBRARIESとDYLD_PRINT_LIBRARIES_POST_LAUNCHのちがいは、プロセスの起動時にロードしたものを表示するか否かです。
% DYLD_PRINT_LIBRARIES=YES ruby2.2 -e 'require "zlib"' dyld: loaded: /opt/local/bin/ruby2.2 dyld: loaded: /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation dyld: loaded: /opt/local/lib/libruby.2.2.0.dylib dyld: loaded: /usr/lib/libSystem.B.dylib : dyld: loaded: /opt/local/lib/ruby2.2/2.2.0/x86_64-darwin14/enc/encdb.bundle dyld: loaded: /opt/local/lib/ruby2.2/2.2.0/x86_64-darwin14/enc/trans/transdb.bundle dyld: loaded: /opt/local/lib/ruby2.2/2.2.0/x86_64-darwin14/thread.bundle dyld: loaded: /opt/local/lib/ruby2.2/2.2.0/x86_64-darwin14/date_core.bundle dyld: loaded: /opt/local/lib/ruby2.2/2.2.0/x86_64-darwin14/zlib.bundle dyld: loaded: /opt/local/lib/libz.1.dylib % DYLD_PRINT_LIBRARIES_POST_LAUNCH=YES ruby2.2 -e 'require "zlib"' dyld: loaded: /opt/local/lib/ruby2.2/2.2.0/x86_64-darwin14/enc/encdb.bundle dyld: loaded: /opt/local/lib/ruby2.2/2.2.0/x86_64-darwin14/enc/trans/transdb.bundle dyld: loaded: /opt/local/lib/ruby2.2/2.2.0/x86_64-darwin14/thread.bundle dyld: loaded: /opt/local/lib/ruby2.2/2.2.0/x86_64-darwin14/zlib.bundle dyld: loaded: /opt/local/lib/libz.1.dylib %
OS X 10.11での制限
そろそろ10.11がリリースされそう(当時beta 8)なのでRubyCocoaの検証をしていたところ、上記のテストスクリプトのフレームワークのパスの検証が通らないことに気付きました。どうもDYLD_FRAMEWORK_PATH環境変数が機能していないようです。
試してみると、
% uname -r 15.0.0 # 10.11 GM % ruby -e "system({'DYLD_AAA' => 'AAA', 'AAA_DYLD_BBB' => 'BBB'}, \ '/usr/bin/printenv | grep DYLD')" AAA_DYLD_BBB=BBB %
と、"DYLD_"ではじまる環境変数が子プロセスに反映されないようになっているようです。betaの無印のときはこんなことなかったと思うんだけどなあ。もちろんOS X 10.10では"DYLD_AAA"も出力されます。
仕方ないので、次のような一時ファイルのシェルスクリプトを生成して実行することを考えています。
# temp shell script for testing with DYLD_ environments DYLD_FRAMEWORK_PATH=path ${RUBY} -rosx/cocoa -e ''
さすがにこれで解消するのですが、なんかすごく不毛な気持ちになります。もうちょっとスマートに書けるような…
よい方法があれば教えてください!だいぶ切実です。
(2015-09-19追記: ついったで @n0kada さんに`env`コマンドでいけることを教えてもらいました。ありがとうございます!)
また、WWDC 2015のセッション706:"Security and Your Apps"(ASCIIwwdc)で説明されているように、保護される領域(/Systemや/usr/binなど)にあるバイナリではDYLD_*環境変数は問答無用で無視されるようになっています。
例を示すと、
% uname -r 15.0.0 # 10.11 GM % cat dyld_env.rb # print DYLD_* environments ENV.each_pair do |k, v| puts "#{k}=#{v}" if k =~ /DYLD/ end % DYLD_AAA=AAA AAA_DYLD_BBB=BBB /usr/bin/ruby dyld_env.rb AAA_DYLD_BBB=BBB % DYLD_AAA=AAA AAA_DYLD_BBB=BBB /opt/local/bin/ruby2.2 dyld_env.rb dyld: warning, unknown environment variable: DYLD_AAA DYLD_AAA=AAA AAA_DYLD_BBB=BBB %
感想
セキュリティの観点としてはいままでがざるざるだったこともあり、制限は仕方ないし正しい方向だとは思います。だけど、プログラム書く人間としてはめんどくさいなあという気持ちも隠せません。
人によってはぜんぜん影響ないかもしれませんが、思わぬワナにはまるかもなのでご注意を。