Linux カーネルでの .o ファイルのコンパイル プロセス

序文

組み込み Linux の開発プロセスにおいて、カーネルのコンパイルは決して避けては通れないテーマです。

カーネルのコンパイル システムを明確に理解すると、少なくとも次のことが可能になります。

  • カーネル全体の構造を理解する
  • コンパイル時間を節約する
  • コンパイルエラー時に問題を素早く特定する
  • カーネルのブートについて詳しく見る

この記事では、Linux カーネルの .o ファイルのコンパイルから kbuild メカニズムについて説明します。

日にち カーネルのバージョン 建築
2022-9-13 Linux5.4.200

実験

ターゲットログの拡張

例として、page_alloc.o のコンパイルからこの実験を開始します。

make mm/page_alloc.o

カーネル ルート ディレクトリの Makefile には次のものが含まれます。

https://elixir.bootlin.com/linux/v5.4.200/source/Makefile#L1733

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

直接見るのはあまり直感的ではありませんが、カーネル バージョン 2.6 の Makefile のステートメントは比較的明確です。

%.o: %.c prepare scripts FORCE
	$(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)

ロジックは比較的明確で、.oファイルはc同じ名前の . ファイルに依存し、コマンドを実行してファイルを生成します。

ヒント: ここではちょっとしたトリックを紹介します。構築ステートメントをコメントアウトし、エラー ログを使用してステートメント展開の構造を逆に観察します。

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

コンパイル コマンドを再度実行し、次のようにします。

#@make -f ./scripts/Makefile.build obj=mm \
single-build=1 \
need-builtin=1 need-modorder=1

ログから、実行が make を再度呼び出していることがわかりますmake mm/page_alloc.oscript/Makefile.buildこのルール ファイルを使用すると、渡されるパラメータは obj=mm です。

建てる

最初のセクションのログから、$(build)展開は次のとおりであることがわかります。

-f ./scripts/Makefile.build obj

それはどこで定義されていますか? グローバル検索後の答えは、scripts/Kbuild.includeヘッダー ファイルのようなものです。

https://elixir.bootlin.com/linux/v5.4.200/source/scripts/Kbuild.include#L160

###
# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj=
# Usage:
# $(Q)$(MAKE) $(build)=dir
build := -f $(srctree)/scripts/Makefile.build obj

このファイルに注目してscripts/Makefile.build、コンパイルに関連するステートメントを探してください.o

$(obj)/%.o: $(src)/%.c $(recordmcount_source) $$(objtool_dep) FORCE
	$(call if_changed_rule,cc_o_c) #调用if_changed_rull这个变量
	$(call cmd,force_checksrc)  #代码检查

$(call if_changed_rule,cc_o_c)here という変数に注目してくださいif_changed_rull。この変数も定義されています。scripts/Kbuild.include

# Usage: $(call if_changed_rule,foo)
# Will check if $(cmd_foo) or any of the prerequisites changed,
# and if so will execute $(rule_foo).
if_changed_rule = $(if $(any-prereq)$(cmd-check),$(rule_$(1)),@:)

論理的な判定文であり、条件が真の場合、カンマより前の動作が実行されます。条件が false の場合は、以下を実行します@:ここでの目的@:は、ログ出力を減らすことです。詳細については、提出資料を参照してください: kernel/git/torvalds/linux.git - Linux カーネル ソース ツリー

条件が true の場合、rule_$(1)展開は ですrule_cc_o_c

さて、scripts/Makefile.build検索の定義に戻りましょうrule_cc_o_c

define rule_cc_o_c
	$(call cmd_and_fixdep,cc_o_c)
	$(call cmd,gen_ksymdeps)
	$(call cmd,checksrc)
	$(call cmd,checkdoc)
	$(call cmd,objtool)
	$(call cmd,modversions_c)
	$(call cmd,record_mcount)
endef

OK、これは物語が真実に近づく前の最後のステップです。$(call cmd_and_fixdep,cc_o_c)私たちが焦点を当てるのは文章です。これは呼び出し関数でもあるため、ヘッダー ファイルに戻ってscripts/Kbuild.include定義を見つけます。

cmd_and_fixdep =                          \
	$(echo-cmd) $(cmd_$(1));              \
	...

経験によれば、cmd_cc_o_cそれを、まだ scripts/Makefile.build ファイルに定義されています。

cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<

ついに真実が明らかになりました!

要約する

.oファイルのコンパイルは次の 4 つの手順に分かれています。

    Makefile
    ---------------
    1: %.o: %.c
    	make -f scripts/Makefile.build obj=mm 

    scripts/Makefile.build
    ---------------
    2: $(obj)/%.o: $(src)/%.c
    	$(call if_changed_rule,cc_o_c)

    scripts/Makefile.build
    ---------------
    3: rule_cc_o_c
    	$(call cmd_and_fixdep,cc_o_c)

    scripts/Makefile.build
    ---------------
    4: cmd_cc_o_c
		$(CC) $(c_flags) -c -o $@ $<

ここでは、上で繰り返し述べた 2 つのファイルを強調します。

  • scripts/Makefile.build: ほぼすべての重要なルールが含まれています
  • scripts/Kbuild.include: ヘッダー ファイルと同様、多くの重要な機能が含まれています

おすすめ

転載: blog.csdn.net/fly_wt/article/details/127187319