一. vmlinux目标及其构建规则
定义在顶层Makefile中,如下:

# The all: target is the default when no target is given on the
# command line.
# This allow a user to issue only 'make' to build a kernel including modules
# Defaults to vmlinux, but the arch makefile usually adds further targets
all: vmlinux...# vmlinux image - including updated kernel symbols
vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o $(kallsyms.o) FORCE
ifdef CONFIG_HEADERS_CHECK$(Q)$(MAKE) -f $(srctree)/Makefile headers_check
endif
ifdef CONFIG_SAMPLES$(Q)$(MAKE) $(build)=samples
endif
ifdef CONFIG_BUILD_DOCSRC$(Q)$(MAKE) $(build)=Documentation
endif$(call vmlinux-modpost)$(call if_changed_rule,vmlinux__)$(Q)rm -f .old_version

当没有给定目标时,默认目标为all,all又被赋值为vmlinux。 CONFIG_HEADERS_CHECK、CONFIG_SAMPLES、CONFIG_BUILD_DOCSRC定义在.config文件中(一般情况下不定义),因此用的是最后一段构建规则。

vmlinux的构建放在最后面讲,这里先给出依赖的生成过程。

二. 依赖$(vmlinux-lds)

在顶层目录的Makefile中:

vmlinux-lds  := arch/$(SRCARCH)/kernel/vmlinux.lds

因为我使用的arm架构,$(SRCARCH) = arm,因此 vmlinux-lds :=arch/arm/kernel/vmlinux.lds。
内核编译链接过程是依靠vmlinux.lds文件,但是未经编译的内核源码在arch/arm/kernel/目录是不存在vmlinux.lds链接脚本的,仅有vmlinux.lds.S和与之对应的vmlinux.lds.h(一些相关的宏定义)文件;vmlinux.lds是vmlinux.lds.S经过条件编译并展开一些宏定义后生成的。
具体分析请移步:linux内核链接脚本vmlinux.lds分析(待更新

三. 依赖$(vmlinux-init)

# 顶层目录的Makefile中
vmlinux-init := $(head-y) $(init-y)

1. $(head-y)

#  arch/arm/Makefile
head-y      := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o

对于没有MMU的处理器,MMUEXT的值为-nommu;对于有MMU的处理器,MMUEXT的值为空。 这里我们使用后者。

2. $(init-y)

#  顶层目录的Makefile中
# Objects we will link into vmlinux / subdirs we need to visit
init-y      := init/
...
init-y      := $(patsubst %/, %/built-in.o, $(init-y))

这里$(init-y) 进行两次赋值(Makefile中 := 的使用请移步到 :Makefile 中:= ?= += =的区别)。后一次是把init目录加了一个built-in.o。 $(vmlinux-init)最终展开为 :

vmlinux-init := arch/arm/kernel/head.o arch/arm/kernel/init_task.o init/built-in.o

四. 依赖$(vmlinux-main)

由于在顶层Makefile中包含了架构体系目录下的Makefile文件(“include $ (srctree)/arch/$(SRCARCH)/Makefile”,以arm架构为例,就是包含了/arch/arm/Makefile 文件),所以在分析这些目标和依赖时,不要忘记这个Makefile中相关的定义。具体如下:

#  顶层目录的Makefile中include $(srctree)/arch/$(SRCARCH)/Makefile
...
# Objects we will link into vmlinux / subdirs we need to visit
drivers-y   := drivers/ sound/ firmware/
net-y       := net/
libs-y      := lib/
core-y      := usr/
...
core-y      += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
...
core-y      := $(patsubst %/, %/built-in.o, $(core-y))
drivers-y   := $(patsubst %/, %/built-in.o, $(drivers-y))
net-y       := $(patsubst %/, %/built-in.o, $(net-y))
libs-y1     := $(patsubst %/, %/lib.a, $(libs-y))
libs-y2     := $(patsubst %/, %/built-in.o, $(libs-y))
libs-y      := $(libs-y1) $(libs-y2)
...
vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)#   在arch/arm/Makefile中
core-$(CONFIG_FPE_NWFPE)    += arch/arm/nwfpe/
core-$(CONFIG_FPE_FASTFPE)  += $(FASTFPE_OBJ)
core-$(CONFIG_VFP)      += arch/arm/vfp/
# If we have a machine-specific directory, then include it in the build.
core-y              += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/
core-y              += $(machdirs) $(platdirs)   机器和平台相关
drivers-$(CONFIG_OPROFILE)      += arch/arm/oprofile/
libs-y              := arch/arm/lib/ $(libs-y)

