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

  • elf section 是否 mis-match 的检查;
  • 生成内核导出符号文件 Module.symvers(Symbol version dump文件);

(1)其中第一个动作,由于明白它需要很多elf文件格式的知识,而本文重在解释内核构建系统的原理与实现,所以要在这里详细讲解就显得不妥,等以后有机会了再写文章来讨论。有兴趣的同学,可以先行查看 scripts/mod/modpost.c 中的代码
(2)而第二个动作中提到的 Modules.symvers 文件,其中包含内核及内部模块所导出的各种符号及其相关CRC校验值。构建系统会在编译 vmlinux.o 的过程中将原始内核文件(vmlinux)所导出的符号记录在这个文件中,在后面讨论内部模块的第二步处理的时候,我们会发现它也会将各内部模块所导出的符号记录在这个文件中。

所谓符号,你可以先将其理解成是基本内核或者内部模块导出来的,供其他人(通常是驱动程序)使用的函数或者变量(不完全准确)。等你以后开发驱动程序进行调试的时候,你会发现有这么一个dump文件会有多么方便。构建系统在编译外部模块的时候也要使用这个文件。

为了不脱离本文主题,我在下面讨论vmlinu.o编译的过程中,特意将上面两个动作中和内核构建无关的部分略去,以后我们会专门写另外的文章来讨论。

vmlinux.o

#  顶层目录的Makefile中

modpost-init := $(filter-out init/built-in.o, $(vmlinux-init))
vmlinux.o: $(modpost-init) $(vmlinux-main) FORCE
$(call if_changed_rule,vmlinux-modpost)

filter-out是一个反过滤函数,主要是过滤掉$(vmlinux-init)中的init/built-in.o,此时:modpost-init :=arch/arm/kernel/head.o arch/arm/kernel/init_task.o init/built-in.o,至于为什么依赖要去掉这个文件,暂时还未找到原因,日后找到后再来批注

vmlinux.o目标的的生成规则是调用 if_changed_rule函数,入参为vmlinux-modpost。(如果不懂if_changed_rule函数的使用,请移步到内核源码中单个.o文件的编译过程(六)中,里面有一小节有讲到这个函数)这里构建系统要调用 rule_vmlinux-modpost 变量所定义的命令,定义如下:

# 在scripts/Kbuild.include中
# printing commands
cmd = @$(echo-cmd) $(cmd_$(1))
...
# Name of target with a '.' as filename prefix. foo/bar.o => foo/.bar.o
dot-target = $(dir $@).$(notdir $@) -------------------------------------------------------------------------------------------------------
# 顶层目录的Makefile中
# Do modpost on a prelinked vmlinux. The finally linked vmlinux has
# relevant sections renamed as per the linker script.
quiet_cmd_vmlinux-modpost = LD $@
cmd_vmlinux-modpost = $(LD) $(LDFLAGS) -r -o $@ \
$(vmlinux-init) --start-group $(vmlinux-main) --end-group \
$(filter-out $(vmlinux-init) $(vmlinux-main) FORCE ,$^)
define rule_vmlinux-modpost
:
+$(call cmd,vmlinux-modpost)
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost $@
$(Q)echo 'cmd_$@ := $(cmd_vmlinux-modpost)' > $(dot-target).cmd
endef

变量 rule_vmlinux-modpost 中定义的命令中,具体流程如下:
(1)首先调用 cmd_vmlinux-modpost 变量所定义的命令来链接生成 vmlinux.o 目标。
(2)然后构建系统就用 scripts/Makefile.modpost来调用 make。注意看第二条make命令中的目标为 $@,也就是说要make vmlinux.o。
(3)最后,它将构建 vmlinux.o 目标的命令保存到 .vmlinux.o.cmd 文件中。

1. $(call cmd,vmlinux-modpost)
调用cmd_vmlinux-modpost。链接生成vmlinux.o。实际执行的命令如下:

arm-linux-ld -EL  -r -o vmlinux.o 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

