Linux kernelのMakefileを雰囲気で読んだ話

2020/04/28

top

この記事は,「カーネルビルドでも進捗が見たい!」に入り切らなかった蛇足です.

kbuild-progressの実装を初めた当初は,kernelのMakefileを改造することで,プログレスバーを表示しようとしていました.Linux kernelのビルドシステムであるKbuildはmakeで書かれています.実際にビルドを行っているmakeなら,ビルド全体のタスク数や,残りのタスク数などを知っているはずだと考えたのです.

Makefileを読んでみた

Linux kernelには 63,135行のMakefile が含まれ(linux-5.6.7で計測), なまら複雑です. 私はmakeについて全く詳しくないので(予防線),雰囲気で読んでいくしかありません.がんばります.

vmlinux ルール

プログレスバーを表示するにあたって,まず,ビルド対象ファイルの一覧的なものがほしいです.もっとも典型的なビルドコマンドであるmake vmlinuxの挙動について考えます.vmlinuxのルールはlinux-5.6.7/Makefile

vmlinux: scripts/link-vmlinux.sh autoksyms_recursive $(vmlinux-deps) FORCE
	+$(call if_changed,link-vmlinux)

ですね.vmlinux:がターゲットで,その後に以下のような依存が続いています.

  • scripts/link-vmlinux.sh
  • autoksyms_recursive
  • $(vmlinux-deps)
  • FORCE

scripts/link-vmlinux.shは…なんかvmlinuxをリンクするやつですね(雑).たぶん今回は見なくてもいいでしょう.FORCEは,このルールの実行を強制する何かです.これも関係なし.

autoksyms_recursiveは怪しい名前ですね.ルールを見ると,

ifdef CONFIG_TRIM_UNUSED_KSYMS
autoksyms_recursive: ...
endif

CONFIG_TRIM_UNUSED_KSYMSが宣言されていないときは何もしないそうです.CONFIG_TRIM_UNUSED_KSYMSとは?

このオプションにより、未使用のエクスポートされたシンボルをビルドから削除できます。 https://cateee.net/lkddb/web-lkddb/TRIM_UNUSED_KSYMS.html

なるほど,kernelをダイエットするやつですね.今回は関係ナシ!

vmlinux-deps変数

ということで$(vmlinux-deps)があやしい.この変数は…

vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_OBJS) $(KBUILD_VMLINUX_LIBS)

ここで作られてますね.おお!ビルド対象の一覧っぽい感じがします!さっそく変数の中身を見てみましょう!

init/built-in.a
usr/built-in.a
arch/x86/built-in.a
...(その他20個のファイル)...

あれ…?なんだか数が少ないですね…トップレベルのオブジェクトファイルしかリストに入っていないようです.じゃあこれらのオブジェクトを生成するルールは…

# The actual objects are generated when descending,
# make sure no implicit rule kicks in
$(sort $(vmlinux-deps)): descend ;

「実際のオブジェクトは下降時に生成されます」,なるほど.たぶんディレクトリを再帰してビルドしていくんですね.

descend ルール

descendのルールは…

descend: $(build-dirs)
$(build-dirs): prepare
	$(Q)$(MAKE) $(build)=$@ \
	single-build=$(if $(filter-out $@/, $(filter $@/%, $(single-no-ko))),1) \
	need-builtin=1 need-modorder=1

ありました.依存はbuild-dirsで,3行のコマンドがあります.build-dirsの中身は…

init usr arch/x86 kernel certs mm fs ipc security crypto block drivers sound net lib arch/x86/lib virt

ほーん.トップディレクトリのリストですね.

コマンドのほうを見てみます.3行ありますが,下2行は変数に何か代入しているだけなので,1行目に注目します.

	$(Q)$(MAKE) $(build)=$@ \

いろいろ変数を展開していますね.QMAKE変数の中身をそれぞれ見てみます.

Q = @
MAKE = make

なるほど,@コマンドはmakeのログを抑制するやつなので,Qは静かにしたいときに使う変数です.MAKEはまんまmakeですね.つまり,このコマンドは別のmakeコマンドを実行しています.build変数の定義は…?

linux-5.6.7$ grep -r "build :="
scripts/Kbuild.include:build := -f $(srctree)/scripts/Makefile.build obj

linux-5.6.7/scripts/Kbuild.include:buildにありました.-f $(srctree)/scripts/Makefile.build objです.つまり先程のコマンドは…

	@make -f $(srctree)/scripts/Makefile.build obj=$@ \

となります.makeの-fオプションはMakefileを明示的に指定するやつですね.obj変数に$@をセットしてlinux-5.6.7/scripts/Makefile.buildを実行しています.@変数はビルドターゲットを表す特殊な変数で,先程のbuild-dirs変数の中身が入ってきます.つまり

make -f $(srctree)/scripts/Makefile.build obj=init
make -f $(srctree)/scripts/Makefile.build obj=usr
make -f $(srctree)/scripts/Makefile.build obj=arch/x86
...(その他20行のコマンド)...

というコマンドが実行されていきます.

scripts/Makefile.build

というわけで,各トップディレクトリの処理はscripts/Makefile.buildが担当しているようです.このMakefileはどんな処理をしているのでしょうか.ちょっと先頭から読んでみます.

src := $(obj)
...
# Init all relevant variables used in kbuild files so
# 1) they have correct type
# 2) they do not inherit any value from the environment
obj-y :=
obj-m :=

おっ,各トップディレクトリが obj 変数にセットされていますね.そして,続くコメントでは「kbuildファイルで使用されるすべての関連変数を初期化」…

