天天看點

2.6 Linux核心makefile解析1、2.4核心Makefile解析2、2.6核心Makefile解析3、核心編譯相關問題4、參考資料

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位元組的檔案頭,對其内容原封沒動。

2.6 Linux核心makefile解析1、2.4核心Makefile解析2、2.6核心Makefile解析3、核心編譯相關問題4、參考資料

這和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究竟是什麼内容呢,我們繼續往下追蹤。

2.6 Linux核心makefile解析1、2.4核心Makefile解析2、2.6核心Makefile解析3、核心編譯相關問題4、參考資料

進入/usr/src/目錄下檢視/usr/src/linux-2.6.16.60-0.21和/usr/src/linux-2.6.16.60-0.21-obj/i386/smp目錄下的實際内容:

2.6 Linux核心makefile解析1、2.4核心Makefile解析2、2.6核心Makefile解析3、核心編譯相關問題4、參考資料

可以看到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入口。

4、參考資料