検索
連載

「Hello World!」の主役printf()の内部動作をデバッガGDBで追うmain()関数の前には何があるのか(2)(1/3 ページ)

C言語の「Hello World!」プログラムで使われる、「printf()」「main()」関数の中身を、デバッガによる解析と逆アセンブル、ソースコード読解などのさまざまな側面から探る連載。今回は、「Hello World!」の主役printf()の内部動作をデバッガGDBで追う。

Share
Tweet
LINE
Hatena

連載目次

ハロー“Hello,World” OSと標準ライブラリのシゴトとしくみ

書籍の中から有用な技術情報をピックアップして紹介する本シリーズ。今回は、秀和システム発行の書籍『ハロー“Hello, World” OSと標準ライブラリのシゴトとしくみ(2015年9月11日発行)』からの抜粋です。

ご注意:本稿は、著者及び出版社の許可を得て、そのまま転載したものです。このため用字用語の統一ルールなどは@ITのそれとは一致しません。あらかじめご了承ください。


※編集部注:前回記事『「Hello World!」の中身を探る意義と環境構築、main()のアセンブラを読んでみる』はこちら

printf()の内部動作を追う

 ハロー・ワールドのプログラムを書くとき、主役となるのはprintf()という標準ライブラリ関数だ。printf()は書式文字列の指定により様々なフォーマットでの出力を行うことができる、非常に便利な関数だ。

 C言語のプログラムを習得するとき、printf()を使ったことが無いという人は(一部の組込みプログラマを除けば)そう多くはいないことだろう。

 しかしprintf()の内部の動作を追いかけてみたことがあるという人は、少ないのではないだろうか。

 本章ではまずは解析の練習として、デバッガを用いてprintf()の動作を探ってみよう。

デバッガを使ってみよう

 hello.cはメッセージを出力するだけのプログラムなのだが、メッセージの出力自体はprintf()という関数によって行われている。

 printf()は「標準ライブラリ関数」などと呼ばれるもので、C言語によるプログラミング環境で、標準的に用意されているライブラリ関数だ。つまりC言語を使ってプログラムを書くプログラマは、あまり難しいことは考えずに、printf()は標準で使うことができるということだ。

 しかしその「printf()」にも、実体はあるはずだ。printf()の中では、いったい何が行われているのだろうか。ということでまずはprintf()の動作を追ってみよう。

 関数の内部の処理を知る方法はいくつかある。ひとつはprintf()のソースコードを追いかける方法だ。いわゆる静的解析と呼ばれるものだ。

 もうひとつはプログラムを実際に動作させ、デバッガなどを用いて実行される手順を追うというものだ。これは動的解析と呼ばれる。

 これらには一長一短があり、どちらが優れているということではなく場合によって取捨選択することになる。しかし手始めに行うには、実際の実行の流れを追うことができる動的解析のほうが馴染みやすいだろう。

 そこでここではデバッガによる動的解析によって、printf()の動作を追ってみよう。

 なお実際にはデバッガによる手作業での方法の他にも、いくつかの解析手法がある。たとえば後述のstraceを利用すれば、システムコールの呼び出しまで一足飛びに追うことはできる。解析時にはひとつの手法にこだわらず、様々な手法を併用して組み合わせるべきと思う。そのような意味で、手数は増やしておいたほうがいい。

 しかしここではデバッガの操作の練習と処理の流れの全体を把握することを目的として、敢えてデバッガでの手作業によって解析してみる。

GDBを起動してみよう

 本書ではGDB(GNU Debugger)というデバッガを利用する。まずは実行ファイルを指定して、GDBを起動してみよう。

[user@localhost hello]$ gdb hello

 もしも以下のようなメッセージが出力されたならば、GDBの実体である「gdbコマンド」がインストールされていない。

[user@localhost ~]$ gdb
-bash: gdb: command not found
[user@localhost ~]$

 その場合にはまずGDBをインストールしてほしい。以下はCentOSでのインストール例だ。

# yum install gdb

「GDB」のように大文字で表記した場合にはデバッガのソフトウェアを指し、「gdb」のように小文字で表記した場合にはコマンドとしてのデバッガを指す。同様のことは「GCC」と「gcc 」についても言える。


 無事に起動できたときには、以下のようにライセンス文章が出力される。

[user@localhost hello]$ gdb hello
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-75.el6)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/user/hello/hello...done.
(gdb)

 ライセンスなどのメッセージ出力後に「(gdb)」というプロンプトが出て、コマンドの入力待ちになっている。

 gdbの操作のためのインターフェースはコマンドラインによる、いわゆるCUIだ。よってコマンドを手打ちして操作することになる。

 なおGDBのインターフェースには、GUIのものも多くある。しかしそのようなものも多くの実体はGUIの先でgdbのコマンドを発行しているだけであり、CUIのコマンドを知っておくことは有益だ。ということで、本書ではコマンドによる操作をベースにして説明していく。なおそのようなGUIは「ラッパー」(wrapper)などと呼ばれたりする。上にかぶせたガワ、という意味だ。

 GDBを起動したら、まずはハロー・ワールドのプログラムを、そのまま実行してみよう。GDBでは「run」というコマンドでプログラムを実行することができる。

(gdb) run
Starting program: /home/user/hello/hello
Hello World! 1 /home/user/hello/hello
 
Program exited normally.
(gdb)

 「Hello World!」と出力されているので、どうやらうまく実行できたようだ。

 またメッセージの出力後には「Program exited normally.」と出力されている。どうやら正常終了しているようだ。

 そしてGDBは、以下のようにquitコマンドで終了できる。

(gdb) quit
[user@localhost hello]$

Copyright © ITmedia, Inc. All Rights Reserved.

       | 次のページへ
ページトップに戻る