2014-10 u-boot make过程分析

2023-10-27

Based on u-boot-2014-10.

 

当我们已经做完make xxx_defconfig后(这个流程可以参看:《2014-10 u-boot make xxx_defconfig 过程分析》),在源码顶层目录生成.config文件,然后我们执行make命令,下面是它的流程:

make默认make all所有的目标,而all的定义如下:

 

all:        $(ALL-y)

 

需要条件$(ALL-y),而$(ALL-y)的定义如下:

 

ALL-y += u-boot.srec u-boot.bin System.map binary_size_check

 

需要条件:

1、u-boot.srec

u-boot.srec: u-boot FORCE

    $(call if_changed,objcopy)

2、u-boot.bin

u-boot.bin: u-boot FORCE

    $(call if_changed,objcopy)

    $(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE))

    $(BOARD_SIZE_CHECK)

3、System.map

System.map: u-boot

 

        @$(call SYSTEM_MAP,$<) > $@

 

4、binary_size_check

 binary_size_check: u-boot.bin FORCE

     @file_size=$(shell wc -c u-boot.bin | awk '{print $$1}') ; \

     map_size=$(shell cat u-boot.map | \

         awk '/_image_copy_start/ {start = $$1} /_image_binary_end/ {end = $$1} END {if (start != "" && end != "") print "ibase=16; " toupper(end) " - " toupper(start)}' \

         | sed 's/0X//g' \ 

         | bc); \          

     if [ "" != "$$map_size" ]; then \

         if test $$map_size -ne $$file_size; then \

             echo "u-boot.map shows a binary size of $$map_size" >&2 ; \                                                                                                                              

             echo "  but u-boot.bin shows $$file_size" >&2 ; \

             exit 1; \     

         fi \

     fi                    

 

由此大概可以看出,他们都首先需要u-boot这个elf文件。

而u-boot的依赖关系:

u-boot:$(u-boot-init) $(u-boot-main) u-boot.lds

    $(call if_changed,u-boot__)

 

(1)u-boot-init定义为:

u-boot-init := $(head-y)

head-y的定义为:  

head-y := $(CPUDIR)/start.o

(2)u-boot-main定义为:

u-boot-main := $(libs-y)

libs-y的定义为各种库和驱动,项目较多,在此只列出几个比较麻烦的引用:

libs-y += lib/

libs-$(HAVE_VENDOR_COMMON_LIB) += board/$(VENDOR)/common/

libs-y += $(CPUDIR)/

ifdef SOC

libs-y += $(CPUDIR)/$(SOC)/

 

endif

 

libs-y += arch/$(ARCH)/lib/

 

VENDOR CPUDIR SOC ARCH等的定义在顶层目录中的config.mk,因为顶层目录的config.mk已经被包含到Makefile中了:

include$(srctree)/config.mk

config.mk的内容在以后博客中分析(已更新《2014-10 u-boot 顶层config.mk分析》)。

(3)u-boot.lds定义为:

u-boot.lds: $(LDSCRIPT) prepare FORCE

    $(call if_changed_dep,cpp_lds)

$(LDSCRIPT)定义为:

 514 # If there is no specified link script, we look in a number of places for it

 515 ifndef LDSCRIPT

 516     ifeq ($(wildcard $(LDSCRIPT)),)

 517         LDSCRIPT := $(srctree)/board/$(BOARDDIR)/u-boot.lds

 518     endif

 519     ifeq ($(wildcard $(LDSCRIPT)),)

 520         LDSCRIPT := $(srctree)/$(CPUDIR)/u-boot.lds

 521     endif

 522     ifeq ($(wildcard $(LDSCRIPT)),)

 523         LDSCRIPT := $(srctree)/arch/$(ARCH)/cpu/u-boot.lds

 524     endif

 525 endif

 

LDSCRIPT优先使用这三者中后面的lds,因为:=符号的取值是由命令当前所处位置决定的。

 

