linux kernel编译生成zImage过程详解

2024-01-06 21:48

本文主要是介绍linux kernel编译生成zImage过程详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

可以看到,在顶层makefile的第278行,包含了scripts/Kbuild.include文件,在这里定义了大量的函数和变量,供顶层makefile和其他makefile文件使用。 
  
在顶层makefile文件的第412行,包含了arch/arm/Makefile。这个是体系结构相关makefile文件。它定义了体系结构相关的一些变量及规则。 
  
当执行”make”时,arch/arm/Makefile中的185行的规则将是make遇到的第一个规则: 
  
all:   $(KBUILD_IMAGE) 
  
KBUILD_IMAGE这个变量是arch/arm/Makefile的第182行定义。 
  
KBUILD_IMAGE := zImage 
  
然后看zImage的构建规则,在arch/arm/Makefile的第212行开始定义 
  
zImage Image xipImage bootpImage uImage: vmlinux 
       $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@ 
  
build变量在scripts/Kbuild.include文件中第114行定义: 
  
build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj 
  
boot变量在arch/arm/Makefile的187行定义: 
  
boot := arch/arm/boot 
  
MACHINE变量的值在arch/arm/Makefile的147行开始定义 
  
ifneq ($(machine-y),) 
MACHINE  := arch/arm/mach-$(machine-y)/ 
else 
MACHINE  := 
endif 
  
这里machine-y := s3c2410,所以变量MACHINE的值为 
  
MACHINE  := arch/arm/mach-s3c2410 
  
所以上面的规则可写为如下形势 
  
  
zImage: vmlinux 
       $(Q)$(MAKE) -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj= / 
       arch/arm/boot MACHINE= arch/arm/mach-s3c2410 arch/arm/boot/ zImage 
  
这个规则的依赖是vmlinux,下面先看看这个依赖目标的创建规则。 
  
vmlinux目标的规则在顶层Makefile的第738行定义。 
  
vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) $(kallsyms.o) FORCE 
ifdef CONFIG_HEADERS_CHECK 
       $(Q)$(MAKE) -f $(srctree)/Makefile headers_check 
endif 
       $(call if_changed_rule,vmlinux__) 
       $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost $@ 
       $(Q)rm -f .old_version 
  
这里涉及到几个变量,先看看这几个变量的定义,前三个变量分别在605、602、603行定义。 
  
vmlinux-init := $(head-y) $(init-y) 
vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y) 
vmlinux-lds  := arch/$(ARCH)/kernel/vmlinux.lds 
  
其中head-y在arch/arm/Makefile中第89行定义, 
  
head-y            := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o 
  
init-y在顶层makefile的433行定义 
  
init-y              := init/ 
  
后又在第567行进行处理 
  
init-y              := $(patsubst %/, %/built-in.o, $(init-y)) 
  
所以变量init-y应为 
  
init-y              := init/built-in.o 
  
因此 
  
vmlinux-init := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o init/built-in.o 
  
同理,其他几个变量也可通过类似方法进行分析,这里不一一分析了。vmlinux-init这个变量的构建规则在748行定义: 
  
$(sort $(vmlinux-init) $(vmlinux-main)) $(vmlinux-lds): $(vmlinux-dirs) ; 
  
这里是一个空命令的规则。空命令行可以防止make在执行时试图为重建这个目标去查找隐含命令。其依赖为vmlinux-dirs,这个变量在顶层Makefile第558行定义: 
  
vmlinux-dirs   := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) / 
                   $(core-y) $(core-m) $(drivers-y) $(drivers-m) / 
                   $(net-y) $(net-m) $(libs-y) $(libs-m))) 
  
这个变量指定了一系列要进入的下层目录。他的规则在顶层Makefile第757行定义 
  
$(vmlinux-dirs): prepare scripts 
       $(Q)$(MAKE) $(build)=$@ 
  
