図2.18では「call *0x1c(%eax)」という関数呼び出しが行われている。
「0x1c(%eax)」という表記は連載第1回で説明したように、EAXレジスタに0x1cを加算したアドレスの位置の値、という意味だ。よってこれは、関数へのポインタを経由しての関数呼び出しになっている。call命令手前の3つのmov命令によってスタック上に準備している引数も含めてC言語風に書くと、以下のような感じだ。
int (*f)(int a, int b, int c); f = *(EAX + 0x1c) f(EBX, EDX, ESI);
ポインタ経由で関数呼び出しされているので、その先にどのような関数があるのかが一見してわからない。アセンブラを見る限り、関数のアドレスはEAX+0x1Cという位置にあるのだが、関数呼び出しによってEAXの値は変化してしまうので、調べるのも面倒そうだ。
ということでこの関数呼び出しの位置にブレークポイントを張ってみよう。call命令は0x8059735というアドレスに配置されている。そして以下のようにすれば、アドレス指定でブレークポイントを設定することができる。アドレス値の前に「*」が必要となる点に注意してほしい。
(gdb) break *0x8059735 Breakpoint 3 at 0x8059735 (gdb)
Ctrl+Lで画面をきれいにして、再度、実行してみよう。
(gdb) run The program being debugged has been started already. Start it from the beginning? (y or n) Starting program: /home/user/hello/hello Breakpoint 1, main (argc=1, argv=0xbffffc14) at hello.c:5 (gdb)
最初はmain()の先頭でブレークしているようだ。continueで処理を進める。
(gdb) continue Continuing. Breakpoint 2, 0x080591d3 in vfprintf () (gdb)
vfprintf()でブレークしたようだ。さらにcontinueで処理を進めよう。
(gdb) continue Continuing. Breakpoint 3, 0x08059735 in vfprintf () (gdb)
画面は図2.19のようになった。先ほどブレークポイントを設定したcall命令の位置でブレークしている。
メッセージ出力が行われるのはここだろうか。nextiで処理を進めてみよう。
メッセージは出力されない。1回目の関数呼び出しではメッセージは出力されないようだ。
再びcontinueで処理を進める。すると再度、call命令の位置でブレークした。
そしてnextiを実行すると、今度は図2.21のようになった。
図2.21はやはり画面が崩れてしまっているのだが、メッセージが出力されている。
ということは、2回目のcall命令でメッセージが出力されるということだ。
runで再実行しよう。1回目のmain()でのブレークと、2回目のvfprintf()でのブレークは、continueで継続する。さらにcall命令でブレークするはずだが、これもcontinueで継続する。そして2回目のcall命令のブレークのときに、stepiで関数内部に入っていこう。
(gdb) run The program being debugged has been started already. Start it from the beginning? (y or n) Starting program: /home/user/hello/hello Breakpoint 1, main (argc=1, argv=0xbffffc14) at hello.c:5 (gdb) continue Continuing. Breakpoint 2, 0x080591d3 in vfprintf () (gdb) continue Continuing. Breakpoint 3, 0x08059735 in vfprintf () (gdb) continue Continuing. Breakpoint 3, 0x08059735 in vfprintf () (gdb) stepi 0x080673a0 in _IO_new_file_xsputn () (gdb)
すると画面は図2.22のようになった。
どうやら_IO_new_file_xsputn()という関数が呼び出されていて、その先でメッセージの出力が行われているようだ。
あとはまた_IO_new_file_xsputn()にブレークポイントを張り、再度実行することで_IO_new_file_xsputn()の中に入っていくことができる。
Copyright © ITmedia, Inc. All Rights Reserved.