アセンブラで遊ぶ時に便利なgdb設定
アセンブラで遊ぶ時に便利な ~/.gdbinit を紹介します。まず ~/.gdbinit を次のように記述してください。
# # ~/.gdbinit # # .so を shlib コマンドで手動で読み込む # set auto-solib-add 0 # スレッド生成時のSIG32でブレークしない handle SIG32 nostop # ニモニック構文の選択 # set disassembly-flavor intel set disassembly-flavor att # フラグレジスタの可読化関数 define pf printf "eflags: %s%s%s%s%s%s%s%s%s (= 0x%08u)\n",\ $eflags & 2048 ? "O":"-",\ $eflags & 1024 ? "D":"-",\ $eflags & 512 ? "I":"-",\ $eflags & 256 ? "T":"-",\ $eflags & 128 ? "S":"-",\ $eflags & 64 ? "Z":"-",\ $eflags & 16 ? "A":"-",\ $eflags & 4 ? "P":"-",\ $eflags & 1 ? "C":"-",\ $eflags end # stepiしてフラグを表示 define sip si pf end # stepiしてフラグ+レジスタを表示 define sips si pf reg end # 次に実行する命令(とその先10命令)のアセンブリリストを出力する define al x/11i $pc end # レジスタを表示 define reg printf "eax:%08x ebx:%08x ecx:%08x edx:%08x\nedi:%08x esi:%08x ebp:%08x esp:%08x\n", \ $eax, $ebx, $ecx, $edx, $edi, $esi, $ebp, $esp end # すべてのレジスタを表示 define allreg reg printf "CS:%04x DS:%04x SS:%04x ES:%04x FS:%04x GS:%04x EIP:%08x", \ $cs, $ds, $ss, $es, $fs, $gs, $eip printf "\n" pf printf "\ncurrent instruction:\n " x/1i $pc end define myinit source ~/gdbcom end
さらに、~/gdbcom というファイルを次のように記述します。
break main # 停止時にPCの指している命令を表示 display/i $pc
実行バイナリをロードしたら (gdb) myinit を実行し、あとは適宜 disas して結果リストを見たりしながら、自分で定義したコマンド sip で読み進めていきます。
(gdb) myinit (gdb) r (gdb) sip 1: x/i $pc 0x8048301 <Strcmp+13>: scas %es:(%edi),%al %eflags = --ITS-AP- (918) (gdb) sip 1: x/i $pc 0x8048302 <Strcmp+14>: jne 0x804830c <Strcmp+24> %eflags = --IT-Z-P- (838)
如何でしょう?
混合モード表示
VC++などの高機能なデバッガでは、一般に「混合モード」などと呼ばれる、高水準言語のソースコードと対応するアセンブリリストを混ぜて表示するモードがあります。しかし、我らがgdbにはもちろん!そんな便利な機能はないんですよね〜。
なんとかそれを模倣する方法はないかと考えてみました。結果、どうにかなりました。以下の方法です。
バイナリとソースコードを同じ場所に置き、
$ objdump -CxS --prefix-addresses a.out
としましょう。-CxS は --demangle --all-headers --source と同じ意味です。-x をつけないと、-d つまり --disassemble を書いたのと同じ意味になりますが、その場合少し出力が減るので、場合によっては見やすいかもしれません。
たとえば次のような出力になります。
int foo(C* c) { 080483d0 <foo(C*)> push %ebp 080483d1 <foo(C*)+0x1> mov %esp,%ebp 080483d3 <foo(C*)+0x3> sub $0x8,%esp int ret = 0; 080483d6 <foo(C*)+0x6> movl $0x0,0xfffffffc(%ebp) for(int j=0; j<5; ++j) { 080483dd <foo(C*)+0xd> movl $0x0,0xfffffff8(%ebp) 080483e4 <foo(C*)+0x14> cmpl $0x4,0xfffffff8(%ebp) 080483e8 <foo(C*)+0x18> jle 080483ec <foo(C*)+0x1c> 080483ea <foo(C*)+0x1a> jmp 0804840c <foo(C*)+0x3c> ret += c->b->a->i(); 080483ec <foo(C*)+0x1c> sub $0xc,%esp 080483ef <foo(C*)+0x1f> mov 0x8(%ebp),%eax 080483f2 <foo(C*)+0x22> mov (%eax),%eax 080483f4 <foo(C*)+0x24> pushl (%eax) 080483f6 <foo(C*)+0x26> call 08048470 <A::i() const> 080483fb <foo(C*)+0x2b> add $0x10,%esp 080483fe <foo(C*)+0x2e> mov %eax,%edx 08048400 <foo(C*)+0x30> lea 0xfffffffc(%ebp),%eax 08048403 <foo(C*)+0x33> add %edx,(%eax) 08048405 <foo(C*)+0x35> lea 0xfffffff8(%ebp),%eax 08048408 <foo(C*)+0x38> incl (%eax) 0804840a <foo(C*)+0x3a> jmp 080483e4 <foo(C*)+0x14> } return ret; 0804840c <foo(C*)+0x3c> mov 0xfffffffc(%ebp),%eax } 0804840f <foo(C*)+0x3f> leave 08048410 <foo(C*)+0x40> ret 08048411 <foo(C*)+0x41> nop
如何でしょう?
最適化をかけたバイナリでもそれなりに閲覧可能です。このdisassemble結果を表示しているターミナルを、gdbの脇にでも置いておけばそれなりに快適なデバッグができます :-)