这里的两个依赖就不分析了,主要看一下这个规则的命令,build和$@变量展开后如下 
  
       $(Q)$(MAKE) -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build  / 
        obj =$(vmlinux-dirs) 
  
这里会再一次进入scripts/Makefile.build执行83行规则 
  
__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) / 
        $(if $(KBUILD_MODULES),$(obj-m)) / 
        $(subdir-ym) $(always) 
       @: 
  
因为KBUILD_BUILTIN在顶层Makefile中被初始化为1,所以这个规则的依赖有一个builtin-target变量。这个变量在scripts/Makefile.build的78行定义 
  
ifneq ($(strip $(obj-y) $(obj-m) $(obj-n) $(obj-) $(lib-target)),) 
builtin-target := $(obj)/built-in.o 
endif 
  
变量obj就是vmlinux-dirs变量指定的目录。所以这里会构建$(vmlinux-dirs)/built-in.o目标,在scripts/Makefile.build文件的261行开始,有这个目标的规则及命令的定义 
  
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 
       $(call if_changed,link_o_target), 
  
scripts/Makefile.build在第16行开始包含了vmlinux-dirs变量指定目录中的Makefile文件,在这些makefile文件中会指定obj-y变量,它指定的都是一些*.o目标文件, 
  
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src)) 
include $(if $(wildcard $(kbuild-dir)/Kbuild), $(kbuild-dir)/Kbuild, $(kbuild-dir)/Makefile) 
  
这些*.o文件的生成方法由scripts/Makefile.build文件202行的模式规则指定 
  
%.o: %.c FORCE 
       $(call cmd,force_checksrc) 
       $(call if_changed_rule,cc_o_c) 
  
通过上面这一系列的步骤,就编译链接出由变量vmlinux-init指定的目标,vmlinux-main变量指定的目标的构建与此类似。再看看vmlinux的构建规则 
  
vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) $(kallsyms.o) FORCE 
ifdef CONFIG_HEADERS_CHECK 
       $(Q)$(MAKE) -f $(srctree)/Makefile headers_check 
endif 
       $(call if_changed_rule,vmlinux__) 
       $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost $@ 
       $(Q)rm -f .old_version 
  
现在vmlinux的依赖都处理好了,开始执行这个规则的命令,命令 
  
$(Q)$(MAKE) -f $(srctree)/Makefile headers_check 
  
是进行头文件的相关检测,这里不作详细分析。看第二条命令 
  
       $(call if_changed_rule,vmlinux__) 
  
这里通过函数调用,执行rule_vmlinux__,在顶层Makefile第636行开始定义 
  
define rule_vmlinux__ 
       : 
       $(if $(CONFIG_KALLSYMS),,+$(call cmd,vmlinux_version)) 
  
       $(call cmd,vmlinux__) 
       $(Q)echo 'cmd_$@ := $(cmd_vmlinux__)' > $(@D)/.$(@F).cmd 
  
       $(Q)$(if $($(quiet)cmd_sysmap),                                      / 
         echo '  $($(quiet)cmd_sysmap)  System.map' &&)                     / 
       $(cmd_sysmap) $@ System.map;                                         / 
       if [ $$? -ne 0 ]; then                                               / 
              rm -f $@;                                                    / 
              /bin/false;                                                  / 
       fi; 
       $(verify_kallsyms) 
endef 
  
这里主要还是调用cmd_vmlinux__,定义在顶层Makefile的610行 
  
      cmd_vmlinux__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) -o $@ / 
      -T $(vmlinux-lds) $(vmlinux-init)                          / 
      --start-group $(vmlinux-main) --end-group                  / 
      $(filter-out $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) FORCE ,$^) 
  
通过这个命令将变量vmlinux-init和vmlinux-main指定的目标链接成vmlinux文件。链接脚本由vmlinux-lds指定。在顶层 Makefile 605行定义: 
  
vmlinux-lds  := arch/$(ARCH)/kernel/vmlinux.lds 
  
