當(dāng)前位置:首頁 > IT技術(shù) > 系統(tǒng)服務(wù) > 正文

Linux編譯系統(tǒng)之主Makefile(二)
2021-10-21 23:09:09

內(nèi)核Makefile

分析Makefile:找到第一個(gè)目標(biāo)文件和鏈接文件。

  • 第一個(gè)目標(biāo)文件(一路跟蹤啟動過程)
  • 鏈接文件:它表示內(nèi)核應(yīng)該放在哪里,里面的代碼是如何排布的。

Linux內(nèi)核Makefile文件分類

  • 頂層Makefile:是所有Makefile文件的核心,從總體上控制著內(nèi)核的編譯,鏈接
  • config:配置文件,在配置內(nèi)核時(shí)生成所有Makefile文件(包含頂層目錄及子目錄)都是根據(jù)config來決定使用哪些文件
  • arch/$(ARCH)/Makefile:對應(yīng)體系結(jié)構(gòu)的Makefile,它用來決定哪些體系結(jié)構(gòu)相關(guān)的文件與內(nèi)核的生成,并提供一些規(guī)則來生成特定格式的內(nèi)核映像
  • scripts/Makefile.*:Makefile共用的通用規(guī)則,腳本等.
  • kbuild Makefiles:各級目錄下的Makefiles,它們相對簡單,被上一層Makefile調(diào)用來編譯當(dāng)前目錄下的文件

對于Makefile 的文檔在Documentationkbuild下的 makefiles.txt 對內(nèi)核的makefile 講的很透徹。

1.從子目錄Makefile看起

每個(gè)子目錄下都會有個(gè) Makefile 文件

obj-$(CONFIG_A2232) += ser_a2232.o generic_serial.o
  • 若這個(gè)變量CONFIG_A2232 在配置文件中被定義成 y 時(shí),則 ser_a2232.c 和generic_serial.c文件會編譯成 .o 文件。最后會鏈接到內(nèi)核中去。
  • 若這個(gè)變量定義成 m 時(shí),則 ser_a2232.c 和generic_serial.c 文件會編譯成.ko模塊文件。

2.架構(gòu)相關(guān)的 Makefile 。(arch/$(ARCH)/Makefile)

分析一個(gè) Makefile 時(shí),從它的命令開始分析。編譯內(nèi)核時(shí)是直接makemake uImage
從頂層Makefile 一直往下走時(shí)會涉及到所有的東西。

  • 1.make uImage時(shí)這個(gè)目標(biāo) uImage 不在頂層的Makefile 中,在arch/arm/Makefile中定義了這個(gè)目標(biāo)。

我們是在頂層目錄make uImage的,則可知頂層 Makefile 會包含arch/arm/Makefile。
在頂層目錄的 Makefile 中搜索 “include”:

# srctree源碼樹,ARCH是arm
include $(srctree)/arch/$(ARCH)/Makefile
  • 2.頂層的.config最終會生成include/linux/autoconf.h頭文件給源碼用,另一個(gè)是include/config/auto.config文件
# Read in config
-include include/config/auto.conf
  • 可見配置文件也被包含到了頂層Makefile 中。
  • 可見,配置文件,子目錄下的Makefile 都會被包含進(jìn)頂層的Makefile 中去。則重點(diǎn)分析頂層Makefile.

3.頂層目錄的 Makefile.

make uImage 命令往下分析。

1.目標(biāo) uImage 定義在arch/arm/Makefile 中,找到uImage 目標(biāo)所在行,查看它相關(guān)的依賴。

# Convert bzImage to zImage
bzImage: zImage
BOOT_TARGETS	= zImage Image xipImage bootpImage uImage
$(BOOT_TARGETS): vmlinux
	$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@

可見 uImage 依賴于 vmlinux.uImage 是一個(gè)頭部+真正的內(nèi)核。所以制作這個(gè)uImage 時(shí)需要編譯出真正的內(nèi)核。這
個(gè)真正的內(nèi)核顯然就是 vmlinux 。vmlinux 的依賴在頂層目錄的 Makefile 中。:
all: vmlinux

