Linux核心的整個makefile編譯系統稱為kbuild系統,負責linux核心源碼檔案架構的編譯和連結工作。2.4核心和2.6核心的kbuild實作上有非常大的差異,但是實作的功能基本是一樣的,從頂層makefile目标到一個個子檔案夾的遞歸調用,最終又合成一個頂層目标。2.4核心的makefile比較直覺比較容易了解些,2.6的makefile檔案比較難看懂,是以本文重點解析2.4核心的makefile架構,功能上其實大緻相同。
可以參考附件中注釋版的實際makefile進行學習。主要還隻是記錄我了解了的部分還有很多細節需要自己去學習分析。
1、2.4核心Makefile解析
以i386架構為例,2.4核心的makefile重點需要關注以下檔案:
- $(TOPDIR) = lniux源碼根目錄
- $(TOPDIR)\makefile
- $(TOPDIR)\rules.makefile
- $(TOPDIR)\arch\i386\makefile
- $(TOPDIR)\arch\i386\boot\makefile
- $(TOPDIR)\arch\i386\boot\compressed\makefile
1.1、編譯目标
核心編譯需要用到的目标大概有以下幾個:
- Make menuconfig (config、xconfig…):
讀取各個子檔案夾下面的config.in檔案生成config菜單,根據使用者在菜單下的選擇結果最終在源碼根目錄下生成一個.config檔案。這個.config檔案包含使用者的所有配置。
- Make dep:
在編譯前,生成檔案之間的依賴關系。
- Make vmlinux:
生成核心的elf格式檔案。Vmlinux是最純粹的核心檔案,沒有進過壓縮,也不帶boot啟動代碼。
- Make bzImage、zImage:
bzImage、zImage即vmlinuz檔案。i386的vmlinuz檔案生成方式是先将elf格式的vmlinux檔案使用objcopy工具轉換成bin格式檔案,再将bin檔案壓縮并加上自解壓的頭,最終使用build工具将壓縮核心和啟動扇區檔案接在一起生成bzImage檔案。
bzImage和zImage的差別就是,zImage就是老版習慣使用低端640k的實體記憶體,bzImage使用1M以上的實體記憶體,現在基本都使用bzImage。
- Make install:
安裝編譯出來的核心,實際動作就是拷貝bzImage和System.map檔案到/boot路徑下。
- Make modules、modules_install:
Make modules編譯在make menuconfig時,選擇為M的子產品。modules_install是安裝子產品,即拷貝編譯出來的子產品到相應目錄。
- Make clean、mrproper、distclean:
由小到大範圍的clean動作。
1.2、$(TOPDIR)\makefile解析
Vmlniux目标的生成規則:
vmlinux: include/linux/version.h $(CONFIGURATION) init/main.o init/version.o init/do_mounts.o linuxsubdirs
$(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o init/do_mounts.o \
--start-group \
$(CORE_FILES) \
$(DRIVERS) \
$(NETWORKS) \
$(LIBS) \
--end-group \
-o vmlinux
$(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > System.map
Vmlinux需要的各個子檔案夾目标的編譯目标:
SUBDIRS =kernel drivers mm fs net ipc lib crypto
$(patsubst %, _dir_%, $(SUBDIRS)) : dummy include/linux/version.h include/config/MARKER
$(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" -C $(patsubst _dir_%, %, [email protected]) // _dir_字首的子目錄目标,生成方法為直接進入子目錄make,make -C $(SUBDIRS)
Clean相關目标的生成規則:
Menuconfig等配置目标:
symlinks:
rm -f include/asm
( cd include ; ln -sf asm-$(ARCH) asm) // 建立符号連結include/asm指向架構目錄include/asm-$(ARCH)
@if [ ! -d include/linux/modules ]; then \
mkdir include/linux/modules; \ // 建立檔案夾include/linux/modules Q:(編譯子產品的時候自動拷貝頭檔案進來??)
fi
oldconfig: symlinks // 各種make config方式,生成根目錄下的
$(CONFIG_SHELL) scripts/Configure -d arch/$(ARCH)/config.in
xconfig: symlinks
$(MAKE) -C scripts kconfig.tk
wish -f scripts/kconfig.tk
menuconfig: include/linux/version.h symlinks
$(MAKE) -C scripts/lxdialog all
$(CONFIG_SHELL) scripts/Menuconfig arch/$(ARCH)/config.in
config: symlinks
$(CONFIG_SHELL) scripts/Configure arch/$(ARCH)/config.in
1.3、$(TOPDIR)\rules.makefile解析
編譯vmlinux時,各個子檔案夾的子目标的編譯規則:
first_rule: sub_dirs // 編譯vmlinux時,子目錄每個makefile的預設目标就是first_rule,依賴于sub_dirs,對所有子目錄進行make -C sub_dirs遞歸
$(MAKE) all_targets
all_targets: $(O_TARGET) $(L_TARGET) // all_targets目标,依賴于所有.o目标和所有.a目标
#
# Rule to compile a set of .o files into one .o file // 将多個.o編譯成一個.o的規則。
#
ifdef O_TARGET
$(O_TARGET): $(obj-y)
rm -f [email protected]
ifneq "$(strip $(obj-y))" ""
$(LD) $(LDFLAGS) $(EXTRA_LDFLAGS) -r -o [email protected] $(filter $(obj-y), $^)
else
$(AR) rcs [email protected]
endif
@ ( \
echo 'ifeq ($(strip $(subst $(comma),:,$(LDFLAGS) $(EXTRA_LDFLAGS) $(obj-y))),$$(strip $$(subst $$(comma),:,$$(LDFLAGS) $$(EXTRA_LDFLAGS) $$(obj-y))))' ; \
echo 'FILES_FLAGS_UP_TO_DATE += [email protected]' ; \
echo 'endif' \
) > $(dir [email protected])/.$(notdir [email protected]).flags
endif # O_TARGET
#
# Rule to compile a set of .o files into one .a file // 将多個.o編譯成一個.a的規則。
#
ifdef L_TARGET
$(L_TARGET): $(obj-y)
rm -f [email protected]
$(AR) $(EXTRA_ARFLAGS) rcs [email protected] $(obj-y)
@ ( \
echo 'ifeq ($(strip $(subst $(comma),:,$(EXTRA_ARFLAGS) $(obj-y))),$$(strip $$(subst $$(comma),:,$$(EXTRA_ARFLAGS) $$(obj-y))))' ; \
echo 'FILES_FLAGS_UP_TO_DATE += [email protected]' ; \
echo 'endif' \
) > $(dir [email protected])/.$(notdir [email protected]).flags
endif
遞歸編譯子檔案夾目标的編譯規則:
#
# A rule to make subdirectories
#
subdir-list = $(sort $(patsubst %,_subdir_%,$(SUB_DIRS)))
sub_dirs: dummy $(subdir-list) // sub_dirs目标,依賴于$(subdir-list)
ifdef SUB_DIRS
$(subdir-list) : dummy // $(subdir-list)目标,依賴于對子目錄的遞歸調用make -C subdir
$(MAKE) -C $(patsubst _subdir_%,%,[email protected])
endif
子產品的編譯規則:
#
# A rule to make modules // ---------- modules的生成規則 ----------
#
ALL_MOBJS = $(filter-out $(obj-y), $(obj-m)) // modules實際目标,取obj-m,那麼obj-m module必須做到一個子產品對應一個.o檔案,而不能一個子產品對應多個.c
ifneq "$(strip $(ALL_MOBJS))" ""
MOD_DESTDIR := $(shell $(CONFIG_SHELL) $(TOPDIR)/scripts/pathdown.sh) // MOD_DESTDIR目标,取主目錄TOPDIR到目前目錄的路徑。
endif
unexport MOD_DIRS
MOD_DIRS := $(MOD_SUB_DIRS) $(MOD_IN_SUB_DIRS) // MOD_DIRS, 包括MOD_SUB_DIRS、MOD_IN_SUB_DIRS
ifneq "$(strip $(MOD_DIRS))" ""
.PHONY: $(patsubst %,_modsubdir_%,$(MOD_DIRS)) // _modsubdir_$(MOD_DIRS)目标,進入子目錄,繼續遞歸make modules目标
$(patsubst %,_modsubdir_%,$(MOD_DIRS)) : dummy
$(MAKE) -C $(patsubst _modsubdir_%,%,[email protected]) modules
.PHONY: $(patsubst %,_modinst_%,$(MOD_DIRS)) // _modinst_$(MOD_DIRS)目标,進入子目錄,繼續遞歸make modules_install目标
$(patsubst %,_modinst_%,$(MOD_DIRS)) : dummy
$(MAKE) -C $(patsubst _modinst_%,%,[email protected]) modules_install
endif
.PHONY: modules
modules: $(ALL_MOBJS) dummy \ // modules目标,依賴于_modsubdir_$(MOD_DIRS)、$(ALL_MOBJS)。$(ALL_MOBJS)生成真正的驅動檔案編譯
$(patsubst %,_modsubdir_%,$(MOD_DIRS))
.PHONY: _modinst__
_modinst__: dummy // _modinst__目标,
ifneq "$(strip $(ALL_MOBJS))" ""
mkdir -p $(MODLIB)/kernel/$(MOD_DESTDIR) // 建立/lib/modules/$(KVER)/kernel/$(MOD_DESTDIR)檔案夾
cp $(sort $(ALL_MOBJS)) $(MODLIB)/kernel/$(MOD_DESTDIR) // 拷貝$(ALL_MOBJS)到建立的檔案夾中
endif
.PHONY: modules_install
modules_install: _modinst__ \ // modules_install目标,依賴于_modinst__、_modinst_$(MOD_DIRS)。
$(patsubst %,_modinst_%,$(MOD_DIRS))
1.4、$(TOPDIR)\arch\i386\makefile解析
定義編譯vmlinux所需要的架構相關的部分:
LD=$(CROSS_COMPILE)ld -m elf_i386
OBJCOPY=$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S
LDFLAGS=-e stext
LINKFLAGS =-T $(TOPDIR)/arch/i386/vmlinux.lds $(LDFLAGS) // LINKFLAGS定義,調用link配置檔案$(TOPDIR)/arch/i386/vmlinux.lds
HEAD := arch/i386/kernel/head.o arch/i386/kernel/init_task.o // HEAD定義,在生成vmlinux的時候用到
SUBDIRS += arch/i386/kernel arch/i386/mm arch/i386/lib // SUBDIRS定義,根據架構增加新的子目錄。加進到SUBDIRS以後,會被子檔案夾makefile的宏給自動調用。arch\i386下的 kernel mm lib檔案夾是由這個包含來啟動make編譯的。
CORE_FILES := arch/i386/kernel/kernel.o arch/i386/mm/mm.o $(CORE_FILES) // CORE_FILES定義,在生成vmlinux的時候用到
LIBS := $(TOPDIR)/arch/i386/lib/lib.a $(LIBS) $(TOPDIR)/arch/i386/lib/lib.a // LIBS定義,在生成vmlinux的時候用到
1.5、$(TOPDIR)\arch\i386\boot\makefile解析
生成bzImage目标,使用build工具将boot扇區和壓縮過的vmlinux檔案建構在一起:
bzImage: $(CONFIGURE) bbootsect bsetup compressed/bvmlinux tools/build // bzImage目标
$(OBJCOPY) compressed/bvmlinux compressed/bvmlinux.out // 将bvmlinux裝換成bvmlinux.out
tools/build -b bbootsect bsetup compressed/bvmlinux.out $(ROOT_DEV) > bzImage // 使用tools/build将bbootsect bsetup compressed/bvmlinux.out生成bzImage
1.6、$(TOPDIR)\arch\i386\boot\compressed\makefile解析
生成bzImage所需要的壓縮核心檔案。将vmlniux檔案,由elf格式轉換成bin格式,并壓縮,在檔案頭部再加上解壓縮代碼:
ZIMAGE_OFFSET =
BZIMAGE_OFFSET =
ZLINKFLAGS = -Ttext $(ZIMAGE_OFFSET) $(ZLDFLAGS)
BZLINKFLAGS = -Ttext $(BZIMAGE_OFFSET) $(ZLDFLAGS)
all: vmlinux
vmlinux: piggy.o $(OBJECTS)
$(LD) $(ZLINKFLAGS) -o vmlinux $(OBJECTS) piggy.o
bvmlinux: piggy.o $(OBJECTS)
$(LD) $(BZLINKFLAGS) -o bvmlinux $(OBJECTS) piggy.o
head.o: head.S
$(CC) $(AFLAGS) -traditional -c head.S
comma := ,
misc.o: misc.c
$(CC) $(CFLAGS) -DKBUILD_BASENAME=$(subst $(comma),,$(subst -,,$(*F))) -c misc.c
piggy.o: $(SYSTEM)
tmppiggy=_tmp$$$$piggy; \
rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk; \
$(OBJCOPY) $(SYSTEM) $$tmppiggy; \
gzip -f - < $$tmppiggy > $$tmppiggy.gz; \
echo "SECTIONS { .data : { input_len = .; LONG(input_data_end - input_data) input_data = .; *(.data) input_data_end = .; }}" > $$tmppiggy.lnk; \
$(LD) -r -o piggy.o -b binary $$tmppiggy.gz -b elf32-i386 -T $$tmppiggy.lnk; \
rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk
2、2.6核心Makefile解析
以i386架構為例,2.6核心的makefile重點需要關注以下檔案:
- $(TOPDIR) = lniux源碼根目錄
- $(TOPDIR)\makefile
- $(TOPDIR) \scripts\Kbuild.include
- $(TOPDIR) \scripts\makefile.build
- $(TOPDIR) \scripts\makefile.lib
- $(TOPDIR) \scripts\kconfig\makefile
- $(TOPDIR)\arch\i386\makefile
- $(TOPDIR)\arch\i386\boot\makefile
- $(TOPDIR)\arch\i386\boot\compressed\makefile
2.6的kbuild系統更加複雜,這裡隻重點講和2.4差異的地方。
2.1、編譯目标
使用make help指令,可以檢視所有可執行的make目标。
Make menuconfig (config、xconfig…):
2.6核心已經不是config.in檔案,而改成了kconfig檔案。
讀取各個子檔案夾下面的kconfig檔案生成config菜單,根據使用者在菜單下的選擇結果最終在源碼根目錄下生成一個.config檔案。這個.config檔案包含使用者的所有配置。
2.2、$(TOPDIR)\makefile解析
2.6的makefile複雜于2.4makefile,最大一部分就是定義了很多自定義make函數,然後再使用call來調用,例如:
$(call cmd,vmlinux_) = cmd_vmlinux_
$(call if_changed_rule,vmlinux_) = rule_vmlinux_
有一個非常重要的自定義變量在編譯目标的時候頻繁用到:
###
# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj=
# Usage:
# $(Q)$(MAKE) $(build)=dir
build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj // build目标。-f指定makefile檔案,obj =
Vmlinux目标的生成規則:
# vmlinux image - including updated kernel symbols // 實際的vmlinux目标。
vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) $(kallsyms.o) FORCE
$(call if_changed_rule,vmlinux__) // 調用rule_vmlinux__
$(Q)rm -f .old_version
Vmlinux需要的各個子檔案夾目标的編譯目标:
vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \ // vmlinux需要的檔案夾。去掉檔案路徑最後一個"/"
$(core-y) $(core-m) $(drivers-y) $(drivers-m) \
$(net-y) $(net-m) $(libs-y) $(libs-m)))
.PHONY: $(vmlinux-dirs)
$(vmlinux-dirs): prepare scripts
$(Q)$(MAKE) $(build)=[email protected] // 這裡啟動vmlinux各組成部分的編譯
2.6核心會建構内置的符号表,即kallsyms元件。其編譯規則如下:
ifdef CONFIG_KALLSYMS_EXTRA_PASS
last_kallsyms :=
else
last_kallsyms :=
endif
kallsyms.o := .tmp_kallsyms$(last_kallsyms).o
define verify_kallsyms // 定義指令包:verify_kallsyms
$(Q)$(if $($(quiet)cmd_sysmap), \
echo ' $($(quiet)cmd_sysmap) .tmp_System.map' &&) \
$(cmd_sysmap) .tmp_vmlinux$(last_kallsyms) .tmp_System.map
$(Q)cmp -s System.map .tmp_System.map || \
(echo Inconsistent kallsyms data; \
echo Try setting CONFIG_KALLSYMS_EXTRA_PASS; \
rm .tmp_kallsyms* ; /bin/false )
endef
# Update vmlinux version before link
# Use + in front of this rule to silent warning about make -j1
# First command is ':' to allow us to use + in front of this rule
cmd_ksym_ld = $(cmd_vmlinux__)
define rule_ksym_ld // 定義指令包:rule_ksym_ld
:
+$(call cmd,vmlinux_version)
$(call cmd,vmlinux__)
$(Q)echo '[email protected] := $(cmd_vmlinux__)' > $(@D)/.$(@F).cmd
endef
# Generate .S file with all kernel symbols
quiet_cmd_kallsyms = KSYM [email protected]
cmd_kallsyms = $(NM) -n $< | $(KALLSYMS) \
$(if $(CONFIG_KALLSYMS_ALL),--all-symbols) > [email protected]
.tmp_kallsyms1.o .tmp_kallsyms2.o .tmp_kallsyms3.o: %.o: %.S scripts FORCE
$(call if_changed_dep,as_o_S)
.tmp_kallsyms%.S: .tmp_vmlinux% $(KALLSYMS)
$(call cmd,kallsyms)
# .tmp_vmlinux1 must be complete except kallsyms, so update vmlinux version
.tmp_vmlinux1: $(vmlinux-lds) $(vmlinux-all) FORCE
$(call if_changed_rule,ksym_ld)
.tmp_vmlinux2: $(vmlinux-lds) $(vmlinux-all) .tmp_kallsyms1.o FORCE
$(call if_changed,vmlinux__)
.tmp_vmlinux3: $(vmlinux-lds) $(vmlinux-all) .tmp_kallsyms2.o FORCE
$(call if_changed,vmlinux__)
2.3、$(TOPDIR) \scripts\Kbuild.include解析
定義了make中使用的通用自定義函數cmd、build、if_changed_rule等等:
###
# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj=
# Usage:
# $(Q)$(MAKE) $(build)=dir
build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj // build目标。-f指定makefile檔案,obj =
# If quiet is set, only print short version of command // cmd表達式。如果quiet_格式指令被定義,,僅列印一個版本号,不列印指令的詳細過程
cmd = @$(if $($(quiet)cmd_$()),\
echo ' $(call escsq,$($(quiet)cmd_$(1)))' &&) $(cmd_$())
# Usage: $(call if_changed_rule,foo)
# will check if $(cmd_foo) changed, or any of the prequisites changed,
# and if so will execute $(rule_foo)
if_changed_rule = $(if $(strip $? $(call arg-check, $(cmd_$()), $(cmd_[email protected])) ),\ // if_changed_rule表達式
@set -e; \
$(rule_$()))
2.4、$(TOPDIR) \scripts\makefile.build解析
解析了使用$(build)方法編譯vmlniux檔案夾各子目标的規則,vmlniux的子目标的編譯和2.4核心不同,子檔案夾下生成的子目标都是相同的built-in.o:
.PHONY: __build
__build: // 預設目标
ifneq ($(strip $(obj-y) $(obj-m) $(obj-n) $(obj-) $(lib-target)),)
builtin-target := $(obj)/built-in.o // builtin-target變量等于檔案夾下面的built-in.o
endif
# We keep a list of all modules in $(MODVERDIR)
__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \ // __build目标的依賴: $(builtin-target) $(lib-target) $(extra-y) $(obj-m) $(subdir-ym) $(always)
$(if $(KBUILD_MODULES),$(obj-m)) \
$(subdir-ym) $(always)
@:
#
# Rule to compile a set of .o files into one .o file // 規則:多個.o編譯進一個.o
#
ifdef builtin-target
quiet_cmd_link_o_target = LD [email protected]
# If the list of objects to link is empty, just create an empty built-in.o
cmd_link_o_target = $(if $(strip $(obj-y)),\
$(LD) $(ld_flags) -r -o [email protected] $(filter $(obj-y), $^),\
rm -f [email protected]; $(AR) rcs [email protected])
$(builtin-target): $(obj-y) FORCE // builtin-target目标的生成方法,
$(call if_changed,link_o_target) // 調用表達式:if_changed -> cmd_link_o_target
targets += $(builtin-target)
endif # builtin-target
#
# Rule to compile a set of .o files into one .a file // 規則:多個.o編譯進一個.a
#
ifdef lib-target
quiet_cmd_link_l_target = AR [email protected]
cmd_link_l_target = rm -f [email protected]; $(AR) $(EXTRA_ARFLAGS) rcs [email protected] $(lib-y)
$(lib-target): $(lib-y) FORCE
$(call if_changed,link_l_target)
targets += $(lib-target)
endif
2.5、$(TOPDIR) \scripts\makefile.lib解析
提供子目錄編譯庫時的編譯規則。
2.6、$(TOPDIR) \scripts\kconfig\makefile解析
提供make menuconfig等config目标的編譯規則。
3、核心編譯相關問題
3.1、Q1:mips系統的vmlinuz格式?
Mips系統在生成核心的時候使用mkimage.exe工具将檔案vmlinux轉換成vmlinuz:“mkimage -A mips -O linux -T kernel -C none -a 0 -e 0 -n vmlinux -d vmlinux vmlinuz”。
經研究發現mips系統的vmlinuz依然是elf格式,mkimage.exe工具隻是簡單的給vmlinux檔案加了一個0x40位元組的檔案頭,對其内容原封沒動。
這和386架構的vmlinuz生成大不相同,386架構得做法是使用objcopy指令将elf格式的vmlinux檔案轉換成bin格式的vmlinuz檔案:
/* --------- linux-2.4.32\arch\i386\boot\Makefile --------- */
bzImage: $(CONFIGURE) bbootsect bsetup compressed/bvmlinux tools/build // bzImage目标
$(OBJCOPY) compressed/bvmlinux compressed/bvmlinux.out // 将bvmlinux裝換成bvmlinux.out
tools/build -b bbootsect bsetup compressed/bvmlinux.out $(ROOT_DEV) > bzImage // 使用tools/build将bbootsect bsetup compressed/bvmlinux.out生成bzImage
3.2、Q2:/usr/src/linux-obj檔案的來曆?
Suse linux和編譯核心相關,在/lib/modules/2.6.16.60-0.21-smp檔案夾下面提供了build和source連個檔案夾,可以看到其實是兩個符号連結,build連結到/usr/src/linux-2.6.16.60-0.21-obj/i386/smp,source連結到/usr/src/linux-2.6.16.60-0.21。編譯子產品時,一般會将核心makefile指向/lib/modules/2.6.16.60-0.21-smp/build路徑,而build實際上就是指向/usr/src/linux-2.6.16.60-0.21-obj/i386/smp目錄,那/usr/src/linux-2.6.16.60-0.21-obj/i386/smp究竟是什麼内容呢,我們繼續往下追蹤。
進入/usr/src/目錄下檢視/usr/src/linux-2.6.16.60-0.21和/usr/src/linux-2.6.16.60-0.21-obj/i386/smp目錄下的實際内容:
可以看到linux和linux-2.6.16.60-0.21檔案夾下是核心源檔案目錄,linux-2.6.16.60-0.21-obj和linux-obj是編譯子產品時指向的核心makefile目錄。但是核心makefile目錄不是應該指向核心源檔案檔案夾嗎,為什麼指向這個linux-obj檔案夾,這個linux-obj檔案夾到底是幹什麼的?
仔細研究以後,才發現其中的奧妙:編譯子產品時可以直接指向linux源代碼目錄,但是前提是這份源代碼必須編譯過,不然很多編譯子產品需要的元件都還沒有産生,而且源代碼檔案夾隻能儲存一份目前配置,而linux-obj的子檔案夾下面儲存了很多種不同的配置,編譯子產品時可以根據自己需要的配置選取其中一種即可,而不用手工重新配置和編譯核心。
是以linux-obj的作用是:1、儲存了子產品需要的元件,不用整個編譯源碼;2、儲存了多種配置,可以靈活選擇。應該是suse為了支援子產品開發,特意封裝的一系列檔案夾。
3.3、Q3:核心編譯頭檔案自解析?
由于核心需要自解析,是以它不能使用外部的庫和頭檔案。其所有的頭檔案都包含在 (TOPDIR)\include下,所有需要的庫都包含在 (TOPDIR) \lib下。編譯的時候,為了避免使用libc庫的頭檔案,gcc 使用—nostdinc選項。
3.4、Q4:核心的符号表自管理?
核心不同于使用者态程式,使用者态程式可以直接使用elf檔案執行,有動态加載庫來幫其解析加載elf檔案格式和符号表。而核心不可能直接使用elf檔案,也沒有别人為其解析elf檔案中的符号表。是以核心管理符号表,需要把elf檔案中的符号表提取出來,當成數組編譯到核心映像當中去,2.6核心的kallsyms元件就是幹這個的。
其原理就是将整個elf格式的vmlinux檔案,使用nm指令和scripts/kallsyms腳本将其符号表編譯成kallsyms.o檔案,再将kallsyms.o和vmlinux連接配接生成最終的vmlinux。
Vxworks映像也是這樣幹的:
3.5、Q5:elf格式檔案和bin格式檔案的不同加載方法?
Elf檔案是不能自加載的,需要有額外的代碼解析elf檔案格式,将相應的段加載到對應位置,再根據解析的elf檔案entry,跳轉到對應位址執行。
但是很多時候沒有解析elf的boot,需要檔案一上去就能運作,這時就需要将elf轉換成bin檔案,并能滿足自運作的需要。其中的原理是這樣的:objcopy将elf轉換成bin檔案,它隻會将所有要加載的段彙總,根據其加載到記憶體中的位置放到bin檔案當中,最低加載位址作為bin檔案的0偏移,段和段之間無效的空間全填0,是以bin檔案有時候做出來很大。而boot代碼隻需要将bin檔案整體copy到加載基位址上去即可,不需要解析elf檔案格式了。這解決了elf檔案解析問題,但是entry問題怎麼解決呢?怎麼能保證bin檔案的第一句話就是elf的entry語句呢??這個使用連結手段來解決,使用ld的連結順序,或者是連結腳本.lds檔案來保證第一句話所在的子產品被連結到檔案的最開始,是以bin檔案的最開始就是entry入口。