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 で書き直した。でも落ちるのは相変わらずでした。まあ別にいいか。
#includevoid _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); }