stacktrace

beroさんに教えていただいたことをやっと理解しはじめました…なるほど… obj の段階で demangle することはできますね…ていうかあの時きちんと man 読んだつもりだったんだけど寝てたんだろうか… (http://d.hatena.ne.jp/shinichiro_h/comment?date=20040710#c)

結論としては、例外が飛んだ場所の正確な行情報が得られる気がする。

> ar x libphobos.a                # object 取り出し
> objcopy
    --redefine-sym _d_throw@4=_d_throw_old@4 \
    --rename-section .gnu.linkonce.t_d_throw=.gnu.linkonce.t_d_throw_old \
    deh2.o                        # _d_throw を捨てる
> cat d_throw.d                   # 新しい _d_throw だよ
extern (Windows) {
  void _d_throw_old(Object* o); 
  void _d_throw(Object* o) {
    printf("error\n");
    _d_throw_old(o);
  }
}
> dmd -c d_throw.d                # コンパイル
> ar crus libphobos.a *.o         # 新しいのを使って phobos 作り直し
> cat test_error.d                # 実験のためのもの
int main() {
  throw new Error(`hoge`);
  return 0;
}
> dmd -c test_error.d             # コンパイルして実行します
> gcc -o test_error test_error.o libphobos.a -lpthread -lm
> ./test_error
error
Error: hoge

と、 _d_throw を奪うことができた。もう少し凝ったことをやってみる。 d_throw.d をこんな感じに。

import std.c.stdio;
extern (C) FILE* popen(char* com, char* type);
extern (C) int pclose(FILE* fp);

extern (Windows) {
  void _d_throw_old(Object* o);
  void _d_throw(Object* o) {
    int* raddrp;
    asm { mov raddrp, EBP ; }
    printf("errored\n");
    printf("ebp is %x\n", raddrp);
    int raddr = *(raddrp+1)-5;
    printf("address is %x\n", raddr);
    FILE* fp = popen(`addr2line -e test_error`, `w`);
    fprintf(fp, "%x\n", raddr);
    pclose(fp);
    _d_throw_old(o);
  }
}

呼び出し元アドレスをひっこ抜いてきて、それを addr2line に渡してるだけ。実行。

> ./test_error
errored
ebp is bfffed54
address is 804a2c8
/home/i/phobos/test_error.d:4
Error: hoge

なんか行情報がずれてるのは DMD の出す行情報がもともとずれてるせい、たぶん。 GDC でやってみると、

> ./test_error
errored
ebp is bfffecf8
address is 80493b1
/home/i/phobos/gdc/test_error.d:2
zsh: abort      ./test_error

落ちられても困るけど行情報はあってるね。落ちてる理由は d_throw.d をめんどいから DMDコンパイルしてるからじゃないかな。たぶんマジメに C で書けば問題ない気が。

追記: ちなみに Linux なのに extern(Windows) が必要になる DMD版 libphobos.a はどうなのかという感もあります。 GDC のなら extern(C) にすれば OK。

もいっこ追記: d_throw を C で書き直した。でも落ちるのは相変わらずでした。まあ別にいいか。

#include 

void _d_throw_old(void* o);
void _d_throw(void* o) {
  int* raddrp;
  asm("mov %%ebp, %0" : "=g" (raddrp));
  printf("errored\n");
  printf("ebp is %x\n", raddrp);
  int raddr = *(raddrp+1)-5;
  printf("address is %x\n", raddr);
  FILE* fp = popen("addr2line -e test_error", "w");
  fprintf(fp, "%x\n", raddr);
  pclose(fp);
  _d_throw_old(o);
}
なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h