2.6 Linux内核makefile解析

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:(编译模块的时候自动拷贝头文件进来??)
    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 $@
    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 $@).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 $@
    $(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 $@).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_%,%,$@)
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_%,%,$@) modules

.PHONY: $(patsubst %,_modinst_%,$(MOD_DIRS))                                               // _modinst_$(MOD_DIRS)目标,进入子目录,继续递归make modules_install目标
$(patsubst %,_modinst_%,$(MOD_DIRS)) : dummy
    $(MAKE) -C $(patsubst _modinst_%,%,$@) 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 = 0x1000
BZIMAGE_OFFSET = 0x100000

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 -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)=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)=$@                                                                // 这里启动vmlinux各组成部分的编译

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

ifdef CONFIG_KALLSYMS_EXTRA_PASS
last_kallsyms := 3
else
last_kallsyms := 2
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 'cmd_$@ := $(cmd_vmlinux__)' > $(@D)/.$(@F).cmd
endef

# Generate .S file with all kernel symbols
quiet_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)=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_$(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-target
quiet_cmd_link_o_target = LD      $@
# 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 $@ $(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_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      $@
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、参考资料

posted @ 2017-10-12 21:01  pwl999  阅读(233)  评论(0)    收藏  举报