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在内的各个平台上.它有以下功能 ...
随机推荐
- 界面重建——Marching cubes算法
一.引子 对于一个标量场数据,我们可以描绘轮廓(Contouring),包括2D和3D.2D的情况称为轮廓线(contour lines),3D的情况称为表面(surface).他们都是等值线或等值面 ...
- 安装vue-lic
vue-cli是Vue.js开发的标准工具.它简化了程序员基于webppack创建工程化的Vue项目的过程.引用自vue-cli官网上的一句话:程序员可以专注在撰写应用上,而不必花好几天去纠结webp ...
- Kubuesphere部署Ruoyi(三):持久化存储配置
按照如下教程配置NFS 先服务器: https://kubesphere.io/zh/docs/v3.3/reference/storage-system-installation/nfs-serve ...
- 【对比】文心一言对飚ChatGPT实操对比体验
前言 缘由 百度[文心一言]体验申请通过 本狗中午干饭时,天降短信,告知可以体验文心一言,苦等一个月的实操终于到来.心中这好奇的对比心理油然而生,到底是老美的[ChatGPT]厉害,还是咱度娘的[文心 ...
- SignalR服务端及客户端实现
服务器端: 引用nuget: 1.Microsoft.AspNet.SignalR.SelfHost 2.Microsoft.Owin.Cors internal class Program { st ...
- 2022-12-17:订单最多的客户。以下数据,结果输出3。请问sql语句如何写? DROP TABLE IF EXISTS `orders`; CREATE TABLE `orders` ( `
2022-12-17:订单最多的客户.以下数据,结果输出3.请问sql语句如何写? DROP TABLE IF EXISTS `orders`; CREATE TABLE `orders` ( `or ...
- 2020-11-19:go中,defer原理是什么?
福哥答案2020-11-19:- - 什么是defer - defer是go语言提供的一种用于注册延迟调用的机制:让函数或者语句在当前函数执行完毕(包括return正常结束或者panic导致的异常结束 ...
- Python随机UserAgent库,让你不再手动敲UA!
前言 之前也懵懵懂懂写过python爬虫,但是经常被网站的反爬机制干趴下,然后手动写了个随机UA库,情况才好些.今天在互联网畅游时发现,有一个能够产生随机UA的第三方库! 安装第三方库 老生常谈啦,p ...
- 代码随想录算法训练营Day46 动态规划
代码随想录算法训练营 代码随想录算法训练营Day46 动态规划| ● 139.单词拆分 关于多重背包,你该了解这些! 背包问题总结篇! 139.单词拆分 题目链接:139.单词拆分 给定一个非空字符 ...
- django 如何提升性能(高并发)
django 如何提升性能(高并发) 对一个后端开发程序员来说,提升性能指标主要有两个一个是并发数,另一个是响应时间网站性能的优化一般包括 web 前端性能优化,应用服务器性能优化,存储服务器优化. ...