はじめに
こんにちは。Advent Calender 12月01日担当のyasuです。世界一有名なプログラムと言われているHello world。今回は、C言語のHello worldとのお遊びを紹介したいと思います。
Hello worldとは
まず、Hello worldとは、画面にHello Worldの文字列を出力するプログラムのことを指します。プログラムを使うほとんどの人が一度は書いたことがあるプログラムだと思います。ちなみに、C言語のソースコードはこんな感じです。
hello.c#include <stdio.h>
int main(int argc, char *argv[])
{
printf("Hello World! %d %s\n", argc, argv[0]);
return 0;
}
Hello worldと遊んでみよう
今回、GdbとGdbserverを使うので、まずはそのインストールをします。以下のコマンドでインストールしてください。
yum -y install gdb
yum -y install gdb-gdbserver
次に、hello.cをコンパイルします。これで新たにファイルが生成されます(私の場合はhelloというファイルでした)。
これで準備完了です!
次に、Gdbでhelloを起動します。その時に、gdbserverを用いて、リモートデバックをします。これにより、文字化けを防ぐことができます。
まずは、gdbserverを起動します。hello.cのあるディレクトリに移り、以下のコマンドを入力してください。
gdbserver localhost:12345 ./hello
localhostの後の数字は何でもよいです。
次に、別のターミナルを起動して、同じようにhello.cのあるディレクトリに移動して、以下のコマンドを入力します。
gdb -q hello
これで、gdbが起動しました。次は、gdbserverと接続をします。以下のコマンドを入力して、gdbserverと接続をします。
target extended-remote localhost:12345
実際やってみた画像はこんな感じです。
gdb側で実行したプログラムがgdbserver側で出力されているのが分かります。
それでは、準備が終わりましたので実際に遊んでみましょう。細かいGdbの使い方はここを参照してください。
今回は、Hello worldが出力される瞬間を調べてみます。
まずは、main関数にブレークポイントを張ります。
上の画像でcall命令を実行したら、Hello worldが出力されるので、今度はprintfにブレークポイントを張り、printfの内部動作を追っていきます。これを5000兆回くらいくりかえすとやがてwrite関数にたどり着くきます。ここでもcall命令があるところまでプログラムを進めていきます。
stepiを実行して関数の内部に入ってみます。
この命令を実行するとHello worldが出力されます。
よって、Hello worldはこのint命令が実行されたら出力されていることが分かります。ちなみにこのint命令はシステムコールと呼ばれるもので、この命令によってプログラムがOSの機能を呼び出しています。int命令までに呼び出した関数はこんな感じです。めちゃめちゃ多いですね。
今回は、システムコールの呼び出しまでの流れを追っていっただけで特に各関数での処理とかは見てないのですが、gdbにはレジスタの値を確認できたりもするので、そこに注目しながらデバックをしていっても面白いと思います。さすがにこれだけで終わるのも忍びないので、最後にシステムコールが呼ばれる直前のレジスタの値だけ見てみます。
eax 0x4 4
ecx 0xb77a3000 -1216729088
edx 0x17 23
ebx 0x1 1
esp 0xbfb2bdd8 0xbfb2bdd8
ebp 0xbfb2bdfc 0xbfb2bdfc
esi 0xb77a3000 -1216729088
edi 0x80d68c0 135096512
eip 0x171414 0x171414 <__kernel_vsyscall>
eflags 0x200246 [ PF ZF IF ID ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
この中で、システムコールの引数に用いられるのはeax,ebx, ecx, edxの四つの値で、それぞれの値とその意味をまとめると次のような図になります。
レジスタ | 値 | 意味 |
---|---|---|
EAX | 4 | システムコール番号(write()は4) |
EBX | 1 | 出力先のファイルディスクリプタ |
ECX | 0xb77a3000 | 出力データのアドレス |
EDX | be | 出力データのサイズ |
つまり、EAXにシステムコール番号、EBX以降に引数を設定して、システムコールを発行して、Linuxカーネルに処理をさせていることがわかりました。
最後に
スクショの時間が投稿日になっているのはPCの設定ミスです。別に早朝急いで仕上げたわけじゃないんだからねっ!! 来年も書く機会があればもう少し準備したいと思います。勉強したてのことを記事にしたので間違っているところがあればご指摘お願いいたします。皆さんもぜひハローワールドと遊んでみてください。特に着地点のない話になってしまいましたが、この辺で終わりにしたいと思います。明日は、RLookさんとMENTOSさんの記事です。お楽しみに!