共有ライブラリの名前とロードされたアドレスを取得する (実行時 ldd 相当)

ldd と違ってどのアドレスにロードされているかの情報も欲しい。 Linux だと /proc/self/maps に書いてある情報。

  • Linux では dl_iterate_phdr でにコールバック関数を渡してやる。
  • FreeBSD では dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &lmap) などとして link_map という構造体をゲットすればいい。
  • MacOSX では dyld(3) 。 _dyld_image_count と _dyld_get_image_name と _dyld_get_image_vmaddr_slide を使えばいい。
  • Win32 では今のところどうすればいいやら。
  • NetBSD では見当らない。 dlctl(3) というのが今作ってるところだ、って man に書いてあって、ヘッダを見ると DL_GETLIST なんてのがあるので、これでできるかもしれない。

ところで dlinfo が man に無いのはヒドい話だと思う。

自分のプロセスの名前を取得する

これ、案外できない。 argv[0] ですむ場合が多いんだけど。

  • Linux では /proc/self/exe くらいかな。
  • FreeBSD では dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &lmap) で自分自身も入ってくる。
  • MacOSX は _dyld_* で自分自身も入ってくる。
  • Win32 では GetModuleHandle(NULL) の返り値に対して GetModuleFileName 。
  • NetBSD は不明。 dlctl が動けばこれは FreeBSD 同様に引っこ抜けると思われる。

共有ライブラリをオープンする

基本は dlopen 。まぁよくやられてるのでサンプル豊富。

  • Linux, FreeBSD, NetBSD では dlopen 。
  • MacOSX では dlopen か NSAddImage(name, NSADDIMAGE_OPTION_WITH_SEARCHING) かな。
  • Win32 では LoadLibrary(name)

ていうかまぁ、 dlsym もサンプル豊富なので省略。

起動後に共有ライブラリの名前とアドレスを取得する

最初のができるなら問題無いけど、 dlopen の返り値使えばもっと速かったりとか。

  • LinuxFreeBSD は dlopen の返り値を dh として、 dlinfo(dh, RTLD_DI_LINKMAP, &lmap) 。
  • MacOSX では、うーんよくわからない。 _dyld_* は起動時の状態しか取れないように見える。
  • Win32 では LoadLibrary の返り値を dh として、 GetModuleFileName(dh, buf, 1024) とか。ただロードアドレスが取れない。
  • NetBSD ではたぶんできない。同じく dlctl が実装されれば、って話。

ちなみに、 dlfcn 環境では、関数名を一つでもヒントとして渡せば、 dlsym の返り値を dladdr に渡してやれば問題なくわかる。

ファイル名からシンボル一覧を取得する (nm相当)

ぶっちゃけ nm しておいてパーズでもいい、が…

libbfd を使うのが基本。基本的には、 bfd_read_minisymbols を使ってそれで得られたものを bfd_minisymbol_to_symbol で変換するのがいいと思う。 bfd_read_minisymbols の第二引数は 0, 1 で指定するダイナミックシンボルを見るか否か。基本的には片方がゼロ個ならもう一方を読めばまず取得できると思う。ところで strip した共有ライブラリから nm -D でシンボル取得できるって知らんかったです…

また、 bfd_get_symtab_upper_bound, bfd_canonicalize_symtab, bfd_get_dynamic_symtab_upper_bound, bfd_canonicalize_dynamic_symtab を使う手もある。速度差はほとんどないように見える、うちのマシン (Celeron 1.7GHz, linux-2.6.11-mm4, glibc-2.3.5) ではシンボルが 8514個ある libkdecore.so を 100 回読んでどっちも 0.6秒くらいで、 0.02秒とかそのくらい minisymbol のやり方の方が速かった。 Win32 でのみ後者が速かった。んで前者の方が省メモリ。

  • Linux, FreeBSD, NetBSD, MacOSX, Win32 ともに基本的に前者のやり方でいい。
  • MacOSX で後者をやると mach-o.c の中で謎の assertion failure が出る。たぶん libbfd のバグを踏んでいる。また、 dynamic 系は常に失敗する。 OSX 標準添付の nm に -D が無いあたりとも関係ありそう。詳しくはTODO。
  • Win32 で dynamic 系をやると返って来なくなる。(但しWINE)

まとめると、 minisymbol 系を使った方がたぶん無難。 Linux, FreeBSD では dynamicバージョンと通常バージョン両方やってみると良い。 MacOSXWin32 では通常バージョンのみが良いと思う。

関数ポインタから任意関数を呼び出す

関数の型がコンパイル時に決まるなら ( (int (*) (const char* s))0x80482cc)("hello"); とかでいい。(ちなみに puts がここにロードされてた)

たぶん libffi でイケるけど Linux 以外では未確認。

なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h