其中CONFIG_FPE_NWFPE、CONFIG_VFP、CONFIG_OPROFILE定义在配置文件.config中,例如mini6410开发板的配置文件定义如下:

#
# At least one emulation must be selected
#
# CONFIG_FPE_NWFPE_XP is not set
# CONFIG_FPE_FASTFPE is not set
CONFIG_FPE_NWFPE=y
CONFIG_VFP=y

这下,内核根目录下所有相关的子目录就都包含了。除了lib目录下有两个文件,built-in.o 和 lib.a,其余下都只有一个文件built-in.o,xxx/built-in.o的生成规则请移步到linux内核Makefile中的变量build— 过渡篇(五)

$(vmlinux-main)最终展开为 :

vmlinux-main :=     \
#core-yusr/built-in.o   kernel/built-in.o   mm/built-in.o  fs/built-in.o  ipc/built-in.o  security/built-in.o  crypto/built-in.o block/built-in.o  \arch/arm/nwfpe/built-in.o   arch/arm/vfp/built-in.o   \arch/arm/kernel/built-in.o   arch/arm/mm/built-in.o  arch/arm/common/built-in.o  \  arch/arm/mach-s3c64xx/built-in.o  arch/arm/plat-samsung/built-in.o    \#libs-yarch/arm/lib/lib.a    lib/lib.a  arch/arm/lib/built-in.o   lib/built-in.o    \ #drivers-y
drivers/built-in.o  sound/built-in.o  firmware/built-in.o     \ #net-ynet/built-in.o

五. 依赖vmlinux.o
内核构建系统之所以要在链接 vmlinux 之前,去链接出vmlinux.o。其原因并不是要将 vmlinux.o 链接进 vmlinux,而是要在链接 vmlinux.o 的过程中做完两个动作: elf section 是否 mis-match 的检查;生成内核导出符号文件 Module.symvers(Symbol version dump文件);如果有兴趣,请移步到linux内核vmlinux的编译过程之 — vmlinux.o详解(八)

六. 依赖$(kallsyms.o)
$(kallsyms.o)里面存的主要是把内核用到的所有函数地址和名称。请移步到:内核vmlinux的编译过程之 — $(kallsyms.o)详解(待更新

七. vmlinux目标的构建
在文章的开头已给出目标的定义和构建规则,这里只列出重要的部分:

# vmlinux image - including updated kernel symbols
vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o $(kallsyms.o) FORCE$(call vmlinux-modpost)$(call if_changed_rule,vmlinux__)$(Q)rm -f .old_version

1. $(call vmlinux-modpost)
在我用的这个内核版本源码中,始终未找到变量 vmlinux-modpost 的定义,我觉得 $(call vmlinux-modpost) 是条多余的且没用的命令。在内核代码里,这样的多余现象也是常有的。

2. $(call if_changed_rule,vmlinux__)
命令 $(call if_changed_rule,vmlinux__) 会调用 rule_vmlinux__ 变量所定义的命令,在顶层 Makefile 中找到 rule_vmlinux__ 的定义:

#顶层Makefile
# Generate System.map
quiet_cmd_sysmap = SYSMAPcmd_sysmap = $(CONFIG_SHELL) $(srctree)/scripts/mksysmap# Link of vmlinux
# If CONFIG_KALLSYMS is set .version is already updated
# Generate System.map and verify that the content is consistent
# Use + in front of the vmlinux_version rule to silent warning with make -j2
# First command is ':' to allow us to use + in front of the rule
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

2.1 $ (if $ (CONFIG_KALLSYMS),+$(call cmd,vmlinux_version))
构建系统会检查是否有定义过 CONFIG_KALLSYMS,如果没有定义过,它就使用 cmd_vmlinux_version 来递增链接版本。一般我们默认配置CONFIG_KALLSYMS =y。

2.2 $(call cmd,vmlinux__)
调用 cmd_vmlinux__ 去把 vmlinux 链接出来。具体代码为:

# 顶层Makefile
# Rule to link vmlinux - also used during CONFIG_KALLSYMS
# May be overridden by arch/$(ARCH)/Makefile
quiet_cmd_vmlinux__ ?= LD      $@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) vmlinux.o FORCE ,$^)