现在再看一下zImage的构建规则 
  
zImage: vmlinux 
       $(Q)$(MAKE) -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj= / 
       arch/arm/boot MACHINE= arch/arm/mach-s3c2410 arch/arm/boot/ zImage 
  
其依赖vmlinux已经构建完成,它的命令同样是执行scripts/Makefile.build文件,它的开头包含了arch/arm/boot/Makefile文件,在这个文件的第56行开始就是arch/arm/boot/zImage的构建规则: 
  
$(obj)/zImage: $(obj)/compressed/vmlinux FORCE 
       $(call if_changed,objcopy) 
       @echo '  Kernel: $@ is ready' 
  
变量obj的值就是arch/arm/boot,前面已经分析过。其依赖  $(obj)/compressed/vmlinux的构建规则在arch/arm/boot/Makefile的53行开始定义的 
  
$(obj)/compressed/vmlinux: $(obj)/Image FORCE 
       $(Q)$(MAKE) $(build)=$(obj)/compressed $@ 
  
这个规则的依赖$(obj)/Image的构建规则在arch/arm/boot/Makefile的49行开始定义: 
  
$(obj)/Image: vmlinux FORCE 
       $(call if_changed, objcopy) 
       @echo '  Kernel: $@ is ready' 
  
在这个规则中,将前面创建的vmlinux文件通过二进制工具objcopy进行处理,在scripts/Makefile.build的第19行包含了scripts/Makefile.lib 
  
include scripts/Makefile.lib 
  
在这个makefile文件中,有cmd_objcopy的定义,在156行开始定义 
  
quiet_cmd_objcopy = OBJCOPY $@ 
cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@ 
  
变量OBJCOPY在顶层Makefile中289行定义: 
  
OBJCOPY             = $(CROSS_COMPILE)objcopy 
  
OBJCOPYFLAGS变量在arch/arm/Makefile中第15行定义 
  
OBJCOPYFLAGS  :=-O binary -R .note -R .comment –S 
  
所以命令cmd_objcopy可扩展为 
  
cmd_objcopy = $(CROSS_COMPILE)objcopy -O binary -R .note -R .comment –S $< $@ 
  
这就是处理vmlinux的命令。然后看看规则 
  
$(obj)/compressed/vmlinux: $(obj)/Image FORCE 
       $(Q)$(MAKE) $(build)=$(obj)/compressed $@ 
  
的命令行,变量扩展后为: 
  
$(Q)$(MAKE) -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj= / 
             $(obj)/compressed $(obj)/compressed/vmlinux 
  
于是在scripts/Makefile的开头会包含arch/arm/boot/compressed/Makefile文件,并执行其中的$(obj) /vmlinux目标所在的规则,在这个Makefile文件的第98行开始定义: 
  
$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.o / 
             $(addprefix $(obj)/, $(OBJS)) FORCE 
       $(call if_changed,ld) 
       @: 
  
这里先看$(obj)/piggy.o,在arch/arm/boot/compreseed/Makefile的103行开始 
  
$(obj)/piggy.gz: $(obj)/../Image FORCE 
       $(call if_changed,gzip) 
  
$(obj)/piggy.o:  $(obj)/piggy.gz FORCE 
  
这两个规则的第一个就是把由vmlinux生成的Image进行压缩生成piggy.gz,然后生成piggy.o 
  
cmd_ld命令在scripts/Makefile.lib文件149行定义: 
  
quiet_cmd_ld = LD      $@ 
cmd_ld = $(LD) $(LDFLAGS) $(EXTRA_LDFLAGS) $(LDFLAGS_$(@F)) / 
              $(filter-out FORCE,$^) -o $@ 
  
这里根据链接脚本arch/arm/boot/compressed/vmlinux.lds链接生成了arch/arm/boot/compressed/vmlinux文件。然后在arch/arm/boot/Makefile的第56行的规则中 
  
