/**

******************************************************************************
* @author    Maoxiao Hu
* @version   V1.0.0
* @date       Dec-2014
******************************************************************************
* < COPYRIGHT 2014 ISE of SHANDONG UNIVERSITY >
*******************************************************************************
**/
 
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
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

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

  1. Contest - 2014 SWJTU ACM 手速测试赛(2014.10.31)

    题目列表: 2146 Problem A [手速]阔绰的Dim 2147 Problem B [手速]颓废的Dim 2148 Problem C [手速]我的滑板鞋 2149 Problem D [手 ...

  2. Linux - Eclipse CDT + GCC 安装(2014.10.2)

    Eclipse CDT + GCC 安装 (2014.10.2) 本文地址:http://blog.csdn.net/caroline_wendy 1. 安装Eclipse,在官方站点下载Eclips ...

  3. App Store审核指南中文版(2014.10.11更新)

    App Store审核指南中文版(2014.10.11更新) 2014-10-11 16:36 编辑: suiling 分类:AppStore研究 来源:CocoaChina  2 8657 App ...

  4. phpStudy + JspStudy 2014.10.02 下载

    phpStudy + JspStudy 2014.10.02 下载 目标:让天下没有难配的php环境. phpStudy Linux版&Win版同步上线 支持Apache/Nginx/Teng ...

  5. 2014.10.5 再次学习LINUX

    mesg 发送信息给root y n write/talk 写消息给 wall 给所有用户发送消息 ps -aux ps -elF pstree 命令行跳转:CTRL+a行首 CTRL+e行尾 CTR ...

  6. 【spring boot】10.spring boot下的单元测试

    spring boot下的单元测试,思前想后还是需要单独用一章篇幅来看看. 然后在看了介绍和使用时候,我感觉并不想多去看了. 但是还是给后来人留下参考的路径: 官网说明:https://spring. ...

  7. iPhone屏幕适配,历史及现状(http://hjcapple.github.io/2014/10/10/iphone-screen.html)

    iPhone屏幕适配,历史及现状 初代iPhone 2007年,初代iPhone发布,屏幕的宽高是320×480像素.下文也是按照宽度,高度的顺序排列.这个分辨率一直到iPhone 3GS的也保持不变 ...

  8. Cheatsheet: 2014 10.01 ~ 10.30

    .NET ASP.NET Web Api: Unwrapping HTTP Error Results and Model State Dictionaries Client-Side HTTP 20 ...

  9. 2014.10.09 Andrew 学习 WPF(刘铁锰) 笔记分享

    引言 主要是讲了关于WPF只是表现层的工具. 第一章: XAML : 可扩张应用程序标记语言    Extensible Application Markup Language 什么是XAML?  X ...

  10. 基于Android的ELF PLT/GOT符号和重定向过程ELF Hook实现(by 低端农业代码 2014.10.27)

    介绍 技术原因写这篇文章,有两种: 一个是在大多数在线叙述性说明发现PLT/GOT第二十符号重定向过程定向x86的,例<Redirecting functions in shared ELF l ...

随机推荐

  1. 1085. Perfect Sequence (25)

    the problem is from PAT,which website is http://pat.zju.edu.cn/contests/pat-a-practise/1085 At first ...

  2. spring+hibernate+struts整合(2)

    spring和struts2的整合 1:配置Web.xml文件 <filter> <filter-name>struts2</filter-name> <fi ...

  3. SQL Server 日期 时间类型

    --1毫秒=0.001秒 --1微秒=0.000 001秒 --1纳秒=0.000 000 001秒 --datetime精度不大好,末尾值只能是这3种: .000, .003, or .007 -- ...

  4. Linux学习之路:命令别名、历史记录和命令查找执行顺序

    一.命令别名 alias rm='rm –i':删除命令时会随时出现提示;alias vi=vim alias 不加参数,显示系统内所以命令别名 unalias 取消别名 二.历史命令 history ...

  5. SqlServer之游标深入

    原创文章,转载必需注明出处:http://www.ncloud.hk/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/introduce-for-sqlserver-curs ...

  6. 给jdk写注释系列之jdk1.6容器(10)-Stack&Vector源码解析

    前面我们已经接触过几种数据结构了,有数组.链表.Hash表.红黑树(二叉查询树),今天再来看另外一种数据结构:栈.      什么是栈呢,我就不找它具体的定义了,直接举个例子,栈就相当于一个很窄的木桶 ...

  7. sublime text修改TAB缩进为空格

    在sublime text中将TAB缩进直接转化为4个空格,可以按照如下方式操作: 菜单栏: Preferences -> Settings – More -> Syntax Specif ...

  8. .net 创建属于自己的log类

    实习到现在已经接近三个月了,由于是校企联合培养计划,所以没有工资,所幸公司对于我们这些实习生并没有什么要求,刚开始我还觉得要做点什么才能学得快,可是到了后来,发现公司安排给我们的任务并不紧要,也不算太 ...

  9. 学习css简单内容

    Css的class,ID和上下文选择符 Class选择符. Class选择符用来配置某一类css规则,将其应用到网页中一个或多个区域.配置一类样式时,要将选择符配置成类名.在类名前加(.).类名必须以 ...

  10. 配置tomcat免安装版服务器

    一.首先,确保服务器已经安装java环境,没有tomcat的可以到这里下载 http://tomcat.apache.org/ 二.解压下载的压缩包,我是解压到D盘根目录下的.记住这个目录,后面会用到 ...