实际执行命令如下:

arm-linux-ld -EL  -p --no-undefined -X --build-id -o vmlinux -T arch/arm/kernel/vmlinux.lds arch/arm/kernel/head.o arch/arm/kernel/init_task.o  init/built-in.o --start-group  usr/built-in.o  arch/arm/nwfpe/built-in.o  arch/arm/vfp/built-in.o  arch/arm/kernel/built-in.o  arch/arm/mm/built-in.o  arch/arm/common/built-in.o  arch/arm/mach-s3c64xx/built-in.o  arch/arm/plat-samsung/built-in.o  kernel/built-in.o  mm/built-in.o  fs/built-in.o  ipc/built-in.o  security/built-in.o  crypto/built-in.o  block/built-in.o  arch/arm/lib/lib.a  lib/lib.a  arch/arm/lib/built-in.o  lib/built-in.o  drivers/built-in.o  sound/built-in.o  firmware/built-in.o  net/built-in.o --end-group .tmp_kallsyms2.o

在这里,注意上面的链接将把 …/arch/arm/kernel/head.o 放在映像文件 vmlinux 的最前面,这是由链接器脚本所规定的,后续内核启动分栏文章中的分析会告诉你这个 head.o 正是整个 Linux 内核开始运行的地方。

2.3 $ (Q)echo ‘cmd_$@ := $(cmd_vmlinux__)’ > $(@D)/. $(@F).cmd
将编译 vmlinux的命令写入到 .vmlinux.cmd文件中保存起来,以便下次再编译内核时可以进行新旧命令的比较。
实际执行命令如下:

 echo 'cmd_vmlinux := /home/hh/opt/FriendlyARM/toolschain/4.5.1/bin/arm-linux-ld -EL  -p --no-undefined -X --build-id -o vmlinux -T arch/arm/kernel/vmlinux.lds arch/arm/kernel/head.o arch/arm/kernel/init_task.o  init/built-in.o --start-group  usr/built-in.o  arch/arm/nwfpe/built-in.o  arch/arm/vfp/built-in.o  arch/arm/kernel/built-in.o  arch/arm/mm/built-in.o  arch/arm/common/built-in.o  arch/arm/mach-s3c64xx/built-in.o  arch/arm/plat-samsung/built-in.o  kernel/built-in.o  mm/built-in.o  fs/built-in.o  ipc/built-in.o  security/built-in.o  crypto/built-in.o  block/built-in.o  arch/arm/lib/lib.a  lib/lib.a  arch/arm/lib/built-in.o  lib/built-in.o  drivers/built-in.o  sound/built-in.o  firmware/built-in.o  net/built-in.o --end-group .tmp_kallsyms2.o' > ./.vmlinux.cmd

2.4 $ (Q) $ (if $ ($ (quiet)cmd_sysmap), echo ’ $ ($(quiet)cmd_sysmap) System.map’ &&) $(cmd_sysmap) $@ System.map; if [ $ $? -ne 0 ]; then rm -f $@; /bin/false;fi;
判断quiet_cmd_sysmap或者cmd_sysmap变量是否有定义,如有定义,把接下来即将执行的命令打印出来,并执行。
实际打印如下:

echo '  /bin/bash /home/hh/linux-2.6.38/scripts/mksysmap  System.map' && /bin/bash /home/hh/linux-2.6.38/scripts/mksysmap vmlinux System.map; if [ $? -ne 0 ]; then rm -f vmlinux; /bin/false; fi;

$(cmd_sysmap) $@ System.map
变量cmd_sysmap定义在顶层Makefile中:

