内核构建系统之所以要在链接 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. 【必知必会的MySQL知识】④DCL语言

    目录 一.概述 二 .授权 2.1 语法格式 2.2 语法说明 2.3 权限类型 2.4 权限级别 三. 回收权限 3.1 语法格式 3.2 语法说明 3.3 注意事项 四 .实践操作 一.概述 数据 ...

  2. 加速 AI 训练,如何在云上实现灵活的弹性吞吐

    AI 已经成为各行各业软件研发的基础,带来了前所未有的效率和创新.今天,我们将分享苏锐在AWS量化投研行业活动的演讲实录,为大家介绍JuiceFS 在 AI 量化投研领域的应用经验,也希望为其他正在云 ...

  3. 【STL】C++使用STL处理替换字符串操作。

    // Examples4STL.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <stdio.h> #incl ...

  4. Spring AOP 分享

    初级篇 AOP是什么? Aspect-oriented Programming (AOP) 即面向切面编程.简单来说,AOP 是一种编程范式,允许我们模块化地定义横跨多个对象的行为.AOP 可以帮助我 ...

  5. golang基础面试题,不完整

    启动流程 Q.go的init函数是什么时候执行的? Q.多个init函数执行顺序能保证吗? Q.go init 的执行顺序,注意是不按导入规则的(这里是编译时按文件名的顺序执行的) Q.init函数能 ...

  6. 献给转java的c#和java程序员的数据库orm框架

    献给转java的c#和java程序员的数据库orm框架 一个好的程序员不应被语言所束缚,正如我现在开源java的orm框架一样,如果您是一位转java的c#程序员,那么这个框架可以带给你起码没有那么差 ...

  7. web自动化09-frame切换、多窗口切换

    frame切换 1.html代码: <frameset cols="25%,50%,25%"> <frame src="a.htm"> ...

  8. Python基础 - 第一个python程序

    Python程序是什么? Python源程序就是一个特殊格式的文本文件,可以使用任意文本编辑器软件做python的开发,python的文件扩展名为 .py 执行python程序的三种方式 直接调用解释 ...

  9. 推荐一个日历转换开源工具库,支持C#、Java、PHP等主流的语言

    日历对我们来说,最熟悉的就是阳历和农历,在中国每年都有固定的节日.节气.中国特有传统节日,有些节日是固定的,但是节气这些都需要我们经过一定规则换算出来. 所以,今天给大家推荐一个开源库,它支持阳历.阴 ...

  10. 自从用了 Kiali 以后才知道,配置 Istio 的 流量管理 是如此容易

    在生产环境中,直接登录服务器是非常不方便的,我们可以使用Kiali配置Istio的流量管理. 本文以Istio官方提供的Bookinfo应用示例为例,使用Kiali配置Istio的流量管理.Booki ...