第3阶段——内核启动分析之make uImage编译内核(3)

目标:

通过分析makefile,明白make uImage如何编译内核

把整个内核的makefile分成三类(makefile资料文档在linux-2.6.22.6/Documentation/build/makefiles.txt)

<1>各级子目录makefile(每个子目录都有makefile) <2>/arch/arm/Makefile(架构相关的makefile) <3>顶层目录makefile

在顶层目录makefile中auto.conf和/arch/arm/Makefile又被包含在其中,如下所示:

413 include $(srctree)/arch/$(SRCARCH)/Makefile 443 include include/config/auto.conf

1.分析子目录makefile, 随便打开一个子目录makefile,可以看到类似的内容:

obj-y  += mem.o random.o tty_io.o n_tty.o tty_ioctl.o

obj-m    += s3c24xx_leds.o

obj-m    += s3c24xx_buttons.o

obj-m    += ker_rw.o

obj-$(CONFIG_LEGACY_PTYS) += pty.o

obj-$(CONFIG_UNIX98_PTYS) += pty.o

在makefile资料文档中得到(linux-2.6.22.6/Documentation/build/makefiles.txt)

40 --- 3.2 Built-in object goals - obj-y            

41~59 ....

60 Example:

61   #drivers/isdn/i4l/Makefile

62   # Makefile for the kernel ISDN subsystem and device drivers.

63   # Each configuration option enables a list of files.

64   obj-$(CONFIG_ISDN)             += isdn.o

65   obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o

从上面第40行得出要追加built-in.o文件(编译进内核)时,使用obj-y

例如:     obj-y += isdn.o   

             obj-y+= isdn_bsdcomp.o

167 --- 3.3 Loadable module goals - obj-m

168~188 ...

189 Example:

190  #drivers/isdn/i4l/Makefile

191  obj-$(CONFIG_ISDN) += isdn.o

192  isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o

从上面第167行得出加载模块.ok文件时,使用obj-m

例如: obj-m += isdn.o

         isdn-objs: = isdn_net_lib.o isdn_v110.o isdn_common.o

最后编译成isdn.ko模块文件

所以

在配置文件auto.conf中CONFIG_XXX=y, 那么编译时,源码.o文件会被Makefile追加到built-in.o文件,供给顶层Makefile生成内核 在配置文件auto.conf中CONFIG_XXX=m,那么编译时, 源码.o文件会被Makefile编译成模块XXX.ko文件; 在配置文件auto.conf中CONFIG_XXX=n, 那么编译时,对应的源码文件不会被makefile编译;

2分析./arch/arm/Makefile(ARM架构makefile)

首先在./arch/arm/Makefile文件第227行得到:

227 zImage Image xipImage bootpImage uImage: vmlinux

228       $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@

得出uImage等5个目标的生成都是依赖于vmlinux(vmlinux位于顶层makefile,其中vmlinux 指未压缩的内核,不能直接加载)

由于,我们在顶层目录下执行make uImage,但是uImage在./arch/arm/Makefile,

在顶层makefile中第413行可以看到:

413 include $(srctree)/arch/$(SRCARCH)/Makefile 

由于打上补丁后, SRCARCH=arm

所以这个./arch/arm/Makefile被顶层makefile包含,然后调用了./arch/arm/Makefile中的uImage

3 分析顶层目录Makefile

3.1 顶层vmlinux生成过程

在顶层目录makefile中第484行得出:

484 all: vmlinux

其中,all就是直接 make 指令编译内核,显然make uImage和make都依赖于vmlinux(内核)

然后在746得到出vmlinux生成步骤:

746 vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) $(kallsyms.o) FORCE

3.1.1 接下来分析顶层vmlinux依赖文件

可以看出vmlinux依赖于:

vmlinux-lds: 链接脚本 vmlinux-init: 初始化相关的代码 vmlinux-main:核心代码

kallsyms.o: 变量

这些依赖在顶层Makefile中608行处定义:

608 vmlinux-init := $(head-y) $(init-y)                        // head-y:头文件   init-y:初始化文件 
609 vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y) // core-y:核心文件libs-y:库文件  drivers-y:驱动文件net-y:网络文件
611 vmlinux-lds := arch/$(SRCARCH)/kernel/vmlinux.lds         // arch/arm/kernel/vmlinux.lds链接脚本

逐个分析:

(1) vmlinux-lds 

因为我们是使用的arm架构,$(SRCARCH) = arm

因此   vmlinux-lds  :=arch/arm/kernel/vmlinux.lds

首先查看arch/arm/kernel/vmlinux.lds文件

如下所示,在288行处设置了内核运行在虚拟地址0Xc0008000处,接下来按顺序存放vmlinux的依赖文件

SECTIONS