# 顶层Makefile
# SHELL used by kbuild
CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \else if [ -x /bin/bash ]; then echo /bin/bash; \else echo sh; fi ; fi)
...
# Generate System.map
quiet_cmd_sysmap = SYSMAPcmd_sysmap = $(CONFIG_SHELL) $(srctree)/scripts/mksysmap

使用 cmd_sysmap 所定义的命令来生成基本内核符号表文件 System.map。在其中包含有所有内核符号以及它们的地址,实际上前面用 kallsyms 包含在基本内核映像中的函数/变量地址及名称信息都等同于 System.map 中的内容,我们以后对内核代码的分析过程会经常引用这个文件。

/bin/bash /home/hh/linux-2.6.38/scripts/mksysmap vmlinux System.map

2.5 $(verify_kallsyms)
最后会额外做一步,用 verify_kallsyms 来确认前面的 kallsyms 是否工作正常。确认的方法是先拿前面的 .tmp_vmlinux2 重新生成一份新的map: .tmp_System.map。接着构建系统会比较 .tmp_System.map 和 System.map,如果不一致,那说明 kallsyms 子系统代码工作不正常,所以构建系统会建议你设置 CONFIG_KALLSYMS_EXTRA_PASS 来重新make vmlinux。verify_kallsyms 的定义为:

define 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

实际执行时的打印:

echo '  /bin/bash /home/hh/linux-2.6.38/scripts/mksysmap  .tmp_System.map' && /bin/bash /home/hh/linux-2.6.38/scripts/mksysmap .tmp_vmlinux2 .tmp_System.map/bin/bash /home/hh/linux-2.6.38/scripts/mksysmap  .tmp_System.map
cmp -s System.map .tmp_System.map || (echo Inconsistent kallsyms data; echo Try setting CONFIG_KALLSYMS_EXTRA_PASS; rm .tmp_kallsyms* ; /bin/false )

3. $(Q)rm -f .old_version

rm -f .old_version

总结
linux内核中编译单个.o的执行顺序:

--------------------------------------------------------------------------------------------------                 usr/built-in.o kernel/built-in.omm/built-in.ofs/built-in.oipc/built-in.osecurity/built-in.ocrypto/built-in.oblock/built-in.o #平台相关arch/arm/nwfpe/built-in.oarch/arm/vfp/built-in.oarch/arm/kernel/built-in.oarch/arm/mm/built-in.oarch/arm/common/built-in.o                            arch/arm/mach-s3c64xx/built-in.o                     arch/arm/plat-samsung/built-in.o                                             ||       sound/built-in.o       arch/arm/lib/lib.a
arch/arm/kernel/init_task.o                ||       firmware/built-in.o    arch/arm/lib/built-in.o
arch/arm/kernel/head.o  init/built-in.o    ||       drivers/built-in.o     lib/lib.a||                  ||            ||             ||               lib/built-in.o    net/built-in.o ||                  ||            ||             ||                   ||                 || $(head-y)          $(init-y)      $(core-y)    $(drivers-y)           $(libs-y)          $(net-y)    |                    |             |              |                    |                  ||                    |             |              |                    |                 /\                   /               \             \                    /              /\               /                    \            \                 /            /\           /                         \           \             /          /\       /                               \         \         /        /\   /                                     \       \     /      /$(vmlinux-init)      .tmp_kallsyms2.o           $(vmlinux-main)           $(vmlinux-lds)\                   |                        /                     /\                 |                     /                     /\               |                  /                     /\             |               /                     /\           |            /                     /\         |          /        链接         /——  —— —— —— —— ——    ——  —— —— —— ——  /      vmlinux----------------------------------------------------------------------------------------------------

