/ «2004-02-15 (Sun) ^ 2004-02-22 (Sun)» ?
   西田 亙の本:GNU 開発ツール -- hello.c から a.out が誕生するまで --

Categories Books | Hard | Hardware | Linux | MCU | Misc | Publish | Radio | Repository | Thoughts | Time | UNIX | Writing | プロフィール


2004-02-21 (Sat)

[Linux] Pitfall of the Linux kernel 2.6

悩めるインクルードファイル

Brainstorm-ML上で、高原さんから「Linux kernel 2.6 での make がうまく行かない」という報告を頂く。特に、「サブディレクトリ内部での make がおかしい」とのこと。

このメッセージを拝見して、一瞬「シマッタ!」と思う。実は、記事執筆の際に使った私のホスト環境では、/usr/include/asm が Linux-2.4.24 内部の include/asm へのシンボリックリンクになっていた。当初は、Linux 2.6 ソースツリー内部の asm-i386 へ、張り直すつもりでいたのだが、仕事を急ぐ余りそのまま放置し、今回の問題点を見逃してしまった・・。滝汗;

全国、いや世界中で、この問題で眠れぬ日々を送っておられる方々のために、応急的な対処方法をご紹介しておこう。

glibc を取るか、カーネルを取るか?

Linux カーネルやモジュール、システムツールのビルド時に、インクルードファイルが「鬼門」となることは、これまでの連載を通じて何回か紹介してきたが、この問題は汎用ライブラリーである glibc 用のヘッダーファイルと、アーキテクチャに強く依存する Linux カーネル用のヘッダーファイルが共存しているために発生している。

なぜ、このような問題が生じるかについては、「インクルードとは何なのか?」という、素朴な疑問を丁寧に紐解いていく必要がある。例によって、世の中にはこのあたりの技術背景を解説した文書は、存在しないようなので、近いうちに GCC プログラミング工房上で徹底解説できればと思う。

ともあれ、時間は限られているので、即席の解決手段をご紹介しよう。まず、あなたは glibc と Linux kernel、おふたりさんのうち、どちらかを選択する必要がある。glibc を選んだ人には、マニュアルでカーネルをビルドするよりも、パッケージで入手することをお勧めする。

暴れ馬である Linux kernel を選んだ方は、落馬を覚悟で次のステップにお進みあれ。

悪い子のための Linux kernel 2.6 ビルド方法

Red Hat や Debian などのディストリビューターは、/usr/include ディレクトリ中に、アーキテクチャに依存するヘッダーファイル群を asm, linux サブディレクトリに格納している。当然のことながら、カーネルの成長と共に、asm, linux ディレクトリ中のヘッダーファイルは日々変遷を遂げている。具体的には、データ構造や、システムコール番号、エラーコード番号、各種インデックス番号などが挙げられる。

カーネルがこれらのファイルに依存しているのはもちろんのこと、実は glibc も一部で依存している。例えば、<errno.h> はその好例だ(実際に、内部の芋づる式 include 命令を追ってみると良い)。

よって、カーネルをアップデートした際には、厳密にはライブラリーも再構築する必要がある。しかし、そのような律儀な人(暇人?)は、まず存在しないだろう。そこで、各ディストリビューターは、カーネルヘッダーファイルと glibc の乖離を防ぐために、自分たちがライブラリーをビルドした際に使用したアーキテクチャ依存性のヘッダーファイルをあらかじめ /usr/include/asm, /usr/include/linux サブディレクトリ内部に用意しているのである。

一般的なアプリケーションをビルドする際には、これでも良いのだが、問題はカーネルに強く依存するモジュールや、システムツールをビルドする場合。詳細は割愛するが、ビルドエラー、もしくはカーネルとモジュール間でデータ構造の整合性が取れない事態に発展する。

以上より、カーネルハッキング大好き人間は、ディストリビューターが用意してくれている asm, linux サブディレクトリをさっさとポイしてきた。本当に rm -r してしまうと、ちと心配なので、通常は asm.orig, linux.orig などに改名すると良いだろう。その上で、/usr/src/linux 内部へのシンボリックリンクを張る。私の場合は、次の通り。

/usr/include # file asm asm-generic
asm:         symbolic link to ../src/linux/include/asm-i386
asm-generic: symbolic link to ../src/linux/include/asm-generic
/usr/include # file ../src/linux
../src/linux: symbolic link to linux-2.6.3

最終的には、/usr/include/asm は、/usr/src/linux-2.6.3/include/asm-i386、/usr/include/asm-generic は、/usr/src/linux-2.6.3/include/asm-generic、/usr/include/linux は、/usr/src/linux-2.6.3/include/linux への参照となる。asm-generic は、カーネル 2.6 から <asm/errno.h> 内部で <asm-generic/errno.h> を孫インクルードするようになったために、必要になったものである。

ここで、Embedded UNIX Vol.6 および GCC プログラミング工房で紹介した、サブディレクトリ内部でのビルドに挑戦してみると・・

Makefile の修正

残念ながら、エラーにより異常終了してしまう(従来通り、トップディレクトリ上でビルドすれば、問題は発生しない)。この原因は、トップディレクトリに用意されている Makefile 中の prepare1 ターゲットにある。

571 # prepare1 is used to check if we are building in a separate output directory,
572 # and if so do:
573 # 1) Check that make has not been executed in the kernel src $(srctree)
574 # 2) Create the include2 directory, used for the second asm symlink
575 
576 prepare1:
577 ifneq ($(KBUILD_SRC),)
578         @echo '  Using $(srctree) as source for kernel'
579 #       $(Q)if [ -h $(srctree)/include/asm -o -f $(srctree)/.config ]; then ?
580 #               echo "  $(srctree) is not clean, please run 'make mrproper'";?
581 #               echo "  in the '$(srctree)' directory.";?
582 #               /bin/false; ?
583 #       fi;
584         $(Q)if [ ! -d include2 ]; then mkdir -p include2; fi;
585         $(Q)ln -fsn $(srctree)/include/asm-$(ARCH) include2/asm
586 endif

結論から言えば、579行目がエラーの発生箇所である。本行が意味するところはなかなか深いのだが、取り急ぎ今は何も考えず 579〜583行をコメントアウトしてしまおう。以上で、カーネルハッキングに最適のビルド環境が出来上がる。

後はひたすらビルドするのみ。Have fun!

Linux が抱える問題

以上のように、Linux 2.6 では 2.4 に比較してカーネルヘッダーファイルの取り扱いが激変している。にもかかわらず、ソースツリー内部の解説文書には、この変化に関する記述が見当たらないのである。

ソースコードと優れた技術文書が融合し、初めて美しい肉体が出来上がるとすれば、現状の Linux はさしずめ「骸骨」と言ったところだろうか・・。