{

. = (0xc0000000) + 0x00008000;          //设置内核运行地址

 .text.head : {                           

  _stext = .;

  _sinittext = .;

*(.text.head)                     //存放.text.head段

}

.init : {
*(.init.text)                    //存放.init.text段   

...   ...

(2)  vmlinux-init head-y在/arch/arm/makefile中94行处定义:

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

由于MMUEXT没有定义 ,所以变量head-y 应为  

head-y := arch/arm/kernel/head.o     arch/arm/kernel/init_task.o

init-y在顶层makefile中427行中定义

427 init-y    := init/

然后在532行中修改init-y

532 init-y    := $(patsubst %/, %/built-in.o, $(init-y))

这里的patsubst 是实现匹配替换的,在这里将$(init-y)  中的  / 替换为'/built-in.o' 。

所以变量init-y 应为  

init-y          := init/built-in.o

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

(3) vmlinux-main

core-y 在顶层Makefile中438行定义,在562行处追加,在574行处修改:

438 core-y    := usr/

562 core-y    += kernel/ mm/ fs/ ipc/ security/ crypto/ block/

574 core-y    := $(patsubst %/, %/built-in.o, $(core-y))   //将$(core-y)中的  的/ 替换为'/built-in.o' 。

所以变量core-y 应为  

core-y := usr/ built-in.o  kernel/ built-in.o  mm/ fs/ built-in.o  ipc/ built-in.o  security/ built-in.o  crypto/ built-in.o  block/ built-in.o

libs-y 在顶层Makefile中437行定义,然后后面577行进行修改:

437 libs-y    := lib/

577 libs-y1    := $(patsubst %/, %/lib.a, $(libs-y))        // libs-y1:= lib/ lib.a

578 libs-y2    := $(patsubst %/, %/built-in.o, $(libs-y))    // libs-y2:= lib/ built-in.o

579 libs-y    := $(libs-y1) $(libs-y2)                  // libs-y:= lib/ lib.a   lib/ built-in.o

所以变量libs-y 应为

libs-y:= lib/ lib.a   lib/ built-in.o

core-y 在顶层Makefile中435行定义,在574行处修改:

435 drivers-y    := drivers/ sound/

475 drivers-y    := $(patsubst %/, %/built-in.o, $(drivers-y)

所以变量drivers-y 应为

drivers-y  := drivers/ built-in.o   sound/ built-in.o     

net-y 在顶层Makefile中436行定义,在575行处修改:

436 net-y    := net/

576 net-y    := $(patsubst %/, %/built-in.o, $(net-y))   

所以变量net-y 应为

net-y  := net/ built-in.o

因此   vmlinux-main := usr/ built-in.o kernel/ built-in.o  mm/ fs/ built-in.o  ipc/ built-in.o security/ built-in.o crypto/ built-in.o  block/ built-in.o  

lib/ lib.a lib/ built-in.o

drivers/ built-in.o  sound/ built-in.o  

net/ built-in.o

vmlinux的依赖文件分析完毕

3.1.2 顶层vmlinux生成规则分析

直接make uImage ,然后ctrl+z 暂停编译,从串口上分析

(1)首先rm vmlinux 删除目标文件,再make uImage v=1   (V=1表示显示详细编译过程 ) 

如上图, 主要通过arm-linux-ld连接选项,通过vmlinux.lds链接脚本对内存的地址设置,然后将 顶层vmlinux依赖文件分析出来的所有文件按一定顺序布局并输出vmlinux文件  (arm-linux-ld使用参考:http://www.cnblogs.com/lifexy/p/7065175.html)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏闵开慧

Hadoop FS Shell命令大全

    调用文件系统(FS)Shell命令应使用 bin/hadoop fs <args>的形式。 所有的的FS shell命令使用URI路径作为参数。URI格...

3559
来自专栏iOS开发日记

iOS - xcode经常报的经典error解决办法大全

错误原因: 返回cell的代码放在括号的范围不对,看下周围代码的作用域。有时括号太多,容易放错代码。

3928
来自专栏生信技能树

构建shell脚本一文就够

非常多的朋友在看我们公众号过往转录组,WES,等流程分享的时候发现很难理解我们的代码,其实就是缺乏shell脚本知识,那么这篇教程你就不容错过。 内容 使用多个...

3054
来自专栏转载gongluck的CSDN博客

学习GDB

1 简介      GDB(GNU Debugger)是GCC的调试工具。其功能强大,现描述如下:      GDB主要帮忙你完成下面四个方面的功能:     ...

3497
来自专栏张戈的专栏

Shell脚本的简单排错法及调试程序bashdb

Jboss 的研究稍有卡壳,那就来点基础教程好了。 与众多脚本语言一样,Shell 脚本在执行时出错是很常见的,最简单的原因无外乎脚本在编写的过程中出现了语法错...

3636
来自专栏从零开始学自动化测试

python笔记26-命令行传参sys.argv

平常我们在用别人写好的python包的时候,在cmd输入xx -h就能查看到帮助信息,输入xx -p 8080就能把参数传入程序里,看起来非常酷。 本篇就来讲下...

1034
来自专栏kangvcar

Ansible极简教程

2442
来自专栏jouypub

Linux各种变量的含义

简书主页:https://www.jianshu.com/u/756c9c8ae984

430
来自专栏LIN_ZONE

谷歌断点调试(转载)

简单地说,断点调试是指自己在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,...

884
来自专栏http://www.cnblogs.com

python3 logging模块

很多程序都有记录日志的需求,并且日志包含的信息有正常的程序访问日志还可能有错误,警告等信息输出,python的logging模块提供了标准的日志接口,可以通过它...

42610

扫码关注云+社区