在頂層目錄直接輸入 make ,默認(rèn)就是執(zhí)行第一個(gè)目標(biāo),"all"就是第一個(gè)目標(biāo)。這個(gè)目標(biāo)也
是依賴于 vmlinux 。即都是要先生成 vmlinux .

2.vmlinux的依賴:

頂層Makefile中

# Include targets which we want to
# execute if the rest of the kernel build went well.
vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps) FORCE
ifdef CONFIG_HEADERS_CHECK
	$(Q)$(MAKE) -f $(srctree)/Makefile headers_check
endif
ifdef CONFIG_SAMPLES
	$(Q)$(MAKE) $(build)=samples
endif
ifdef CONFIG_BUILD_DOCSRC
	$(Q)$(MAKE) $(build)=Documentation
endif
ifdef CONFIG_GDB_SCRIPTS
	$(Q)ln -fsn `cd $(srctree) && /bin/pwd`/scripts/gdb/vmlinux-gdb.py
endif
	+$(call if_changed,link-vmlinux)
  • scripts/link-vmlinux.sh:shell腳本
  • $(vmlinux-deps)vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN)其中
    • $(KBUILD_LDS):鏈接腳本
    • $(KBUILD_VMLINUX_INIT):一些初始化代碼
    • $(KBUILD_VMLINUX_MAIN):一些主要代碼

3.分別分析這些變量:在頂層Makefile中

# Externally visible symbols (used by link-vmlinux.sh)
export KBUILD_VMLINUX_INIT := $(head-y) $(init-y)
export KBUILD_VMLINUX_MAIN := $(core-y) $(libs-y) $(drivers-y) $(net-y)
export KBUILD_LDS          := arch/$(SRCARCH)/kernel/vmlinux.lds
  • $(head-y):在頂層目錄的Makefile 中沒有定義,則會在架構(gòu)目錄下(arch/arm/Makefile)的Makefile 中。MMUEXT等于空,相當(dāng)于head-y=head.o
#Default value
head-y		:= arch/arm/kernel/head$(MMUEXT).o
  • init-y在頂層 Makefile 中。
init-y		:= init/
init-y		:= $(patsubst %/, %/built-in.o, $(init-y))
  • %/代表的是init/目錄下的所有文件。
  • %/built-in.o 相當(dāng)于在 init/下的文件全部編譯成 built-in.o。這個(gè)函數(shù)的意思是:init-y := $(patsubst %/, %/built-in.o, $(init-y)) = $(patsubst %/, %/built-in.o, init/) = init/built-in.o即 init-y 等于 init 目錄下所有涉及的那些文件,這些文件會被編譯成一個(gè)built-in.o。,也可以使用obj=$(dir:%.c=%.o)效果一樣的
vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)
  • core-y :核心
core-y		:= usr/
core-y		+= kernel/ mm/ fs/ ipc/ security/ crypto/ block/
 		     $(core-y) $(core-m) $(drivers-y) $(drivers-m) 

# core-y = usr/built-in.o
core-y		:= $(patsubst %/, %/built-in.o, $(core-y))

意思是最后

core-y = usr/built-in.o
+= kernel/built-in.o
+= mm/built-in.o
+= fs/built-in.o
+= ipc/built-in.o
+= security/built-in.o
+= crypto/built-in.o
+= block/built-in.o

就是將這些目錄(usr、kernel、mm、fs、ipc、security、crypto、block)下涉及的文件分別
編譯成built-in.o 不是所有文件,而是涉及到的文件。

  • libs-y:庫
libs-y		:= lib/
		     $(net-y) $(net-m) $(libs-y) $(libs-m)))
libs-y1		:= $(patsubst %/, %/lib.a, $(libs-y))
libs-y2		:= $(patsubst %/, %/built-in.o, $(libs-y))
libs-y		:= $(libs-y1) $(libs-y2)

最后

libs-y = lib/lib.a
+= lib/built-in.o
  • drivers-y:驅(qū)動
drivers-y	:= drivers/ sound/ firmware/
		     $(core-y) $(core-m) $(drivers-y) $(drivers-m) 