2. $ (Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost $@

展开后为:

make -f scripts/Makefile.modpost vmlinux.o

vmlinux.o的构建规则定义如下

# scripts/Makefile.modpost
quiet_cmd_kernel-mod = MODPOST $@
cmd_kernel-mod = $(modpost) $@ vmlinux.o: FORCE
$(call cmd,kernel-mod)

调用cmd函数,入参为kernel-mod(cmd函数定义见上面的代码)。实际上执行的是@ $(echo-cmd) $(cmd_ $(1))这么一句命令。它的作用同样就是打印执行的命令,然后执行这个命令—cmd_kernel-mod。实际执行的是 $(modpost) $@;其中 $@ =vmlinux.o, $(modpost)定义如下:

# scripts/Makefile.modpost
kernelsymfile := $(objtree)/Module.symvers
...
ifneq ($(KBUILD_BUILDHOST),$(ARCH))
cross_build := 1
endif
...
modpost = scripts/mod/modpost \
$(if $(CONFIG_MODVERSIONS),-m) \
$(if $(CONFIG_MODULE_SRCVERSION_ALL),-a,) \
$(if $(KBUILD_EXTMOD),-i,-o) $(kernelsymfile) \
$(if $(KBUILD_EXTMOD),-I $(modulesymfile)) \
$(if $(KBUILD_EXTRA_SYMBOLS), $(patsubst %, -e %,$(KBUILD_EXTRA_SYMBOLS))) \
$(if $(KBUILD_EXTMOD),-o $(modulesymfile)) \
$(if $(CONFIG_DEBUG_SECTION_MISMATCH),,-S) \
$(if $(KBUILD_EXTMOD)$(KBUILD_MODPOST_WARN),-w) \
$(if $(cross_build),-c)

其中 $ (objtree)为空。且可以看出,其中包含有很多条件的判断。由于编译 vmlinux.o 时, CONFIG_MODVERSIONS,CONFIG_MODULE_SRCVERSION_ALL,KBUILD_EXTMOD,KBUILD_EXTRA_SYMBOLS,CONFIG_MARKERS,CONFIG_DEBUG_SECTION_MISMATCH 等等都没定义,并且我们做的是交叉编译,即 cross_build = 1。所以在 scripts/Makefile.modpost 中,处理 vmlinux.o 的命令就剩下以下三行有用的部分:

modpost = scripts/mod/modpost                    \
$(if $(KBUILD_EXTMOD),-i,-o) $(kernelsymfile) \
$(if $(CONFIG_DEBUG_SECTION_MISMATCH),,-S) \
$(if $(cross_build),-c) # 最终展开形式:
modpost = scripts/mod/modpost -o Module.symvers -S -c

因此$ (Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost $@实际执行的是以下命令

 scripts/mod/modpost   -o /home/hh/linux-2.6.38/Module.symvers    -S  -c vmlinux.o

即它最终会调用modpost工具(该工具是由scripts/mod/modpost.c编译出来的),作用是利用modpost工具 来解析 vmlinux.o 对象文件,并将基本内核导出的所有符号都记录到文件 Module.symvers 中去。当然,这个命令附带完成的,还有前面所说到的检查 sections 是否 mis match 的工作。你可以先浏览看看 scripts/mod/modpost.c 中的代码。打开文件 Module.symvers,你会发现针对每个内核导出的符号(symbol),都会有一行,其格式和举例如下:

0x00000000	usb_serial_generic_submit_read_urb	vmlinux	EXPORT_SYMBOL_GPL
0x00000000 unregister_vt_notifier vmlinux EXPORT_SYMBOL_GPL
0x00000000 generic_file_splice_write vmlinux EXPORT_SYMBOL
0x00000000 set_anon_super vmlinux EXPORT_SYMBOL
0x00000000 kmem_cache_alloc vmlinux EXPORT_SYMBOL
0x00000000 __cond_resched_softirq vmlinux EXPORT_SYMBOL
0x00000000 i2c_put_adapter vmlinux EXPORT_SYMBOL
0x00000000 rtc_class_open vmlinux EXPORT_SYMBOL_GPL
0x00000000 scsi_sense_key_string vmlinux EXPORT_SYMBOL
...

注意,此时打开该文件后看到的符号CRC值都是0x00000000,那是因为我在配置的时并没有设置 CONFIG_MODVERSIONS。一旦设置过这个配置选项,就意味着打开了内核的 Module versioning功能。Module versioning 功能应用在我们使用模块的场合。如果Module versioning功能被打开的话,它会以每个导出符号的C原型声明作为输入,计算出对应的CRC校验值,保存在文件 Module.symvers 中。如此一来,内核在后面要加载使用模块的时候,会两相比较模块中的CRC值和保存下来的CRC值,如果发现不相等,内核就拒绝加载这个模块。

3. $ (Q)echo ‘cmd_$@ := $(cmd_vmlinux-modpost)’ > $(dot-target).cmd
$(dot-target)定义在scripts/Kbuild.include中,主要是定义一个文件名,.前面是目标的目录,后面是目标去掉目录之后的名字。实际执行时的打印如下:

echo 'cmd_vmlinux.o := arm-linux-ld -EL  -r -o vmlinux.o
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 ' > ./.vmlinux.o.cmd

补充:
保存*.cmd 有什么用?
将编译 vmlinux.o 的命令写入到.vmlinux.o.cmd 文件中保存起来,以便下次再编译内核时可以进行新旧命令的比较。以便下次再编译内核时可以进行新旧命令的比较。

linux内核vmlinux的编译过程之 --- vmlinux.o详解(八)的更多相关文章

  1. Linux内核驱动学习(十)Input子系统详解

    文章目录 前言 框架 如何实现`input device` 设备驱动? 头文件 注册input_dev设备 上报按键值 dev->open()和dev->close() 其他事件类型,处理 ...

  2. Linux 内核配置和编译

    Linux 内核配置和编译 一.配置内核 (1). 为什么要配置内核 1. 硬件需求 2. 软件需求 选出需要的,去掉不要的 (2). 如何配置内核 1. make  config 基于文本模式的交互 ...

  3. Linux下nginx编译安装教程和编译参数详解

    这篇文章主要介绍了Linux下nginx编译安装教程和编译参数详解,需要的朋友可以参考下 一.必要软件准备1.安装pcre 为了支持rewrite功能,我们需要安装pcre 复制代码代码如下: # y ...

  4. I/O模型之二:Linux IO模式及 select、poll、epoll详解

    目录: <I/O模型之一:Unix的五种I/O模型> <I/O模型之二:Linux IO模式及 select.poll.epoll详解> <I/O模型之三:两种高性能 I ...

  5. (转)Linux下select, poll和epoll IO模型的详解

    Linux下select, poll和epoll IO模型的详解 原文:http://blog.csdn.net/tianmohust/article/details/6677985 一).Epoll ...

  6. shell编程之awk命令详解

    shell编程之awk命令详解 a:focus { outline: thin dotted #333; outline: 5px auto -webkit-focus-ring-color; out ...

  7. 技巧:Linux 动态库与静态库制作及使用详解

    技巧:Linux 动态库与静态库制作及使用详解 标准库的三种连接方式及静态库制作与使用方法 Linux 应用开发通常要考虑三个问题,即:1)在 Linux 应用程序开发过程中遇到过标准库链接在不同 L ...

  8. Linux NFS服务器的安装与配置方法(图文详解)

    这篇文章主要介绍了Linux NFS服务器的安装与配置方法(图文详解),需要的朋友可以参考下(http://xb.xcjl0834.com) 一.NFS服务简介 NFS 是Network File S ...

  9. Java中String的intern方法,javap&cfr.jar反编译,javap反编译后二进制指令代码详解,Java8常量池的位置

    一个例子 public class TestString{ public static void main(String[] args){ String a = "a"; Stri ...

  10. Linux Shell脚本入门--wget 命令用法详解

    Linux Shell脚本入门--wget 命令用法详解 wget是在Linux下开发的开放源代码的软件,作者是Hrvoje Niksic,后来被移植到包括Windows在内的各个平台上.它有以下功能 ...

随机推荐

  1. CSS—相对单位rem

    一.概述 rem是一个相对长度单位,它的单位长度取决于根标签html的字体尺寸.rem即root em的意思,中文翻译为根em.浏览器的文本尺寸一般默认为16px,即默认情况下: 1rem = 16p ...

  2. C# 反射 操作列表类型属性

    本文介绍对列表进行创建及赋值的反射操作 我们现在有TestA.TestB类,TestA中有TestB类型列表的属性List,如下: 1 public class TestA 2 { 3 public ...

  3. lombok版本报错问题java.lang.IllegalAccessError: class lombok.javac.apt.LombokProcessor (in unnamed module

    lombok版本报错问题 记录一个项目部署时遇到的问题,我本地采用的JDK8的版本,然后我的服务器采用的是JDK17,然后在用maven进行打包的时候,发现package失败. 复现 我在本地采用的l ...

  4. Spring Boot入门项目之外卖

    文章目录 呱呱外卖 前言 项目介绍 项目技术栈介绍(主讲后端) 项目功能介绍 项目的代码结构 实体类 控制类(Controller) Service类和Mapper类 缓存 接口文档管理 项目添加的部 ...

  5. 2020-10-31:java中LinkedTransferQueue和SynchronousQueue有什么区别?

    福哥答案2020-11-01:SynchronousQueue:线程A使用put将数据添加到队列,如果没有其他线程使用take去获取数据,那么线程A阻塞,直到数据被其他线程获取,同理 如果线程B从队列 ...

  6. 2020-01-20:mysql中,一张表里有3亿数据,未分表,要求是在这个大表里添加一列数据。数据库不能停,并且还有增删改操作。请问如何操作?

    2020-01-20:mysql中,一张表里有3亿数据,未分表,要求是在这个大表里添加一列数据.数据库不能停,并且还有增删改操作.请问如何操作?福哥答案2020-01-20: 陌陌答案:用pt_onl ...

  7. ET框架6.0分析二、异步编程

    概述 ET框架很多地方都用到了异步,例如资源加载.AI.Actor模型等等.ET框架对C#的异步操作进行了一定程度的封装和改造,有一些特点: 显式的或者说强调了使用C#异步实现协程机制(其实C#的异步 ...

  8. 工欲善其事必先利其器--CMake牛刀小试

    这里假设用户已经安装好MinGW编译套件!并配置好环境变量!具体怎么下载和配置网上教程非常多,这里贴上一个链接:不仅教你安装MinGW还教你安装VScode配置 1.学习c plus plus编码为什 ...

  9. 数据库SQL复习

    数据库SQL介绍 Def:SQL是一种极其高效的数据库系统语言:可以实现对数据库中的数据进行增删改查等操作 增加操作:使用create命令: 可以create table 可以create View ...

  10. ODDO之三 :Odoo 13 开发之创建第一个 Odoo 应用

    Odoo 开发通常都需要创建自己的插件模块.本文中我们将通过创建第一个应用来一步步学习如何在 Odoo 中开启和安装这个插件.我们将从基础的开发流学起,即创建和安装新插件,然后在开发迭代中更新代码来进 ...