エンジニアならC言語プログラムの終わりに呼び出されるexit()の中身分かってますよね?main()関数の前には何があるのか(9)(1/3 ページ)

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

» 2017年07月13日 05時00分 公開
[坂井弘亮]

連載目次

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

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

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


※編集部注:前回記事「あなたが知らないプログラムの真の始まり――main()関数の前にあるスタートアップとは」はこちら

exit()の処理

 main()関数が呼び出されるまでの処理の内容はだいたいわかってきたが、次はプログラムの終了についてだ。

 プログラムの実行は、exit()が呼び出されることで終了する。これはプログラマが明示的に呼び出すこともあれば、main()から戻った後にスタートアップによって呼び出される場合もあるだろう。

 しかしexit()からは戻ってくることは無いため呼び出されっぱなしであり、このためexit()の中ででどのような処理が行われているのかを意識することは少ないかもしれない。

 ここではGDBによる動的解析と、glibcのソースコード読解による静的解析の両面から、exit()で行われる処理を追ってみよう。

exit()の処理をデバッガで追う

 まずはGDBでexit()の処理を追うことで、プログラムの実行が終了するその瞬間を探ってみよう。

 GDBでhelloを起動して、exit()にブレークポイントを張る。

[user@localhost hello]$ gdb -q hello
Reading symbols from /home/user/hello/hello...done.
(gdb) break exit
Breakpoint 1 at 0x8048e65
(gdb) run
Starting program: /home/user/hello/hello
Hello World! 1 /home/user/hello/hello
 
Breakpoint 1, 0x08048e65 in exit ()
(gdb)

 exit()もglibcによって与えられるものなので、ブレークした箇所のC言語のソースコードは出てこない。layout asmでアセンブラを見てみよう。

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

図5.7: exit()でブレークする 図5.7: exit()でブレークする

 さて、プログラムが終了する瞬間を見つけるのは簡単だ。この状態でnextiによりステップ実行を繰り返していき、終了するところを探ればいい。

 実際に何度か繰り返して試してみると、図5.8の位置でnextiを実行したときにプログラムが終了することがわかってきた。

図5.8: _exit()の呼び出し 図5.8: _exit()の呼び出し

 _exit()という関数を呼び出したときに終了しているようだ。

_exit()の呼び出し

 そこで次は_exit()にブレークポイントを張ってみよう。

(gdb) break _exit
Breakpoint 2 at 0x8053c10
(gdb)

 再実行してみる。

(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, 0x08048e65 in exit ()
(gdb)

 まずはexit()の先頭でブレークする。さらにcontinueしてみると_exit()の先頭でブレークし、図5.9のような画面になった。

図5.9: _exit()の先頭 図5.9: _exit()の先頭

 0x8058304の位置でint $0x80が呼び出されていることに注目してほしい。ということはこれはシステムコールの呼び出しだ。

 これは一見すると_exit()の内部でシステムコールが呼ばれているように思えるのだが、実際にステップ実行で処理を進めてみると、0x8053c19にあるcall命令で関数呼び出しされ図5.10のようになり、さらにそこでint $0x80が呼び出されるところでプログラムが終了した。

図5.10: int $0x80の呼び出し 図5.10: int $0x80の呼び出し

 ということは、何らかのシステムコールによって終了しているようだ。

 またexit()は_exit()を呼び出し、実際の終了はそちらで行われる。exit()は終了処理を行った後に_exit()を呼び出すライブラリ関数になっているということがわかるだろう。

exit_groupとexitの2つのシステムコール

 どのようなシステムコールが呼ばれているのかを知るには、int $0x80が呼ばれたときのシステムコール番号を見てみればいいだろう。

 図5.10の位置で、レジスタの値を見てみる。

(gdb) info registers
eax	0xfc	252
ecx	0x1	1
edx	0x80d82b4	135103156
ebx	0x0	0
esp	0xbffffb48	0xbffffb48
ebp	0xbffffb68	0xbffffb68
esi	0x0	0
edi	0x8048c20	134515744
eip	0x110414 0x110414 <__kernel_vsyscall>
eflags	0x246	[ PF ZF IF ]
cs	0x73	115
ss	0x7b	123
ds	0x7b	123
es	0x7b	123
fs	0x0	0
gs	0x33	51
(gdb)

 EAXの値が252になっている。これがシステムコール番号なわけだが、これに相当するシステムコールは何であろうか。

 システムコール番号はLinuxカーネルのシステムコール・テーブルを見ればわかるはずだ。連載第6回の説明を見返すと、arch/x86/kernel/syscall_table_32.Sというファイルにある。

 実際にsyscall_table_32.Sを見ると、252番は以下のように定義されている。

...
252:	.long sys_fadvise64	/* 250 */
253:	.long sys_ni_syscall
254:	.long sys_exit_group
...

 「exit_group」というシステムコールがあるようだ。man exit_groupで調べてみると、以下のように書かれている。

NAME
	exit_group - exit all threads in a process
 
SYNOPSIS
	#include <linux/unistd.h>
 
	void exit_group(int status);
 
DESCRIPTION
	This system call is equivalent to exit(2) except that it terminates not
	only the calling thread, but all threads in the calling process's
	thread group.
...

 プロセス内の全スレッドを終了するシステムコールとのことだ。

 DESCRIPTIONを読むと、exitシステムコールと等価であるが、exitはそれを呼んだスレッドのみ終了するのに対して、exit_groupはプロセス内の全スレッドを終了する、とある。

 ここでもう一度、連載第4回のstraceによるシステムコール・トレースを見てほしい。以下のようなシステムコール呼び出しが検出されており、プログラムの終了時には実はexit_groupが呼ばれていたことがわかる。

exit_group(0)				= ?

 さらに図5.9を見返してみよう。

 callによる関数呼び出しの先ではexit_groupが呼ばれるためにcall以降が実行されることはないはずなのだが、そこにあるコードはこれもint $0x80によるシステムコール呼び出しだ。直前でEAXに1を設定しているのでシステムコール番号は1のようだ。syscall_table_32.Sのテーブルを調べると、以下のようになっている。

ENTRY(sys_call_table)
	.long sys_restart_syscall	/* 0 - old "setup()" system call, used for restarting */
	.long sys_exit
...

 どうやら「exit」というシステムコールがあるようだ。

 つまり「exit_group」と「exit」という2種類のシステムコールがあるようなのだが、これはいったいどういうことであろうか。

       1|2|3 次のページへ

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

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

メールマガジン登録

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