preparede定义:

prepare:prepare0

prepare0的定义:

prepare0: archprepare FORCE

    $(Q)$(MAKE)$(build)=.

 

archprepare的定义:

archprepare: prepare1 scripts_basic

scripts_basic展开为:

@make -f scripts/Makefile.build obj=scripts/basic

原因请见另一篇blog《u-boot make xxx_defconfig 过程分析》

prepare1的定义:

prepare1: prepare2 $(version_h) $(timestamp_h) include/config/auto.conf

------

$(version_h): include/config/uboot.release FORCE

    $(call filechk,version.h)

version_h:= include/generated/version_autogenerated.h

$(timestamp_h): $(srctree)/Makefile FORCE

    $(call filechk,timestamp.h)

timestamp_h := include/generated/timestamp_autogenerated.h

------

prepare2的定义:

prepare2: prepare3 outputmakefile

outputmakefile并不执行,原因还是请见另一篇blog《u-boot make xxx_defconfig 过程分析》

prepare3的定义:

prepare3: include/config/uboot.release

 

include/config/auto.conf的定义和生成:

include/config/%.conf:$(KCONFIG_CONFIG) include/config/auto.conf.cmd

 

    $(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig

 

$(KCONFIG_CONFIG) 就是 .config 这个配置文件。

那么  include/config/auto.conf.cmd 这个文件应该在什么时候生成?


现在仍然假设 auto.conf 和 auto.conf.cmd 还没有生成,那么由上面的 $(KCONFIG_CONFIG) include/config/auto.conf.cmd: ; 这条语句知道,该语句中的目标没有依赖,也没有生成它的规则命令,所以可想 GNU Make 本身无法生成 auto.conf.cmd 的。然后该条语句后面的一个分号表明,这两个目标被强制是最新的,所以下面这条命令得以执行:
$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig

这里我们看到要生成一个目标 silentoldconfig ,这个目标定义在 scripts/kconfig/Makefile 中。因为这里使用 -f 选项重新指定了顶层 Makefile,而目标又是 silentoldconfig ,所以该命令最终会在顶层 Makefile 的 462-464 这里执行:

1

2

3

%config: scripts_basic outputmakefile FORCE

        $(Q)mkdir -p include/linux include/config

        $(Q)$(MAKE) $(build)=scripts/kconfig $@



这时,我们来到 scripts/kconfig/Makefile 文件里。在该文件的 32-34 行看到:

1

2

3

silentoldconfig: $(obj)/conf

        $(Q)mkdir -p include/generated

        $< -s $(Kconfig)


从上面看到,silentoldconfig 目标需要依赖 conf 这个程序,该程序也在 scripts/kconfig 目录下生成。
$< -s $(Kconfig) 该条命令相当于 conf -s $(Kconfig) ,这里 $(Kconfig) 是位于不同平台目录下的 Kconfig 文件,比如在 x86 平台就是 arch/x86/Kconfig 。

conf 程序的源代码的主函数在同目录的 conf.c 文件中,在 main() 函数中看到:

1

2

3

4

5

6

7

8

9

10

while ((opt = getopt(ac, av, "osdD:nmyrh")) != -1) {

        switch (opt) {

        case 'o':

            input_mode = ask_silent;

            break;

        case 's':

            input_mode = ask_silent;

            sync_kconfig = 1;

            break;

... ...


所以,在使用 s 参数时,sync_kconfig 这个变量会为 1 。同样在 main() 函数还看到:

1

2

3

4

5

6

7

8

9

10

11

12

13

    if (sync_kconfig) {

        name = conf_get_configname();

        if (stat(name, &tmpstat)) {

            fprintf(stderr, _("***\n"

                "*** You have not yet configured your kernel!\n"

                "*** (missing kernel config file \"%s\")\n"

                "***\n"

                "*** Please run some configurator (e.g. \"make oldconfig\" or\n"

                "*** \"make menuconfig\" or \"make xconfig\").\n"

                "***\n"), name);

            exit(1);

        }

    }


上面代码中,如果我们从未配置过内核,那么就会打印出错误信息,然后退出。这里假设已经配置过内核,并生成了 .config 文件,那么在 main() 函数中会来到:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

    switch (input_mode) {

    case set_default:

        if (!defconfig_file)

            defconfig_file = conf_get_default_confname();

        if (conf_read(defconfig_file)) {

            printf(_("***\n"

                "*** Can't find default configuration \"%s\"!\n"

                "***\n"), defconfig_file);

            exit(1);

        }

        break;

    case ask_silent:

    case ask_all:

    case ask_new:

        conf_read(NULL);

        break;

... ...


由于使用 s 选项,则 input_mode 为 ask_silent,所以这里会执行 conf_read(NULL); 函数。
conf_read(NULL); 函数用来读取 .config 文件。读取的各种相关内容主要存放在一个 struct symbol 结构链表里,而各个结构的相关指针则放在一个 symbol_hash[] 的数组中,对于数组中元素的寻找通过 fnv32 哈希算法进行定位。

最后会来到 conf.c 中的底部:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

    if (sync_kconfig) {

        /* silentoldconfig is used during the build so we shall update autoconf.

         * All other commands are only used to generate a config.

         */

        if (conf_get_changed() && conf_write(NULL)) {

            fprintf(stderr, _("\n*** Error during writing of the kernel configuration.\n\n"));

            exit(1);

        }

        if (conf_write_autoconf()) {

            fprintf(stderr, _("\n*** Error during update of the kernel configuration.\n\n"));

            return 1;

        }

    else {

        if (conf_write(NULL)) {

            fprintf(stderr, _("\n*** Error during writing of the kernel configuration.\n\n"));

            exit(1);

        }

    }

 

实际上也只有当处理 silentoldconfig 目标是 sync_kconfig 变量才会为 1 。上面代码中的 conf_write_autoconf() 函数就用来生成 auto.conf, auto.conf.cmd 以及 autoconf.h 这 3 个文件。

在 if (conf_get_changed() && conf_write(NULL)) 这个判断里,conf_get_changed() 函数判断 .config 文件是否做过变动,如果是,那么会调用 conf_write(NULL) 来重新写 .config 文件。实际上,对于 defconfig, oldconfig, menuconfig 等目标来说,conf 程序最终也是调用 conf_write() 函数将配置结果写入 .config 文件中(最后那个 else 里的内容便是)。

确保了 .config 已经最新后,那么调用 conf_write_autoconf() 生成 auto.conf,auto.conf.cmd 以及 autoconf.h 这 3 个文件。

来到 conf_write_autoconf() 函数:j

在 conf_write_autoconf() 里,调用 file_write_dep("include/config/auto.conf.cmd"); 函数将相关内容写入 auto.conf.cmd 文件。在生成的 auto.conf.cmd 文件中可以看到:

 

1

2

include/config/auto.conf: \

        $(deps_config)


可以看到 auto.conf 文件中的内容依赖于 $(deps_config) 变量里定义的东西,这些东西基本上是各个目录下的 Kconfig 以及其它一些相关文件。

auto.config 和 .config 的差别是在 auto.config 里去掉了 .config 中的注释项目以及空格行,其它的都一样。

仍然在 conf_write_autoconf() 里,分别建立了 .tmpconfig,.tmpconfig_tristate 和 .tmpconfig.h 这 3 个临时文件:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

out = fopen(".tmpconfig""w");

    if (!out)

        return 1;

 

    tristate = fopen(".tmpconfig_tristate""w");

    if (!tristate) {

        fclose(out);

        return 1;

    }

 

    out_h = fopen(".tmpconfig.h""w");

    if (!out_h) {

        fclose(out);

        fclose(tristate);

        return 1;

    }


然后将文件头的注释部分分别写入到这几个临时文件中:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

    sym = sym_lookup("KERNELVERSION", 0);

    sym_calc_value(sym);

    time(&now);

    fprintf(out, "#\n"

             "# Automatically generated make config: don't edit\n"

             "# Linux kernel version: %s\n"

             "# %s"

             "#\n",

             sym_get_string_value(sym), ctime(&now));

    fprintf(tristate, "#\n"

              "# Automatically generated - do not edit\n"

              "\n");

    fprintf(out_h, "/*\n"

               " * Automatically generated C config: don't edit\n"

               " * Linux kernel version: %s\n"

               " * %s"

               " */\n"

               "#define AUTOCONF_INCLUDED\n",

               sym_get_string_value(sym), ctime(&now));



接着在 for_all_symbols(i, sym) 这个循环里(是一个宏)里将相关内容分别写入到这几个文件中。

在最后一段代码中,将这几个临时文件进行改名:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

name = getenv("KCONFIG_AUTOHEADER");

    if (!name)

        name = "include/generated/autoconf.h";

    if (rename(".tmpconfig.h", name))

        return 1;

    name = getenv("KCONFIG_TRISTATE");

    if (!name)

        name = "include/config/tristate.conf";

    if (rename(".tmpconfig_tristate", name))

        return 1;

    name = conf_get_autoconfig_name();

    /*

     * This must be the last step, kbuild has a dependency on auto.conf

     * and this marks the successful completion of the previous steps.

     */

    if (rename(".tmpconfig", name))

        return 1;


上面代码中的 conf_get_autoconfig_name() 实现为:

 

1

2

3

4

5

6

const char *conf_get_autoconfig_name(void)

{

    char *name = getenv("KCONFIG_AUTOCONFIG");

 

    return name ? name : "include/config/auto.conf";

}


从上面可以看到,分别生成了以下几个文件:

引用

include/generated/autoconf.h
include/config/tristate.conf
include/config/auto.conf



其中 include/generated/autoconf.h 头文件由内核本身使用,主要用来预处理 C 代码。比如在 .config 或 auto.conf 中定义要编译为模块的项,如:
CONFIG_DEBUG_NX_TEST=m
在 autoconf.h 中会被定义为:
#define CONFIG_DEBUG_NX_TEST_MODULE 1

在 .config 或 auto.conf 后接字符串值的项,如:
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
在 autoconfig.h 中会被定义为:
#define CONFIG_DEFCONFIG_LIST "/lib/modules/$UNAME_RELEASE/.config"

同样对应于 int 型的项如 CONFIG_HZ=1000 在 autoconf.h 中被定义为 #define CONFIG_HZ 1000 。

 

后半段引用于:http://blog.csdn.net/lcw_202

来源于http://www.cnblogs.com/humaoxiao

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

2014-10 u-boot make过程分析 的相关文章

  • Docker下载包的时候显示Connection failed [IP: 91.189.88.142 80]

    之前使用Docker生成镜像的时候 有一些包因为网络问题出现Connection failed 和 failed to fetch 这种时候我们可以通过修改Dockerfile中的下载源来防止网络连接失败 具体操作为在Dockerfile
  • 读取Excel数据

    该工具类是可以用的 注意别导错包 还有Excel是有版本控制的 我这里就没有那么详细了 工具类 import java io File import java io FileInputStream import java util Arra
  • Dubbo序列化问题排查

    h1 现象 为一个dubbbo接口新增了一个方法 code DomainObject
  • 动态建立Vxlan实现隧道跨子网互访实验配置(集中式网关场景)

    目录 基础配置 配置E V P N 在CE1 CE2 CE3开启E V P N功能 建立CE1 CE2 CE3之间的E V P N对等体 创建BD域并配置EVPN实例 选择报文进入Vxlan隧道 配置发送Type3路由 创建三层网关的Vbd
  • 【 Linux 网络编程 】Linux 下 select 的使用

    一 相关函数 NAME select FD CLR FD ISSET FD SET FD ZERO synchronous I O multiplexing 同步多路IO转接 SYNOPSIS According to POSIX 1 20
  • 简单的实现app界面劫持和后台监控

    如今越来越多的手机玩家都为了折腾一些稀奇古怪的东西 或出于好奇 或出于贪心 往往会root手机去尝试安装一些外挂 刷分 刷粉 修改版等等乱七八糟的东西 正因为此类用户的存在 一些小人就盯上了这方面的市场 去开发一些盗取财产 密码 锁机等等
  • Java面试必问的HashMap,javaweb开发的框架

    准备好套路 自我介绍 千万不能筐瓢 一定要牢记 自然流畅地介绍自己的学习经历 工作经历 项目经历 个人优势等等 抽象概念 当面试官问你是如何理解多线程的时候 你要知道从定义 来源 实现 问题 优化 应用方面系统性地回答 项目强化 必须针对简

随机推荐

  • Java学生信息管理系统_毕业设计项目实例(附源码)

    一 项目介绍 1 面向人群 学校的学生信息管理人员以及老师和学生 2 功能描述 1 基本功能 该学生成绩管理系统 涉及学生基本信息 姓名 学号 专业 班级 所属院系 高等数学 英语 数据结构 计算机组成原理 面向对象程序设计 系统可以完成对
  • 【重庆邮电大学协办】2023年第六届数据挖掘与知识发现国际会议(DMKD 2023)

    重庆邮电大学协办 2023年第六届数据挖掘与知识发现国际会议 DMKD 2023 重要信息 会议网址 www icdmkd org 会议时间 2023年6月24 26日 召开地点 中国 重庆 截稿时间 2023年5月24日 录用通知 投稿后
  • OPC服务器简介和入门介绍

    什么是OPC OPC代表OLE 对象链接和嵌入 过程控制 OPC是最流行的数据连接标准 用于在控制器 设备 应用程序和其他基于服务器的系统之间进行通信 而无需进入数据传输的自定义驱动程序 工厂自动化系统或过程由来自不同供应商或供应商的不同协
  • 动态主机配置协议DHCP

    文章目录 一 DHCP介绍 二 DHCP工作原理 三 DHCP实验 eNSP 四 总结 一 DHCP介绍 DHCP 全称动态主机配置协议 Dynamic Host Configuration Protocol 在大型企业网络中 会有大量的主
  • python hook android_[原创]初识Frida--Android逆向之Java层hook (一)

    博客同步 访问 0x00 文中用到的工具Frida jadx gui 一个强大的android反编译工具 genymotion模拟器 Python2 7以及frida python库 radare2 反汇编器 pycharm 0x01 ho
  • 安卓APP_ 布局(1)—— LinearLayout

    摘自 安卓APP 布局 1 LinearLayout 作者 丶PURSUING 发布时间 2021 04 05 16 46 22 网址 https blog csdn net weixin 44742824 article details
  • python决策树算法代码_Python3.0 实现决策树算法的流程

    决策树的一般流程 检测数据集中的每个子项是否属于同一个分类 if so return 类标签 Else 寻找划分数据集的最好特征 划分数据集 创建分支 节点 from math import log import operator 生成样本
  • ASP.NET微信公众号开发实记之一 接入配置

    第一步 填写服务器配置 登录微信公众平台官网后 在公众平台后台管理页面 开发者中心页 点击 修改配置 按钮 填写服务器地址 URL Token和EncodingAESKey 其中URL是开发者用来接收微信消息和事件的接口URL Token可
  • 【Unity 3D】图形界面GUI的讲解及在C#中实现用户登录界面的实战(附源码)

    需要源码请点赞关注收藏后评论区留言并且私信 在游戏开发过程中 游戏界面占据了非常重要的地位 玩家启动游戏的时候 首先看到的就是游戏的UI 其中包含图片 按钮和高级控件等等 UGUI和GUI是Unity 3D中最常用的两个UI系统 一 GUI
  • VMware workstation搭建华为FusionCompute实验环境(五)保姆级安装教程,可运行虚拟机

    VMware安装VRM 1 打开VMware 点击文件 选择新建虚拟机 2 选择 自定义 3 硬件兼容性保持默认 4 选择稍后安装操作系统 5 操作系统选择 ESXI 6 输入虚拟机名称及虚拟机保存位置 7 配置处理器 最少4个 8 配置内
  • 低功耗设计及其UPF实现第一节

    第一节 低功耗设计的基本单元和概念 大家好 最近疫情宅家 工作没有那么忙 所以写一下之前从事过的低功耗设计并和大家分享 希望大家多多提出不足 本次总结一共分为若干小节 还没定 我会从最最基础的低功耗的概念和器件开始分享 尽量全部说干货 不过
  • 暑假补卷2——进程控制:

    进程地址空间 接进程概念 写时拷贝 通常 父子代码共享 父子再不写入时 数据也是共享的 当任意一方试图写入 便以写时拷贝的方式各自一份副本 具体见下图 fork常规用法 一个父进程希望复制自己 使父子进程同时执行不同的代码段 例如 父进程等
  • 晶振/晶体布线原则

    1 晶振布放和走线同IC在相同层 50欧姆走线 布线层到参考层间挖开GND 晶振模块远离其他器件 2 晶体模块参考层尽量使用完整GND层 没有完整GND层可用 也要在晶振下面完全铺地 3 走线仅可能短 两根线尽量等长且尽量紧密耦合 4 将负
  • 学生选课系统项目设计报告

    设 计 报 告 报告题目 学生选课系统的数据库设计 摘 要 在当下疫情肆虐的时期 线上教学成为了主流方式 各种教学活动都转为线上 网上选课系统成为学生课程分配的主要方式 传统的选课管理模式已经无法满足当前的实际需求 为此 我们开发了学生选课
  • 2023年最新最全软件测试用例大全,轻松学会测试用例,有手就行

    一 概念 测试用例的基本概念 测试用例 Test Case 是为了实施测试而向被测试的系统提供的一组集合 这组集合包含 测试环境 操作步骤 测试数据 预期结果等要素 主要步骤 测试环境 测试步骤 测试数据 预期结果 网易邮箱注册成功测试用例
  • torchvision中的resnet的实现

    D pythonCodes 深度学习实验 经典分类网络 6 ResNet resnet手动实现实验 一 ResNet网络模型详解 1 1 整体结构 可以看到 resnet34主要就是由layer1 4组成 而这4个layer分别就是由3 4
  • Web前端iframe使用以及页面通信postmessage

    iframe基本内涵 通常我们使用iframe直接直接在页面嵌套iframe标签指定src就可以了 iframe常用属性 1 frameborder 是否显示边框 1 yes 0 no 2 height 框架作为一个普通元素的高度 建议在使
  • AppsFlyer 研究(六) 域名及用户邀请归因

    一 AppsFlyer涉及的域名 1 https conversions appsflyer com 该域名用于SDK上报App首次打开的session数据 包含用户的设备id 供AF归因 2 https gcdsdk appsflyer
  • 百人计划 图形1.4 PC手机图形API介绍作业/个人笔记

    作业部分 1 本人对Vulkan不了解 下述文字根据网上资料搜集总结而来 如日后有新的想法将更新在此段文字之后 Vulkan优点是轻薄 降低CPU的开销 缺点是相对繁琐 接口多概念多 Vulkan让我想到houdini 功能强大 建模特效程
  • 2014-10 u-boot make过程分析

    Based on u boot 2014 10 当我们已经做完make xxx defconfig后 这个流程可以参看 2014 10 u boot make xxx defconfig 过程分析 在源码顶层目录生成 config文件 然后