Hatena::ブログ(Diary)

miura1729の日記 このページをアンテナに追加 RSSフィード

2008-09-22

llvmrubyを追ってみた

19:43 |  llvmrubyを追ってみたを含むブックマーク  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を渡すと面白いことが出来ることが判りましたが、また悲しい思いをしてしまいました。

追記

http://sources.redhat.com/cgi-bin/cvsweb.cgi/src/winsup/cygwin/dlfcn.cc?rev=1.39&content-type=text/x-cvsweb-markup&cvsroot=src

によると、CygwinもdlopenでNULLを渡したときの動作はちゃんと作ってありそうです。そうすると、なぜ動かないんだろう?

トラックバック - http://d.hatena.ne.jp/miura1729/20080922/1222080204