linux内核vmlinux的编译过程(七)相关推荐

  1. xilinx linux内核,Xilinx-Zynq Linux内核源码编译过程

    本文内容依据http://www.wiki.xilinx.com网址编写,编译所用操作系统为ubuntu 14 1.交叉编译环境的安装配置 2.uboot的编译 1)下载uboot源代码 下载uboo ...

  2. linux内核vmlinux的编译过程之 --- vmlinux.o详解(八)

    内核构建系统之所以要在链接 vmlinux 之前,去链接出vmlinux.o.其原因并不是要将 vmlinux.o 链接进 vmlinux,而是要在链接 vmlinux.o 的过程中做完两个动作: e ...

  3. zynq linux内核出错,Xilinx Zynq Linux内核源码编译过程

    1.交叉编译环境的安装配置 1) +Xilinx+Tools 2.uboot的编译 1)下载uboot源代码 下载uboot源代码,务必要下载tar.gz格式的文件,地址: https://githu ...

  4. Linux内核裁剪及编译

    Linux内核裁剪及编译可加载模块 一 Linux基础知识 linux内核组要由五个子系统组成: 进程调度 内存管理 文件系统 网络接口 进程间通信 Linux源码目录 arch    目录中包含于体 ...

  5. 【华为云技术分享】Linux内核的分布式编译(1)

    上一期中我们介绍了Linux内核模块依赖图的绘制方法,这一期中我们将介绍Linux内核的分布式编译方法和分布式编译工具distcc的安装过程. 一.Linux内核的分布式编译 分布式编译是指将源程序通 ...

  6. 【linux内核-源码编译之centos7】

    linux内核-源码编译之centos7 一. 为什么要编译内核 二.疑难杂症 三.演示环境 四.下载源码 4.1.两者源码区别 4.2.将获取到的源码放在/usr/src/kernels/ 下 五. ...

  7. 编译linux源码报错,记录一次Linux内核源码编译实验

    记录一次Linux内核源码编译实验 文章目录 记录一次Linux内核源码编译实验 0. 实验环境 1. 选择.下载内核源码 2. 安装必要的依赖软件以及性能要求 3. 解压.配置和编译内核源码 3.1 ...

  8. Linux内核5.10编译 与调试

    Linux内核5.10编译 与调试 Linux 5.10 编译 下载内核 准备编译环境 配置模板 编译 安装新内核 qemu 调试 busybox 根文件系统制作 qemu 运行 脚本二 方法三 目的 ...

  9. Linux ffmpeg的安装编译过程

    Linux ffmpeg的安装编译过程 1.下载ffmpeg.     在网上搜索一下,或者到官方网站下载 2.解压    tar命令解压 3.配置 ./configure --enable-shar ...

最新文章

  1. CSS float浮动的深入研究、详解及拓展(二)
  2. 【Dijkstra】最短路径
  3. 网站自动登录功能的设计[转]
  4. 【计算机网络】计算机网络 OSI 参考模型 与 TCP/IP 参考模型 对比
  5. 基于依存句法分析的关键短语抽取算法实战
  6. dvwa如何打开_一篇文章让你搭建自己的Web安全测试平台(Dvwa)
  7. 关于编译错误 fatal error C1083: Cannot open precompiled header file
  8. 核心概念——节点/边/Combo——内置节点——Ellipse
  9. 自然数幂求和方法1:扰动法(求两次)
  10. 复制控制---复制构造函数
  11. Medoo 开源项目发布,超轻量级的PHP SQL数据库框架
  12. python 系统学习笔记(十二)---os os.path os.walk
  13. Servlet的入门
  14. Accept-Encoding
  15. CVPR2021 用更好的目标检测器提取视觉特征!微软提出VinVL,基于更好的视觉特征,达到更强的多模态性能...
  16. PrintService类打印
  17. jmeter如何看tps_jmeter性能测试疑难杂症解决思路
  18. Centos 7.5 1804安装绿联PL2303串口驱动
  19. Hello-Rust
  20. 2005年下半年网络工程师全省前20名

热门文章

  1. 驱动开发:探索DRIVER_OBJECT驱动对象
  2. LADA:Local Additivity Based Data Augmentation for Semi-supervised NER理解
  3. PHP扩展 zqf 兼容7.0
  4. 【深入理解HTTP协议】破冰篇
  5. 让计算机开口说话教案,大班安全详案教案及教学反思《会说话的安全标志》
  6. 基于深度学习的高精度塑料瓶检测识别系统(PyTorch+Pyside6+YOLOv5模型)
  7. MySQL 多表操作
  8. MySQL中一个B+树能存储多少数据
  9. 多目录Makefile编写
  10. 电商项目测试实战(一)