2008-09-22
llvmrubyを追ってみた
|Cygwinのllvmrubyで、test_ruby_vm.rbを動かすと、
ERROR: Program used external function 'rb_ary_store' which could not be resolved!
となる不具合を追ってみました。
このエラーを出すのは、lib/ExecutionEngine/JIT/Intercept.cppのJIT::getPointerToNamedFunctionです。この中で、
// If it's an external function, look it up in the process image... void *Ptr = sys::DynamicLibrary::SearchForAddressOfSymbol(NameStr); if (Ptr) return Ptr;
とシンボルストリングをサーチして対応する値を得ようとしているみたいです。
sys::DynamicLibrary::SearchForAddressOfSymbolは、lib/System/DynamicLibrary.cppにあります。検索しているのはここと思います。
// Now search the libraries. for (std::vector<void *>::iterator I = OpenedHandles.begin(), E = OpenedHandles.end(); I != E; ++I) { //lt_ptr ptr = lt_dlsym(*I, symbolName); void *ptr = dlsym(*I, symbolName); if (ptr) return ptr; }
OpenedHandlesにこれまでdlopenしたファイルハンドルが入っています。このファイルハンドルを使って名前からアドレスを得ようとしているわけです。では、どこで、dlopenしているか調べるためにdlopenでgrepしてみました。すると、SearchForAddressOfSymbolと同じlib/System/DynamicLibrary.cppにある、DynamicLibrary::LoadLibraryPermanentlyが見つかりました。こんな定義です。
bool DynamicLibrary::LoadLibraryPermanently(const char *Filename, std::string *ErrMsg) { void *H = dlopen(Filename, RTLD_LAZY|RTLD_GLOBAL); if (H == 0) { if (ErrMsg) *ErrMsg = dlerror(); return true; } OpenedHandles.push_back(H); return false; }
LoadLibraryPermanentlyを使っているところをgrepすると、みんなFilenameの処に0(null)を入れて呼んでいます。dlopenのファイル名に0を入れると、特別な動作をするようです。
http://docs.hp.com/ja/B2355-60104-06/dlopen.3C.htmlによると
file の値が 0 の場合、 dlopen は、「グローバルシンボルオブジェクト」 上に handle を与えます。このオブジェクトは、オリジナルの a.out、 a.out とともにプログラムの起動時にロードされたすべてのオブジェクト、および RTLD_GLOBAL フラグを指定した dlopen 操作を使用してロードされるすべての オブジェクトの順に構成されるオブジェクトのセットからのシンボルへのアクセス を提供します。この中の最後のオブジェクトのセットは実行中に変化する 可能性があるので、 handle により指定されるセットも動的に変化する可能性が あります。
そういうことで、dlopenを0を使って呼び出して現在実行中のRubyのプログラム、llvmrubyの拡張プログラム、その他もろもろのシンボルテーブルにアクセスできるようになるようです。
ところが、Cygwinのdlopenはそのような気の利いた機能が無いんじゃないかなと思います。確かめたわけじゃないのですが。
そのため、シンボルテーブルにアクセスできず"ERROR: Program used external function 'rb_ary_store' which could not be resolved!"なんてエラーが出てしまったんじゃないかなと思います。
独自でシンボルテーブルを作る必要がありそうです。
まとめ
dlopenに0を渡すと面白いことが出来ることが判りましたが、また悲しい思いをしてしまいました。
追記
によると、CygwinもdlopenでNULLを渡したときの動作はちゃんと作ってありそうです。そうすると、なぜ動かないんだろう?
- 13 http://www.rubyist.net/~kazu/samidare/
- 1 http://a.hatena.ne.jp/fujita-y/
- 1 http://a.hatena.ne.jp/h_sakurai/
- 1 http://azby.search.nifty.com/blogsearch/search?cflg=検索&q=BEGIN&htmltype=2&type=&ss=up&chartype=0
- 1 http://b.hatena.ne.jp/entry/http://llvmruby.org/wordpress-llvmruby/
- 1 http://d.hatena.ne.jp/
- 1 http://d.hatena.ne.jp/diarylist?of=50&mode=rss&type=public
- 1 http://d.hatena.ne.jp/keyworddiarymobile/Ruby
- 1 http://reader.livedoor.com/reader/
- 1 http://www.google.co.jp/search?hl=ja&client=firefox-a&rls=org.mozilla:ja:unofficial&q=Error+calling+bind()&btnG=検索&lr=lang_ja