$(obj)/zImage: $(obj)/compressed/vmlinux FORCE 
       $(call if_changed,objcopy) 
       @echo '  Kernel: $@ is ready' 
  
经过objcopy处理后便生成的最终的zImage 。 
  
  
  
下面看一下顶层Makefile生成的vmlinux以及arch/arm/boot/compressed/makefile生成的vmlinux的起始地址。 
  
通过顶层Makefile中的规则生成vmlinux是根据arch/arm/kernel/vmlinux.lds这个脚本链接生成的。arch/arm/kernel/vmlinux.lds是由arch/arm/kernel/vmlinux.lds.S生成的,其生成规则在scripts/Makefile.build的第246行开始定义 
  
quiet_cmd_cpp_lds_S = LDS     $@ 
      cmd_cpp_lds_S = $(CPP) $(cpp_flags) -D__ASSEMBLY__ -o $@ $< 
  
%.lds: %.lds.S FORCE 
       $(call if_changed_dep,cpp_lds_S) 
  
在arch/arm/kernel/vmlinux.lds.S的开始处有 
  
#ifdef CONFIG_XIP_KERNEL 
       . = XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR); 
#else 
       . = PAGE_OFFSET + TEXT_OFFSET; 
#endif 
  
我们这里的起始地址就是PAGE_OFFSET + TEXT_OFFSET。 
  
在include/asm-arm/memory.h的49行开始有 
  
#ifndef PAGE_OFFSET 
#define PAGE_OFFSET        UL(0xc0000000) 
#endif 
  
而arch/arm/kernel/vmlinux.lds.S的开头有 
  
#include <asm/memory.h> 
  
asm是一个符号,链接到asm-arm上的 
  
在arch/arm/Makefile第140行,有 
  
TEXT_OFFSET := $(textofs-y) 
  
第90行有 
  
textofs-y := 0x00008000 
  
所以TEXT_OFFSET := 0x00008000 
  
在153行有export TEXT_OFFSET将此变量输出。这样arch/arm/kernel/vmlinux.lds.S也就获得了PAGE_OFFSET + TEXT_OFFSET的值。 
  
  
现在看看arch/arm/boot/compressed/makeflie生成的vmlinux。它是根据arch/arm/boot/compressed/vmlinux.lds链接脚本生成的。这个脚本由arch/arm/boot/compressed/vmlinux.lds.in生成,在这个文件的开始处有 
  
  . = TEXT_START; 
  
现在看arch/arm/boot/compressed/Makefile,在110行有 
  
$(obj)/vmlinux.lds: $(obj)/vmlinux.lds.in arch/arm/boot/Makefile .config 
       @sed "$(SEDFLAGS)" < $ $@ 
  
这就是由vmlinux.lds.in生成vmlinux.lds的规则,在它的命令中有个变量SEDFLAGS,在74行定义 
  
SEDFLAGS    = s/TEXT_START/$(ZTEXTADDR)/;s/BSS_START/$(ZBSSADDR)/ 
  
这里就把TEXT_START换成了ZTEXTADDR。再往上看从66行起 
  
ifeq ($(CONFIG_ZBOOT_ROM),y) 
ZTEXTADDR := $(CONFIG_ZBOOT_ROM_TEXT) 
ZBSSADDR    := $(CONFIG_ZBOOT_ROM_BSS) 
else 
ZTEXTADDR := 0 
ZBSSADDR    := ALIGN(4) 
endif 
  
如果zImage是从ram中启动ZTEXTADDR      := 0,否则从rom或flash启动时ZTEXTADDR := $(CONFIG_ZBOOT_ROM_TEXT),这里要在配置时设定CONFIG_ZBOOT_ROM_TEXT的值。 
  
到这里,关于zImage的生成过程算是可以结束了。

这篇关于linux kernel编译生成zImage过程详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/577726

相关文章

MySQL数据目录迁移的完整过程

