內(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í)是直接make
或 make 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ì)命令的最后一條
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/