linux内核vmlinux的编译过程之 --- vmlinux.o详解(八)
内核构建系统之所以要在链接 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详解(八)的更多相关文章
- Linux内核驱动学习(十)Input子系统详解
文章目录 前言 框架 如何实现`input device` 设备驱动? 头文件 注册input_dev设备 上报按键值 dev->open()和dev->close() 其他事件类型,处理 ...
- Linux 内核配置和编译
Linux 内核配置和编译 一.配置内核 (1). 为什么要配置内核 1. 硬件需求 2. 软件需求 选出需要的,去掉不要的 (2). 如何配置内核 1. make config 基于文本模式的交互 ...
- Linux下nginx编译安装教程和编译参数详解
这篇文章主要介绍了Linux下nginx编译安装教程和编译参数详解,需要的朋友可以参考下 一.必要软件准备1.安装pcre 为了支持rewrite功能,我们需要安装pcre 复制代码代码如下: # y ...
- I/O模型之二:Linux IO模式及 select、poll、epoll详解
目录: <I/O模型之一:Unix的五种I/O模型> <I/O模型之二:Linux IO模式及 select.poll.epoll详解> <I/O模型之三:两种高性能 I ...
- (转)Linux下select, poll和epoll IO模型的详解
Linux下select, poll和epoll IO模型的详解 原文:http://blog.csdn.net/tianmohust/article/details/6677985 一).Epoll ...
- shell编程之awk命令详解
shell编程之awk命令详解 a:focus { outline: thin dotted #333; outline: 5px auto -webkit-focus-ring-color; out ...
- 技巧:Linux 动态库与静态库制作及使用详解
技巧:Linux 动态库与静态库制作及使用详解 标准库的三种连接方式及静态库制作与使用方法 Linux 应用开发通常要考虑三个问题,即:1)在 Linux 应用程序开发过程中遇到过标准库链接在不同 L ...
- Linux NFS服务器的安装与配置方法(图文详解)
这篇文章主要介绍了Linux NFS服务器的安装与配置方法(图文详解),需要的朋友可以参考下(http://xb.xcjl0834.com) 一.NFS服务简介 NFS 是Network File S ...
- Java中String的intern方法,javap&cfr.jar反编译,javap反编译后二进制指令代码详解,Java8常量池的位置
一个例子 public class TestString{ public static void main(String[] args){ String a = "a"; Stri ...
- Linux Shell脚本入门--wget 命令用法详解
Linux Shell脚本入门--wget 命令用法详解 wget是在Linux下开发的开放源代码的软件,作者是Hrvoje Niksic,后来被移植到包括Windows在内的各个平台上.它有以下功能 ...
随机推荐
- DRF版本控制(源码分析)
DRF中版本控制的五种情况(源码分析) 在restful规范中要去,后端的API中需要体现版本. drf框架中支持5种版本的设置. 1. URL的GET参数传递(*) 示例: user/?versio ...
- If选择语句的用法
今天我们学习下If判断语句. 首先了解下它有几种用法: If单选择语句 If双选择语句 If多选择语句 我们一个一个用,每一个用法都给一个运用的过程演练一下. If单选择语句:我们很多需要判断一个东西 ...
- 基于kubeasz部署高可用k8s集群
在部署高可用k8s之前,我们先来说一说单master架构和多master架构,以及多master架构中各组件工作逻辑 k8s单master架构 提示:这种单master节点的架构,通常只用于测试环境, ...
- Django笔记三十九之settings配置介绍
本文首发于公众号:Hunter后端 原文链接:Django笔记三十九之settings配置介绍 这一篇笔记介绍 Django 里 settings.py 里一些常用的配置项,这些配置有一些是在之前的笔 ...
- 2022-01-27:供暖器。 冬季已经来临。 你的任务是设计一个有固定加热半径的供暖器向所有房屋供暖。 在加热器的加热半径范围内的每个房屋都可以获得供暖。 现在,给出位于一条水平线上的房屋 hous
2022-01-27:供暖器. 冬季已经来临. 你的任务是设计一个有固定加热半径的供暖器向所有房屋供暖. 在加热器的加热半径范围内的每个房屋都可以获得供暖. 现在,给出位于一条水平线上的房屋 hous ...
- Windows server 2012 安装ad域
Windows server 2012 安装ad域 安装ad域(active directory)服务的作用:存储目录数据并管理域之间的通信,包括用户登录处理,身份验证和目录搜索等. 1.使用ad ...
- nginx发布vue 项目
在本次使用nginx发布vue项目遇到 配置location 始终404 和 在项目子目录点击浏览器刷新出现404问题 使用nginx发布vue项目,为了方便测试就下载了一个nginx 放置自己目录下 ...
- js代理(Proxy) 和 反射(Reflection)
在实际开发中经常会遇到js抛出的错误,但是我们有没有想过自己去接管js异常验证,根据自己的需求抛出异常呢?原本也许不行,但是在es6出来后就可以做到了 一.代理(Proxy) 什么是'代理' 呢?代理 ...
- 渗透测试-struts2攻防环境搭建拿shell
一.下载Jspstudy 打开目录D:\JspStudy\tomcat\webapps 二.打开struts2并进行拿shell 1.打开struts2 在浏览器中输入网址http://localho ...
- 基于Quartz的可视化UI操作组件GZY.Quartz.MUI更新说明(附:在ABP中集成GZY.Quartz.MUI可视化操作组件)
前言 时隔2年.(PS:其实陆陆续续在优化,不过没发博客).. .本组件又迎来了新的更新... 很久没更新博客了.生了娃,换了工作单位,太忙了..实在抱歉 NET Core 基于Quartz的UI可视 ...