2009-11-29

C++: BFDを使ってみる その2(たぶん最後)

昨日の続き。いろいろできそうだなと思ったが、少し落とし穴があった・・・

【落とし穴その1】
mprotectで取得した関数のアドレスを実行可能とか読み込み可能とかにしないと動かせない。
よく考えればあたりまえか・・・
っが、MingwとかCygwin(要するにWindows環境)にはそげなものがない・・・

【落とし穴その2】
dlopenとかdlsymとかその辺の話。ある意味当然ではあるが、共有ライブラリじゃないと読み込めない。
上記の問題から、BFDでシンボル読み込んで、シンボル名をデマングルして、呼び出し時にシンボル名に変換してやればいいかなとか思ったんだけど、BFDはWindowsのDLLに対応していない。Mingwの-sharedオプションで作ったにもかかわらずである。
(本当かどうかはよく知らん、調べてない。PEに変換しているからか?)

この辺を試してみよう。Windows限定の話になるけど・・・

余談
折角なので(備忘録も兼ねて)、BFDの使い方をメモ。
こんな感じの流れ。
bfd_init(); //初期化。一回だけでいい。
bfd* abfd = bfd_openr("hoge.o", NULL); // .a とか .soでもいいと思う
if (bfd == 0) {
  bfd_perror("failed to open"); // error
  return; // 失敗したので
}
// オブジェクトのフォーマットチェック
// アーカイブとかもヘッダの中にあるので、適当に必要なチェックで。
// .aとかのアーカイブだと、.oが複数個入っているので、それ用の処理がいる
if (!bfd_check_format(abfd, bfd_object)) { 
  bfd_perror("format error"); // .o ファイルじゃないよ
  return; // それじゃ!
}
// 中身を読み込む
if (!(bfd_get_file_flags(abfd) & HAS_SYMS)) {
  return; // シンボルなし
}
long storage = bfd_get_symbol_upper_bound(abfd); // サイズを取得
if (storage < 0) {
  bfd_perror("bfd_get_symbol_upper_bound"); //  中身なし?
  return;
}
asymbol** symbol = (asymbol**)malloc(storage); // メモリ確保(new じゃだめなのかね?)
int symbolCount = bfd_canonicalize_symtab(abfd, symbol);
if (symbolCount < 0) {
  bfd_perror("no symbol"); // シンボルがない
  return;
}
// シンボルを読み出す
for (int i = 0; i < symbolCount; i++) {
  asymbol asym = symbol[i];
  int value = bfd_asymbol_value(asym); // シンボルの値(何が入ってるんだろう?)
  const char* name = bfd_asymbol_name(asym); // シンボル名(名前マングルされてる)
  if (name == 0 || ::strlen(name) == 0) continue; // シンボル名が無ければ次
  symbol_info info;
  bfd_symbol_info(abfd, asym, &info); // シンボル情報を取得(中身はよく知らん)
  // 関数ポインタ
  int* fp = (int)file->contents // .o ファイルの中身(バイナリで読み込む)
            + asym->section->filepos // セクションの場所?
            + value;
  // 多分セクションデータとかを自分自身に配置しないと
  // (略)
  mprotect((void*)(((int)fp+4095) & ~4095 - 4096),
          4096, PROT_READ | PROT_WRITE | PROT_EXEC);
  /* こっちのが正しい?(どっちにしろMingwでは動かん
  long pagesize = sysconf(_SC_PAGESIZE);
  char *p = (char *)((long)s & ~(pagesize - 1L));
  mprotect(p, pagesize * 10L, PROT_READ|PROT_WRITE|PROT_EXEC);
  */
  //適当にキャストして、実行
  typedef void (*fnc)(); // とりあえず、戻り値、引数無しと仮定
  fnc fncP = (fnc)fp;
  fncP(); // 実行
}
多分こんな感じ。実際に動かせないので、正しいかどうかは保障できない。
(他人のコードから推測、というかほぼ丸写し、しただけだし・・・)

2009/11/30 追記

落とし穴その1は大嘘であった。
mprotectで実行可能にしなくてもWindowsだと普通に動いた。Cで作った関数だが。
C++側だとどうもいろいろ面倒なのだろう。コンストラクタとか、継承とか。
(確かに、仮想関数のアドレスとかどうやって取るとか、thisポインタを渡すとか、考えればいくらでも解決しないといけない問題がありそう)
憶測だが、コンストラクタのアドレスが分かったところで、クラス全体の情報が取れているわけではないのだろう。ヒープに取るメモリの量とかコンストラクタだけじゃ取れそうにないし。
構造体がBFDから作れるか試してから探した方がいいだろうか?
(C互換の構造体だと単なるメモリの塊というイメージだが・・・)
気合入れればいけそうなのだが、頭と気力が足りない感じ・・・前者が特に・・・orz

No comments:

Post a Comment