gdb hacks - gdb を電卓の代わりに使う
gdb hacks 第 3 回。今回は以下のトピックを扱います。
- gdb を電卓の代わりに使う
- gdb が扱うシンボルの型情報
筆者は gdb を電卓代わりによく使っています。以下のように出力フォーマットを手軽に指定できること、C の表現式がそのまま使えること、前の計算結果を簡単に参照できることなど、他の電卓プログラムにはない魅力があります。bc や irb と比較しても gdb はよりプログラマ向きの電卓なのではないかと思います。
% gdb (gdb) p 123+456+789 $1 = 1368 (gdb) p 123+0456+0x789 $2 = 2354 (gdb) p/o $2 $3 = 04462 (gdb) p/x $3 $4 = 0x932 (gdb) p/t $4 $5 = 100100110010 (gdb) p/x 'a' $6 = 0x61 (gdb) p/c 0x41 $7 = 65 'A' (gdb) p 123e4/567.8 $8 = 2166.2557238464251 (gdb) p "hoge" evaluation of this expression requires the target program to be active
(わざとらしいですが)最後に文句を言われてしまいました。配列や文字列といったオブジェクトのメモリ領域を確保したり、関数を実行したりするには、ターゲットプロセスのコンテキストが必要になります。
ターゲットプロセスはデバッグできるプログラムであればなんでもいいので、以下のように main() だけを含む a.out を適当に作って使ってみます。
% echo "int main(){returnh 0;}" | gcc -xc - % gdb a.out (gdb) start Breakpoint 1 at 0x804835a Starting program: /home/yaegashi/a.out 0x0804835a in main () (gdb) p "hoge" $1 = "hoge" (gdb) ptype $1 type = char [5] (gdb) p printf("%s%s\n", $1, $1) hogehoge $2 = 9 (gdb) p {(char*)"ls", (char*)"/", (char*)0} $3 = {0x804a028 "ls", 0x804a038 "/", 0x0} (gdb) p execvp("ls", $3) app boot etc initrd media opt root srv tmp var bin dev home lib mnt proc sbin sys usr Program exited normally. The program being debugged stopped while in a function called from GDB. When the function (execvp) is done executing, GDB will silently stop (instead of continuing to evaluate the expression containing the function call). (gdb)
gdb 電卓コンテキスト用の a.out ができましたので、調子に乗って libm の数学関数を使うことを考えてみます。a.out に libm をリンクせずとも、環境変数 LD_PRELOAD を使って ld.so に libm.so を読み込ませてしまえば、gdb から libm の関数にアクセスすることができます。
% gdb a.out (gdb) set environment LD_PRELOAD=/lib/libm.so.6 (gdb) start Breakpoint 1 at 0x804835a Starting program: /home/yaegashi/a.out 0x0804835a in main () (gdb) p sqrt(2.0) $1 = 14624 (gdb) p/f $1 $2 = 2.04925887e-41
しかし、なんだか意図したものとは全然違う結果が返ってきました。
これは ptype を使って sqrt の型について調べてみれば原因がわかります。
(gdb) ptype sqrt type = int ()
このように gdb は sqrt 関数を整数値 int を返す関数だと考えていることがわかります。本来なら sqrt は浮動小数点値を返すので戻り値は浮動小数点値スタックのトップ (st0) に置かれるはずなのですが、gdb は EAX レジスタの内容を戻り値として報告してしまっているのです。
これを是正するには gdb に sqrt の正しい型情報を教えなくてはなりません。Debian にはデバッグ情報を含む glibc を収めた libc6-dbg というパッケージがありますので、こちらの libm.so.6 を使ってみます。
% apt-get install libc6-dbg % gdb a.out (gdb) set environment LD_PRELOAD=/usr/lib/debug/libm.so.6 (gdb) start Breakpoint 1 at 0x804835a Starting program: /home/yaegashi/a.out 0x0804835a in main () (gdb) ptype sqrt type = int () (gdb) ptype __sqrt type = double (double) (gdb) p __sqrt(2.0) $1 = 1.4142135623730951
sqrt はあいかわらずだめでしたが、__sqrt というシンボルでは正しく型情報が設定されており、期待どおりの値が返ってきることがわかります。
これで gdb 電卓でも数学関数が使えるようになり少し実用度が増しましたが、__sqrt(2.0) などという表記はあまりスマートとはいえません。実際には gdb に望みの型情報を与えるもうすこしまともな方法があるような気がしているので、引き続き調査してみる予定です。
つづく。
カテゴリ
gdb hacksトラックバック(0)
このブログ記事を参照しているブログ一覧: gdb hacks - gdb を電卓の代わりに使う
このブログ記事に対するトラックバックURL: http://www.keshi.org/mt/mt-tb.cgi/33
コメントする