kernelやkernel moduleを開発したことのある方なら,obj-yobj-mという変数を見てニヤリとするはずです.これらは,Linux kernelに自分の書いたプログラムを登録するときに使う変数ですね.kernelの各ディレクトリには それぞれのMakefileがあり,ビルドしたいファイルをこれらの変数に登録しているのです.

obj-yを調べれば,ビルド対象のオブジェクトがリストできるかも!

obj-y

ではobj-yはどのように利用されているのでしょうか.Makefile.buildの中にはそれらしいヤツがなかったのですが,

linux-5.6.7$ grep -r "obj-y"
...
scripts/Makefile.lib:real-obj-y := $(foreach m, $(obj-y), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y)) $($(m:.o=-))),$($(m:.o=-objs)) $($(m:.o=-y)),$(m)))

scripts/Makefile.libに,ぽいのがありました.objarch/x86の時のreal-obj-yを覗いてみましょう.

arch/x86/entry/built-in.a
arch/x86/events/built-in.a
arch/x86/realmode/built-in.a
arch/x86/kernel/built-in.a
arch/x86/mm/built-in.a
arch/x86/crypto/built-in.a
arch/x86/ia32/built-in.a
arch/x86/platform/built-in.a
arch/x86/net/built-in.a

お!ビルド対象のオブジェクトですね!でも数が少ない…

kbuildにおいてbuilt-in.aは,各ディレクトリのオブジェクトファイルをまとめたやつです.今回は,arch/x86の,各サブディレクトリのbuilt-in.aしかリストされていません.各サブディレクトリのビルドは誰がやるのでしょうか…?

再帰処理

完全に勘 ですが,Makefile.buildは再帰的に呼び出されるのでしょう.試しに「build」とかでMakefile.buildを検索してみると…

# Descending
# ---------------------------------------------------------------------------

PHONY += $(subdir-ym)
$(subdir-ym):
	$(Q)$(MAKE) $(build)=$@ \

ほらでた. subdir-ymとか,名前からして再帰処理関係です.objarch/x86のときのsubdir-ymの中身を見てみると,

arch/x86/crypto arch/x86/entry arch/x86/events arch/x86/ia32 arch/x86/kernel arch/x86/mm arch/x86/net arch/x86/platform arch/x86/realmode

あーね.arch/x86のサブディレクトリでしょう.念のため,arch/x86以下のディレクトリを確認します.

linux-5.6.7/arch/x86$ find . -type f
./crypto
...
./events
...
./entry
...

うん,ありますね.subdir-ymは1つ下のディレクトリのリストです.そして,それらに対して自分自身を呼び出していますね.どうやらMakefile.buildは,各ディレクトリに対して呼び出され,そこのobj-yobj-mをビルドしていくということで間違い無さそうです.

結局…

薄々勘づいていたのですが, ビルド対象ファイルの一覧など,どこにもありません. 各ディレクトリのビルド対象ファイルを,各ディレクトリに対して呼び出されたMakefile.buildが知っているだけで, だれも全体を把握していないのです…

Makefileを改造するのは筋が悪いということで,方針を考え直した結果,makeをdry-runするという方法を思いつきました.

他に調べたこと

stackoverflow

こんな質問を見つけました

Kernel compilation progress [closed]

linux kernelのコンパイルの進捗を表示したいんだけど,なんかいい方法ない? https://stackoverflow.com/questions/12251949/kernel-compilation-progress

これに対して,「Makefileを改造すれば?」的な返信がありますが,まぁ面倒ですよね.みんな困ってるんすね〜.

help-make

help-makeのメーリングリストに,似たようなことをやろうとしている人がいました.

Monitoring make progression

makeの進行状況を監視する方法はありますか?

https://lists.gnu.org/archive/html/help-make/2006-09/msg00025.html

に対しての返信では,

Re: Monitoring make progression

問題は,いくつのステップが実行されるかを知らないことです.Makeは,実行される可能性のあるステップの合計数さえ知りません.

そうそう.

最初に「make -n」を使用して,makeにステップを生成させる人のことを聞いたことがあります.それらを実行することで,ステップをカウントし,実際のmakeを実行します.

お!私と同じアプローチですね!

ただし,この方法結構不安定です.Makefileによっては,全てのルールが出力されない場合がありますし,出力されたルールが実際には実行されない場合もあります.また,プロジェクトによっては,ドライランは非常に重いでしょう.
でも,ドライランが今のところ最良の方法です.

https://lists.gnu.org/archive/html/help-make/2006-09/msg00026.html

はー,そうなんですね.makeは奥が深そうです.

GOLEM

先程のメーリングリストによれば,makeのドライランは決して完璧な方法ではないようです.

A Robust Approach for Variability Extraction from the Linux Build Systemによれば,Linux kernelのMakefileを解析するGOLEMというツールを開発したそうです(GOLEMは,リンク先のundertakerというツールのサブセットです).

これを使えばより確実にビルド対象ファイルをリストできると考えたのですが,私の環境ではGOLEMがうまく動きませんでした.ツールの使い方が悪いだけかも知れませんが…

まとめ

結局,Makefileをここまで読む必要はなかったのですが,まぁためになったのでよしです.

あーつかれた

続けて読む…

【Linux】カーネルビルドでも進捗が見たい!

2020/04/27

SICPの備忘録

2023/07/29

Gatsby製ブログで自然言語処理して関連記事を表示する

2020/06/12

ざっくりホーア論理

2024/09/28

TypeScriptにおける配列の共変性

2022/12/15

シングルユーザーモードのUbuntuにSSHする(SSH to Single User Mode Ubuntu 18.04)

2020/10/25

書いた人

sititou70のアイコン画像
sititou70

都内の社会人エンジニア4年生。Web技術、3DCG、映像制作が好き。