Linuxカーネルのソースコードを読んで、システムコールを探る:main()関数の前には何があるのか(6)(1/3 ページ)
C言語の「Hello World!」プログラムで使われる、「printf()」「main()」関数の中身を、デバッガによる解析と逆アセンブル、ソースコード読解などのさまざまな側面から探る連載。前回まで、printf()内の中身をさまざまな方法で探り、write()やint $0x80の呼び出しまでたどり着いた。今回は、さらにその先にあるLinuxカーネル側のシステムコールを見ていく。
書籍の中から有用な技術情報をピックアップして紹介する本シリーズ。今回は、秀和システム発行の書籍『ハロー“Hello, World” OSと標準ライブラリのシゴトとしくみ(2015年9月11日発行)』からの抜粋です。
ご注意:本稿は、著者及び出版社の許可を得て、そのまま転載したものです。このため用字用語の統一ルールなどは@ITのそれとは一致しません。あらかじめご了承ください。
※編集部注:前回記事「printf()のソースコードで、ソースコードリーディングのコツを身に付ける」はこちら
Linuxカーネルのソースコードを読んでみよう
「Linuxカーネルのソースコードを読む」と言われると、ハードルが高いと感じてしまう読者のかたもいるかもしれない。
しかしここで行うのは、本格的なコードリーディングではない。
カーネル・ソースコードを読む際によく言われることは、何を知りたいかを決めて読む、ということだ。つまり、場所を限定して読むことだ。
そしてここではシステムコールの処理に限定して読む。あまり気負わずに、気軽に読んでみよう。
Linuxカーネルのダウンロード
Linuxカーネルの本家ダウンロードサイトは以下だ。
https://www.kernel.org/
mainlineと呼ばれるものが、開発中の最新カーネルだ。本書執筆時点(※2015年9月)では、バージョン4系がmainlineになっている。
それ以外はstable、longtermと呼ばれるものがある。stableは安定バージョン、longtermは長期メンテナンスが行われるバージョンだ。
ところで、本書のCentOS環境のLinuxカーネルのバージョンはいくつだろうか。これはunameコマンドで調べることができる。
[user@localhost ~]$ uname -r 2.6.32-504.16.2.el6.i686 [user@localhost ~]$
バージョンは2.6.32のようだ。
本来ならば実行環境と同じバージョンのカーネルのソースコードを読むべきだが、システムコールの仕様は頻繁に変わるようなものではないので、それほど神経質にはならなくともいいだろう。
本書執筆時点(※2015年9月)では、本家サイトでは2.6系のlong termカーネルの最新版として、2.6.32.65というバージョンのものがダウンロードできた。ということで本書では、2.6.32.65のカーネルを参照することにしよう。
なお上記バージョンのカーネルは、本書のサポートサイトからもダウンロードできるようにしてある。本家サイトで見当たらなかったら、そちらからダウンロードできる。詳しくは連載第1回を参照してほしい。
ディレクトリ構成を見る
まずは軽く、ディレクトリ構成を見てみよう。
[user@localhost ~]$ cd linux-2.6.32.65 [user@localhost linux-2.6.32.65]$ ls COPYING MAINTAINERS arch firmware ipc net sound CREDITS Makefile block fs kernel samples tools Documentation README crypto include lib scripts usr Kbuild REPORTING-BUGS drivers init mm security virt [user@localhost linux-2.6.32.65]$
様々なディレクトリがあるようだが、ディレクトリ構成をおおまかに説明しておこう。
まず本書で重要な部分として、archはアーキテクチャ依存の処理、fsはファイルシステム関連、kernelはカーネルのアーキテクチャ共通の処理になる。さらにdriversは各種デバイスドライバ、includeは各種ヘッダファイル、mmは仮想メモリ関連、netはネットワーク関連だ。
さてこの中で、いったいどこを見ていけばいいのであろうか。
見たいのはint $0x80が呼ばれたときの処理だ。つまりシステムコール命令が呼ばれたときの処理だ。
システムコールはCPUに対しての例外発行になる。これはいわゆるソフトウェア割込みなので、割込み処理にその入り口がある。そして割込み処理はCPUごとの独自処理となるため、アーキテクチャ依存の処理となる。
アーキテクチャ依存の処理は、ディレクトリarchの中にある。つまりそこに、目的の処理があるはずだ。
まずはディレクトリarchの中を確認しよう。
[user@localhost linux-2.6.32.65]$ cd arch [user@localhost arch]$ ls Kconfig avr32 frv m32r microblaze parisc score um alpha blackfin h8300 m68k mips powerpc sh x86 arm cris ia64 m68knommu mn10300 s390 sparc xtensa [user@localhost arch]$
様々なアーキテクチャが存在しているようだが、中にはx86というディレクトリがある。ここにx86依存の処理があるのだと思われる。
確認してみよう。
[user@localhost arch]$ cd x86 [user@localhost x86]$ ls Kbuild Kconfig.debug boot ia32 kvm math-emu pci video Kconfig Makefile configs include lguest mm power xen Kconfig.cpu Makefile_32.cpu crypto kernel lib oprofile vdso [user@localhost x86]$
ここにも様々なディレクトリがあるようだ。
目的の処理を探す
ファイルやディレクトリが大量に存在しているのを見ると圧倒されてしまいがちだが、こういうときの調べかたにはコツがある。
基本はgrepという、検索用のコマンドをうまく使うことなのだが、使いかたはそれほど難しくはない。システムコールの処理はどこにあるのか、grepで検索してみよう。
問題は検索のためのキーワードなのだが、ここはセンスが問われるところだ。とりあえずシステムコールの処理なので「SystemCall」や「SYSTEMCALL」で検索してみよう。
[user@localhost x86]$ grep -r SystemCall . [user@localhost x86]$ grep -r SYSTEMCALL . [user@localhost x86]$
とくに何も無いようだ。
grepコマンドは「-r」というオプションを付加することで、ディレクトリを再帰的に追いかけて検索してくれる。つまり上の例では、カレントディレクトリ以下のファイルを全検索している。
次は「syscall」をキーワードにして検索してみよう。
[user@localhost x86]$ grep -r syscall . ./include/asm/ia32_unistd.h: * Only add syscalls here where some part of the kernel needs to know ./include/asm/ia32_unistd.h:#define __NR_ia32_restart_syscall 0 ./include/asm/syscalls.h: * syscalls.h - Linux syscall interfaces (arch-specific) ./include/asm/sys_ia32.h: * sys_ia32.h - Linux ia32 syscall interfaces ./include/asm/unistd_64.h:/* at least 8 syscall per cacheline */ ./include/asm/unistd_64.h:__SYSCALL(__NR_uselib, sys_ni_syscall) ...
今度は大量にヒットした。ヒット数を見てみよう。
[user@localhost x86]$ grep -r syscall . | wc -l 618 [user@localhost x86]$
600件以上もヒットしている。これくらいの数ならば、ヒットしたファイルをすべて調べていくことは不可能ではないが、まずは調べるファイルを限定していくことを考えたほうがいいだろう。
さて、どのようにして範囲を狭めていけばいいだろうか。
Copyright © ITmedia, Inc. All Rights Reserved.
関連記事
- プログラミング言語Cについて知ろう
プログラミング言語の基本となる「C」。正しい文法や作法を身に付けよう。Cには確かに学ぶだけの価値がある(編集部) - シェルコード解析に必携の「5つ道具」
コンピュータウイルスの解析などに欠かせないリバースエンジニアリング技術ですが、何だか難しそうだな、という印象を抱いている人も多いのではないでしょうか。この連載では、「シェルコード」を例に、実践形式でその基礎を紹介していきます。(編集部) - 【 od 】コマンド――ファイルを8進数や16進数でダンプする
本連載は、Linuxのコマンドについて、基本書式からオプション、具体的な実行例までを紹介していきます。今回は、「od」コマンドです。