drivers-y	:= $(patsubst %/, %/built-in.o, $(drivers-y))

意思是最后 drivers-y = drivers/built-in.o (將 drivers 目錄下所有涉及的文件編譯成
built-in.o 文件)
+= sound/built-in.o (將 sound 目錄下所有涉及的編譯成 built-in.o 文件)
+= firmware/built-in.o

  • net-y:網(wǎng)絡(luò)
net-y		:= net/
		     $(net-y) $(net-m) $(libs-y) $(libs-m)))
net-y		:= $(patsubst %/, %/built-in.o, $(net-y))

意思是最后,將 net/目錄下的所有涉及到的文件編譯 built-in.o 這個(gè)文件。

從面的依賴文件展開來看,源材料就是上面這一大堆東西。這些東西如何組合成一個(gè)內(nèi)核
(鏈接成在一塊),要看 vmlinux 如何編譯的。

4.vmlinux如何編譯
編譯時(shí)是通過這些命令來編譯的。這些命令最終會生成什么東西?可以通過這里一一分析下
去。這里涉及的腳本、函數(shù)太龐大了。沒精力去做。想知道上在的源材料如何編譯成內(nèi)核:
方法1:分析 Makefile .
方法2:直接編譯內(nèi)核??淳幾g過程。
a.rm vmlinux 先刪除原來編譯得到的內(nèi)核。
b.make uImage V=1 (V=1 是更加詳細(xì)的列出那些命令。)
我們關(guān)心詳細(xì)命令的最后一條
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-0dypAYEm-1634653707268)(image/vmlinux_lk.jpg)]

arm-linux-ld -EL -p --no-undefined -X -o vmlinux (-o 這里生成 vmlinux 了)
-T arch/arm/kernel/vmlinux.lds 鏈接腳本

arch/arm/kernel/head.o
arch/arm/kernel/init_task.o
init/built-in.o

--start-group
usr/built-in.o
arch/arm/kernel/built-in.o
arch/arm/mm/built-in.o
arch/arm/common/built-in.o
arch/arm/mach-s3c2410/built-in.o
arch/arm/mach-s3c2400/built-in.o
arch/arm/mach-s3c2412/built-in.o
arch/arm/mach-s3c2440/built-in.o
arch/arm/mach-s3c2442/built-in.o
arch/arm/mach-s3c2443/built-in.o
arch/arm/nwfpe/built-in.o
arch/arm/plat-s3c24xx/built-in.o
kernel/built-in.o
mm/built-in.o
fs/built-in.o
ipc/built-in.o
security/built-in.o
crypto/built-in.o
block/built-in.o
arch/arm/lib/lib.a
lib/lib.a
arch/arm/lib/built-in.o
lib/built-in.o
drivers/built-in.o
sound/built-in.o
net/built-in.o
--end-group .tmp_kallsyms2.o

鏈接腳本:arch/arm/kernel/vmlinux.lds(決定內(nèi)核如何排布).
鏈接腳本vmlinux.lds 是由vmlinux.lds.S 文件生成的。

# arch/arm/kernel/vmlinux.lds.S
. = (0xc0000000) + 0x0008000;

這里一開始便指定了內(nèi)核放在哪里。這顯然是虛擬的地址。

.text.head : {
	_stext = .;
	_sinittext = .;
	*(.text.head)
}

一開始是放“*”(指所有文件)的 “.text.head”段。

.init : { /*Init code and data*/
	*(.init.text)

再接著是放所有文件的“.init.text”段。這些所有文件排放在相應(yīng)的 “段” 中,排放的順序就是如下“鏈接腳本”后面“.o”文件的排布順序:首先放 head.o 的,等等。文件的順序由上面這些“.o”文件出現(xiàn)的順序?yàn)闇?zhǔn)。里面的代碼段等等其他段的排放由“vmlinux.lds”決定,首先放“.text.head”段,其次是“.init.text”段等依次往下排(參見“vmlinux.lds”內(nèi)容)。

本文摘自 :https://www.cnblogs.com/

開通會員,享受整站包年服務(wù)立即開通 >