2.6 Linux内核makefile解析

来源:互联网 发布:微信网页授权 多域名 编辑:程序博客网 时间:2024/06/06 00:56

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_%, %, $@)         // _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:(编译模块的时候自动拷贝头文件进来??)    fioldconfig: symlinks                                                                         // 各种make config方式,生成根目录下的    $(CONFIG_SHELL) scripts/Configure -d arch/$(ARCH)/config.inxconfig: symlinks    $(MAKE) -C scripts kconfig.tk    wish -f scripts/kconfig.tkmenuconfig: include/linux/version.h symlinks    $(MAKE) -C scripts/lxdialog all    $(CONFIG_SHELL) scripts/Menuconfig arch/$(ARCH)/config.inconfig: 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_targetsall_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 $@    ifneq "$(strip $(obj-y))" ""    $(LD) $(LDFLAGS) $(EXTRA_LDFLAGS) -r -o $@ $(filter $(obj-y), $^)    else    $(AR) rcs $@    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 += $@' ; \        echo 'endif' \    ) > $(dir $@)/.$(notdir $@).flagsendif # 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 $@    $(AR) $(EXTRA_ARFLAGS) rcs $@ $(obj-y)    @ ( \        echo 'ifeq ($(strip $(subst $(comma),:,$(EXTRA_ARFLAGS) $(obj-y))),$$(strip $$(subst $$(comma),:,$$(EXTRA_ARFLAGS) $$(obj-y))))' ; \        echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \        echo 'endif' \    ) > $(dir $@)/.$(notdir $@).flagsendif

递归编译子文件夹目标的编译规则:

## 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_%,%,$@)endif

模块的编译规则:

## A rule to make modules                                                                    // ---------- modules的生成规则 ----------#ALL_MOBJS = $(filter-out $(obj-y), $(obj-m))                                               // modules实际目标,取obj-m,那么obj-m module必须做到一个模块对应一个.o文件,而不能一个模块对应多个.cifneq "$(strip $(ALL_MOBJS))" ""                                                           MOD_DESTDIR := $(shell $(CONFIG_SHELL) $(TOPDIR)/scripts/pathdown.sh)                     //  MOD_DESTDIR目标,取主目录TOPDIR到当前目录的路径。endifunexport MOD_DIRSMOD_DIRS := $(MOD_SUB_DIRS) $(MOD_IN_SUB_DIRS)                                             // MOD_DIRS, 包括MOD_SUB_DIRS、MOD_IN_SUB_DIRSifneq "$(strip $(MOD_DIRS))" "".PHONY: $(patsubst %,_modsubdir_%,$(MOD_DIRS))                                               // _modsubdir_$(MOD_DIRS)目标,进入子目录,继续递归make modules目标$(patsubst %,_modsubdir_%,$(MOD_DIRS)) : dummy    $(MAKE) -C $(patsubst _modsubdir_%,%,$@) modules.PHONY: $(patsubst %,_modinst_%,$(MOD_DIRS))                                               // _modinst_$(MOD_DIRS)目标,进入子目录,继续递归make modules_install目标$(patsubst %,_modinst_%,$(MOD_DIRS)) : dummy    $(MAKE) -C $(patsubst _modinst_%,%,$@) modules_installendif.PHONY: modulesmodules: $(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_installmodules_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_i386OBJCOPY=$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -SLDFLAGS=-e stextLINKFLAGS =-T $(TOPDIR)/arch/i386/vmlinux.lds $(LDFLAGS)                                   // LINKFLAGS定义,调用link配置文件$(TOPDIR)/arch/i386/vmlinux.ldsHEAD := 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 = 0x1000BZIMAGE_OFFSET = 0x100000ZLINKFLAGS = -Ttext $(ZIMAGE_OFFSET) $(ZLDFLAGS)BZLINKFLAGS = -Ttext $(BZIMAGE_OFFSET) $(ZLDFLAGS)all: vmlinuxvmlinux: piggy.o $(OBJECTS)    $(LD) $(ZLINKFLAGS) -o vmlinux $(OBJECTS) piggy.obvmlinux: piggy.o $(OBJECTS)    $(LD) $(BZLINKFLAGS) -o bvmlinux $(OBJECTS) piggy.ohead.o: head.S    $(CC) $(AFLAGS) -traditional -c head.Scomma   := ,misc.o: misc.c    $(CC) $(CFLAGS) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) -c misc.cpiggy.o:    $(SYSTEM)    tmppiggy=_tmp_$$$$piggy; \    rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk; \$(OBJCOPY) $(SYSTEM) $$tmppiggy; \    gzip -f -9 < $$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)=dirbuild := -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)=$@                                                                // 这里启动vmlinux各组成部分的编译

2.6内核会构建内置的符号表,即kallsyms组件。其编译规则如下:

ifdef CONFIG_KALLSYMS_EXTRA_PASSlast_kallsyms := 3elselast_kallsyms := 2endifkallsyms.o := .tmp_kallsyms$(last_kallsyms).odefine 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 rulecmd_ksym_ld = $(cmd_vmlinux__)define rule_ksym_ld                                                                     // 定义命令包:rule_ksym_ld    :     +$(call cmd,vmlinux_version)    $(call cmd,vmlinux__)    $(Q)echo 'cmd_$@ := $(cmd_vmlinux__)' > $(@D)/.$(@F).cmdendef# Generate .S file with all kernel symbolsquiet_cmd_kallsyms = KSYM    $@      cmd_kallsyms = $(NM) -n $< | $(KALLSYMS) \                     $(if $(CONFIG_KALLSYMS_ALL),--all-symbols) > $@.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)=dirbuild := -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_$(1)),\                                                       echo '  $(call escsq,$($(quiet)cmd_$(1)))' &&) $(cmd_$(1)) # 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_$(1)), $(cmd_$@)) ),\             // if_changed_rule表达式            @set -e; \            $(rule_$(1)))

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-targetquiet_cmd_link_o_target = LD      $@# If the list of objects to link is empty, just create an empty built-in.ocmd_link_o_target = $(if $(strip $(obj-y)),\              $(LD) $(ld_flags) -r -o $@ $(filter $(obj-y), $^),\              rm -f $@; $(AR) rcs $@)$(builtin-target): $(obj-y) FORCE                                                            // builtin-target目标的生成方法,    $(call if_changed,link_o_target)                                                       // 调用表达式:if_changed -> cmd_link_o_targettargets += $(builtin-target)endif # builtin-target## Rule to compile a set of .o files into one .a file                                        // 规则:多个.o编译进一个.a#ifdef lib-targetquiet_cmd_link_l_target = AR      $@cmd_link_l_target = rm -f $@; $(AR) $(EXTRA_ARFLAGS) rcs $@ $(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字节的文件头,对其内容原封没动。

1

这和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入口。

4、参考资料