b2con
はじめに
これはBinary 2.0 カンファレンスにおいて、浜地が発表したプレゼンを html 化したものです。 実行ファイルはb2con_hamaji.zipとして置いてあります。 Windows の環境があるかたはぜひこちらをご覧下さい。 改造済みSPSのソースはものすごい汚ないので見ると目がほげほげなので目薬を用意下さい。
Dynamic Programming Language C
- 私は誰? -
- Shinichiro Hamaji
- shinichiro.hamaji _at_ gmail.com
- 2005/12/02 Binary 2.0 カンファレンス
- SPS, SDL Presentation System
私は誰?
shinichiro.h / shinh など
そこらの趣味プログラマ
http://shinh.skr.jp/
好きなもの
→ はどうでもいい
嫌いなもの
似たコードの連続
こういうのとか
suite.addTest(test1); suite.addTest(test2); suite.addTest(test3); suite.addTest(test4); suite.addTest(test5);
嫌いなもの
こういうのとか
rb_define_method( rb_cFile,"atime",rb_file_atime,0); rb_define_method( rb_cFile,"mtime",rb_file_mtime,0); rb_define_method( rb_cFile,"ctime",rb_file_ctime,0); rb_define_method( rb_cFile,"chmod",rb_file_chmod,1); rb_define_method( rb_cFile,"chown",rb_file_chown,2);
Ruby の array.c より
あげくの果てには
こんなんなっちゃう
suite.addTest(test1); suite.addTest(test2); suite.addTest(test3); suite.addTest(test3); suite.addTest(test5);
すごい困る!
解決法
スクリプトでコード生成
ダサい
生成してから編集すると再生成しにくくてイヤン問題
(行数は増えるからおしごとではいいけど…)
(いいのか!?)
解決法
C/C++ のマクロ
CppUnit? とか
少しマシになっただけ
CPPUNIT_TEST_SUITE(HogeTest); CPPUNIT_TEST(testHoge1); CPPUNIT_TEST(testHoge2); CPPUNIT_TEST(testHoge3); CPPUNIT_TEST_SUITE_END();
解決法
そうだリフレクションだ
こんな感じができるといいな
void** funcs = read_exe(argv[0]); int i; for (i = 0; i < func_size; i++) { ((void (*)())funcs[i])(); }
でも C には…
リフレクションありませんからっ
Java を使う
→ 論外
なんとかやってしまおう
この話の本題
Dynamic Programming Language C
道のり
which $0
ldd
nm
c++filt
取得した関数のcall
→ これらを実行時に行う
which $0
私は誰?
自分のファイル名
自分のファイルハンドル
自分のモジュールハンドル
私は誰?
案外わからない
argv[0] を使う?
$PATH 使った場合はダメ
/bin/ls なのに argv[0] == ls
私は誰?
環境依存するしかない
Linux :
readlink("/proc/self/exe")
MacOSX:
_dyld_get_image_name
Win32 :
HMODULE h = LoadLibrary(NULL); GetModuleFileName(h, buf, 1024);
私は誰?
簡単なことでも意外と面倒
(バッド)ノウハウの山
- #ifdef の嵐
- dlinfo(3)がLinuxのmanに無いとか
- OSX では BFD が…とか
- WINEが…とか → ライブラリ化したい
詳しいことは省略して話します
道のり
which $0
ldd
nm
c++filt
取得した関数のcall
ldd
ロードされているDLL
フルパス
/lib/libc-2.3.5.so
ロードアドレス
0x41000000
→ 「私は誰?」以上に環境依存
- /proc/self/maps
- dl_iterate_phdr
道のり
which $0
ldd
nm
c++filt
取得した関数のcall
nm
オブジェクトファイルを見てシンボルを取ってくる
0805bf84 T bfd_make_readable 0805cc51 T bfd_make_section 0805d0c6 T bfd_make_section_anyway
本来なら一番面倒な部分
→ オブジェクトファイルは千差万別
libbfdが環境依存をほとんど吸収してくれる
libbfd
GNU binutilsのライブラリ
オブジェクトファイルを読み書きできる
ex. シンボル、行情報、etc...
多数の対応フォーマット
ちょっと環境依存な部分も
GPL
道のり
which $0
ldd
nm
c++filt
取得した関数のcall
c++filt
C++のシンボルは読めない
デマングル
? _ZN7DtrFile9load_symsEv
! DtrFile?::load_syms()
GNU libiberty の cp_demangle
→ 使いにくいAPIだが…
c++filt
副産物
関数の引数の型がわかる
でも返り値の型はわからない
- イマイチ
- D言語はわかる
クラス名がわかる
- 特定クラスのメソッド一覧など
道のり
which $0
ldd
nm
c++filt
取得した関数のcall
関数 call
関数のシグネチャが決まっていれば簡単
void* fp = get_func("puts"); ((int (*)(char*))fp)("hello");
関数 call
関数のシグネチャが決まっていれば簡単
int i; for (i = 0; i < func_size; i++) { ((void (*)())funcs[i])(); }
関数 call
わかっていない場合
libffi
→ GCJ で利用されている
ffcall
→ GNUStep で利用されている
libffi
void* fp = puts; void* args = hogehoge; ffi_type* ats[1]; ats[0] = &ffi_type_pointer; int ret; ffi_cif cif; ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1, &ffi_type_sint32, ats); ffi_call(&cif, FFI_FN(fp), &ret, args);
→ 動的ディスパッチが可能
まとめ
以下のようにリフレクションを実現
EXEとDLLを取得
libbfd で関数アドレスを取得
C++ ならデマングリング
libffiやffcallで関数起動
問題点
JavaやRubyなどと比較
可搬性にやや難
→ 努力すればカバーできる
わかりにくいエラーメッセージ
→ 最悪 SegFault?
言語に統合されていない
構造体メンバが見れない
→ シリアライズができない。今後の課題
いいところ
楽しい!
→ 自己満足でいいじゃないか
→ 開きなおり
活用実例
しー言語
適当に作ったデモ言語
C の関数をバインディング無しで実行
Linux, FreeBSD, NetBSD, OpenBSD, Solaris, MacOSX, Windows で動作
活用実例
しー言語
#!./she puts "hello world!" !load "m" dx = cos 3.2 printf "%f" dx
言語というよりバッチ
! ではじまるのが予約語
変数の型は先頭の文字で判定
- d => double, i => int, ...
- ダメな意味で低レベル
活用実例
Dynamic Test Runner
.o ファイルをロード・再配置
- 再配置時にassertを乗っとる
- 勝手に abort させないため
内部の dtr_test を含む関数をかたっぱしから実行
./dtr test/*.o test/*.a -lm -lz
→ おてがる
Dynamic Test Runner
発想
テストに時間かけるのはバカらしい
俺はテストがしたいだけなんだ!
テストフレームワークの勉強がしたいんじゃない!
Dynamic Test Runner
書きたい時にすぐ書く
外部ファイル一切不要
- dtr本体だけ
dtr_test を含む関数をいきなりどっかに書く
どこからも呼び出されないからリンクされれば消える
Dynamic Test Runner
現状
環境依存
- Linux, FreeBSD 以外では未確認
- CPU も x86 だけ
対応言語はCとC++とObjC
- GCJとDも対応を実装中
Ruby/C++, Ruby/D
Ruby からクラスを直で
SWIGはいいものだが正直めんどい
Objective-C++ みたいに
Java & Groovy みたいに
できるといいな
ちょっと作ってみただけ
Ruby/C++
使用例
C++側
class BankAccount { public: explicit BankAccount(int d); int dollars(); void set_dollars(int d); void deposit(int d); void withdraw(int d); private: int dollars_; };
Ruby/C++
使用例
Ruby側
require 'rubycpp' BankAccount = Cplusplus::declare_class( "bankaccount", "BankAccount", 4) b = BankAccount.new(100) b.deposit(300) p b.dollars
→ バインディング不要
Ruby/C++
問題点
- インライン関数呼べない
- C++のシンボルには返り値情報が無い
- 親クラスのメソッドを呼んでない
- オーバーロードを解決していない
- std::string を扱ってない
- たぶん Linux のみ
終わり
言いたかったこと
コピペでコード書きたくない!
オブジェクトファイルの情報をもっと有効活用しよう!
なんか楽しいし!
SPS
SDL Presentation System
- Zinniaさん作
- FPSカウンタのあるプレゼンツール
SDL?
- Simple Directmedia Layer
- クロスプラットフォーム
- 要はゲームライブラリ
プレゼン資料もバイナリ配布の時代