連載
» 2017年06月01日 05時00分 公開

main()関数の前には何があるのか(3):試行錯誤のデバッグで探る、printf()内のポインタ経由での関数呼び出しが行き着く先とは (2/4)

[坂井弘亮,著]

メッセージの出力箇所を探る

 これでprintf()の中に入れたわけだが、ステップ実行によってさらに処理を先まで進めることで、ハロー・ワールドのメッセージが出力されている箇所を探っていこう。

 ここではメッセージ出力が行われている関数が知りたいので、nextiで処理を進めていけばいいだろう。そこでnextiを何回か実行すると、図2.13のような関数の呼び出しが見つかる。

図2.13: vfprintf()の呼び出し 図2.13: vfprintf()の呼び出し

 ニーモニックを見ると、「call 0x80591c0 <vfprintf>」とある。命令としては「0x80591c0という位置にある関数を呼び出す」という意味なのだが、アドレスからそこにある関数をGDBが割り出して、vfprintf()が呼ばれていると表示してくれているようだ。

 ということで、どうやらvfprintf()という関数が呼び出されているようだ。この中で文字列の出力が行われているのだろうか。

 関数の実行時に、メッセージが出力されるかどうかを試してみよう。まず画面上にメッセージが残ってしまっている場合には、Ctrl+Lで画面をリフレッシュする。さらにnextiで関数の処理を進めてみよう。

(gdb) nexti
0x08049381 in printf ()
(gdb)

 すると図2.14のようになった。

図2.14: nextiでvfprintf()の呼び出しを進める 図2.14: nextiでvfprintf()の呼び出しを進める

 表示が崩れてしまっていてわかりづらいのだが、よく見ると反転表示されている行の数行下に「Hello World!」の文字列が出力されていることがわかる。つまり、メッセージ出力が行われているということだ。

 ということは、メッセージ出力が行われているのはvfprintf()という関数の呼び出しの先ということがわかる。

vfprintf()の中に入る

 どうやらメッセージの出力は、vfprintf()という関数の先で行われているようだ。ということでvfprintf()にブレークポイントを新たに張って、再度実行してそこまで進めてみよう。

(gdb) break vfprintf
Breakpoint 2 at 0x80591d3
(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()の先頭に張ったブレークポイントがまだ残っているので、main()の先頭でブレークしたはずだ。ここで「Hello World!」のメッセージがまだ画面上に残っているようならば、メッセージ出力のタイミングを見誤らないように、Ctrl+Lを押すことで画面をリフレッシュしておこう。

 さらにcontinueを実行しよう。すると次のブレークポイントであるvfprintf()の先頭まで進むはずだ。画面は図2.15のようになるだろう。

図2.15: vfprintf()でブレークする 図2.15: vfprintf()でブレークする

 先ほどと同様にnextiで処理を進め、メッセージが出力されるタイミングを調べよう。実際にやってみると、まず図2.16のようなstrchrnul()という関数の呼び出しがある。

図2.16: strchrnul()の呼び出し 図2.16: strchrnul()の呼び出し

 メッセージの出力が行われているのはこの関数の先だろうか?

 まあ関数名からして関係なさそうな気はするのだが、練習として一応見てみよう。nextiで関数を実行すると、図2.17のようになった。

図2.17: strchrnul()の呼び出しでは、メッセージは出力されない 図2.17: strchrnul()の呼び出しでは、メッセージは出力されない

 メッセージが出力されるようなことは無かったようだ。よってこの関数呼び出しは関係ないようだ。

 このようにしてメッセージ出力が行われる箇所を探っていくことができる。途中、repnzという命令でnextiで進まなくなる箇所があるが、数10回nextiを繰り返すと抜けられるので、気にせずに進めてほしい。

 すると、図2.18の位置でメッセージが出力されることがわかる。なお図2.18の位置はメッセージ出力までに何度か通過しているようなのだが、その中で数回目にメッセージが出力されるようだ。

図2.18: ポインタ経由の関数呼び出しでメッセージが出力される 図2.18: ポインタ経由の関数呼び出しでメッセージが出力される

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。