《MySQL数据目录迁移的完整过程》文章详细介绍了将MySQL数据目录迁移到新硬盘的整个过程,包括新硬盘挂载、创建新的数据目录、迁移数据(推荐使用两遍rsync方案)、修改MySQL配置文件和重启验证... 目录1,新硬盘挂载(如果有的话)2,创建新的 mysql 数据目录3,迁移 MySQL 数据(推荐两

HTML5的input标签的`type`属性值详解和代码示例

《HTML5的input标签的`type`属性值详解和代码示例》HTML5的`input`标签提供了多种`type`属性值,用于创建不同类型的输入控件,满足用户输入的多样化需求,从文本输入、密码输入、... 目录一、引言二、文本类输入类型2.1 text2.2 password2.3 textarea(严格

MyBatis-Plus逻辑删除实现过程

《MyBatis-Plus逻辑删除实现过程》本文介绍了MyBatis-Plus如何实现逻辑删除功能,包括自动填充字段、配置与实现步骤、常见应用场景,并展示了如何使用remove方法进行逻辑删除,逻辑删... 目录1. 逻辑删除的必要性编程1.1 逻辑删除的定义1.2 逻辑删php除的优点1.3 适用场景2.

Linux内核定时器使用及说明

《Linux内核定时器使用及说明》文章详细介绍了Linux内核定时器的特性、核心数据结构、时间相关转换函数以及操作API,通过示例展示了如何编写和使用定时器,包括按键消抖的应用... 目录1.linux内核定时器特征2.Linux内核定时器核心数据结构3.Linux内核时间相关转换函数4.Linux内核定时

C++ move 的作用详解及陷阱最佳实践

《C++move的作用详解及陷阱最佳实践》文章详细介绍了C++中的`std::move`函数的作用,包括为什么需要它、它的本质、典型使用场景、以及一些常见陷阱和最佳实践,感兴趣的朋友跟随小编一起看... 目录C++ move 的作用详解一、一句话总结二、为什么需要 move?C++98/03 的痛点⚡C++

MySQL中between and的基本用法、范围查询示例详解

《MySQL中betweenand的基本用法、范围查询示例详解》BETWEENAND操作符在MySQL中用于选择在两个值之间的数据,包括边界值,它支持数值和日期类型,示例展示了如何使用BETWEEN... 目录一、between and语法二、使用示例2.1、betwphpeen and数值查询2.2、be

Linux镜像文件制作方式

《Linux镜像文件制作方式》本文介绍了Linux镜像文件制作的过程,包括确定磁盘空间布局、制作空白镜像文件、分区与格式化、复制引导分区和其他分区... 目录1.确定磁盘空间布局2.制作空白镜像文件3.分区与格式化1) 分区2) 格式化4.复制引导分区5.复制其它分区1) 挂载2) 复制bootfs分区3)

python中的flask_sqlalchemy的使用及示例详解

《python中的flask_sqlalchemy的使用及示例详解》文章主要介绍了在使用SQLAlchemy创建模型实例时,通过元类动态创建实例的方式,并说明了如何在实例化时执行__init__方法,... 目录@orm.reconstructorSQLAlchemy的回滚关联其他模型数据库基本操作将数据添

Java中ArrayList与顺序表示例详解

《Java中ArrayList与顺序表示例详解》顺序表是在计算机内存中以数组的形式保存的线性表,是指用一组地址连续的存储单元依次存储数据元素的线性结构,:本文主要介绍Java中ArrayList与... 目录前言一、Java集合框架核心接口与分类ArrayList二、顺序表数据结构中的顺序表三、常用代码手动

JAVA线程的周期及调度机制详解

《JAVA线程的周期及调度机制详解》Java线程的生命周期包括NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING和TERMINATED,线程调度依赖操作系统,采用抢占... 目录Java线程的生命周期线程状态转换示例代码JAVA线程调度机制优先级设置示例注意事项JAVA线程