uboot编译过程详细分析

  一、uboot编译和生成文件

  说明

  现在的uboot已经做得和kernel很像,最主要的一点是,uboot也使用了dtb的方法,将设备树和代码分离开来(当然可以通过宏来控制)。

  project-x/u-boot/configs/TIny210_defconfig

  CONFIG_OF_CONTROL=y

  // 用于表示是否使用了dtb的方式

  CONFIG_OF_SEPARATE=y

  // 是否将dtb和uboot分离表一

  所以在uboot的编译中,和spl的最大区别是还要编译dtb。 (前面我们将的spl是没有使用dtb的,当然好像也可以使用dtb,只是我没有试过)。

  U-Boot编译命令

  对于mini2440开发板,编译U-Boot需要执行如下的命令:

  $ make mini2440_config

  $ make all

  使用上面的命令编译U-Boot,编译生成的所有文件都保存在源代码目录中。为了保持源代码目录的干净,可以使用如下命令将编译生成的文件输出到一个外部目录,而不是在源代码目录中,下面的2种方法都将编译生成的文件输出到 /tmp/build目录:

  $ export BUILD_DIR=/tmp/build

  $ make mini2440_config

  $ make all

  或

  $ make O=/tmp/build mini2440_config (注意是字母O,而不是数字0)

  $ make all

  为了简化分析过程,方便读者理解,这里主要针对第一种编译方式(目标输出到源代码所在目录)进行分析。

  uboot编译流程

  编译整体流程

  根据一、2生成的文件说明可知简单流程如下:

  (1)各目录下built-in.o的生成

  源文件、代码文件编译、汇编目标文件同目录目标文件连接built-in目标文件

  (2)由所有built-in.o以u-boot.lds为连接脚本通过连接来生成u-boot

  built-in目标文件以u-boot.lds为连接脚本进行统一连接u-boot

  (3)由u-boot生成u-boot-nodtb.bin

  u-bootobjcopy动作去掉符号信息表u-boot-nodtb.bin

  (4)由生成uboot的dtb文件

  dts文件dtc编译、打包dtb文件u-boot.dtb

  (5)由u-boot-nodtb.bin和u-boot.dtb生成u-boot-dtb.bin

  u-boot-nodtb.bin和u-boot.dtb追加整合两个文件u-boot-dtb.bin

  (6)由u-boot-dtb.bin复制生成u-boot.bin

  u-boot-dtb.bin复制u-boot.bin

  U-Boot配置、编译、连接过程

  U-Boot开头有一些跟主机软硬件环境相关的代码,在每次执行make命令时这些代码都被执行一次。

  1. U-Boot配置过程

  (1)定义主机系统架构

  HOSTARCH := $(shell uname -m | \

  sed-e s/i.86/i386/ \

  -e s/sun4u/sparc64/ \

  -e s/arm.*/arm/ \

  -e s/sa110/arm/ \

  -e s/powerpc/ppc/ \

  -e s/ppc64/ppc/ \

  -e s/macppc/ppc/)

  “sed –e”表示后面跟的是一串命令脚本,而表达式“s/abc/def/”表示要从标准输入中,查找到内容为“abc”的,然后替换成“def”。其中“abc”表达式用可以使用“。”作为通配符。

  命令“uname –m”将输出主机CPU的体系架构类型。作者的电脑使用Intel Core2系列的CPU,因此“uname–m”输出“i686”。“i686”可以匹配命令“sed -e s/i.86/i386/”中的“i.86”,因此在作者的机器上执行Makefile,HOSTARCH将被设置成“i386” 。

  (2)定义主机操作系统类型

  HOSTOS := $(shell uname -s | tr‘[:upper:]’ ‘[:lower:]’ | \

  sed -e ‘s/cygwin.*/cygwin/’)

  “uname –s”输出主机内核名字,作者使用Linux发行版Ubuntu9.10,因此“uname –s”结果是“Linux”。“tr ‘[:upper:]’ ‘[:lower:]’”作用是将标准输入中的所有大写字母转换为响应的小写字母。因此执行结果是将HOSTOS设置为“linux”。

  (3)定义执行shell脚本的shell

  # Set shell to bash if possible, otherwisefall back to sh

  SHELL := $(shell if [ -x“

  BASH”];thenecho

  BASH; \

  elseif [ -x /bin/bash ]; then echo /bin/bash; \

  elseecho sh; fi; fi)

  “$$BASH”的作用实质上是生成了字符串“$BASH”(前一个$号的作用是指明第二个$是普通的字符)。若执行当前Makefile的shell中定义了“$BASH”环境变量,且文件“$BASH”是可执行文件,则SHELL的值为“$BASH”。否则,若“/bin/bash”是可执行文件,则SHELL值为“/bin/bash”。若以上两条都不成立,则将“sh”赋值给SHELL变量。

  由于作者的机器安装了bash shell,且shell默认环境变量中定义了“$BASH”,因此SHELL 被设置为$BASH 。

  (4)设定编译输出目录

  ifdef O

  ifeq (“$(origin O)”,“command line”)

  BUILD_DIR := $(O)

  endif

  endif

  函数$( origin, variable) 输出的结果是一个字符串,输出结果由变量variable定义的方式决定,若variable在命令行中定义过,则origin函数返回值为“command line”。假若在命令行中执行了“exportBUILD_DIR=/tmp/build”的命令,则“$(origin O)”值为“command line”,而BUILD_DIR被设置为“/tmp/build”。

  ifneq ($(BUILD_DIR),)

  saved-output := $(BUILD_DIR)

  # Attempt to create a output directory.

  $(shell [ -d ${BUILD_DIR} ] || mkdir -p${BUILD_DIR})

  若${BUILD_DIR}表示的目录没有定义,则创建该目录。

  # Verify if it was successful.

  BUILD_DIR := $(shell cd $(BUILD_DIR)&& /bin/pwd)

  $(if $(BUILD_DIR),,$(error output directory“$(saved-output)” does not exist))

  endif # ifneq ($(BUILD_DIR),)

  若$(BUILD_DIR)为空,则将其赋值为当前目录路径(源代码目录)。并检查$(BUILD_DIR)目录是否存在。

  OBJTREE :=$(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))

  SRCTREE :=$(CURDIR)

  TOPDIR :=$(SRCTREE)

  LNDIR :=$(OBJTREE)

  … …

  MKCONFIG :=$(SRCTREE)/mkconfig

  … …

  ifneq ($(OBJTREE),$(SRCTREE))

  obj := $(OBJTREE)/

  src := $(SRCTREE)/

  else

  obj :=

  src :=

  endif

  CURDIR变量指示Make当前的工作目录,由于当前Make在U-Boot顶层目录执行Makefile,因此CURDIR此时就是U-Boot顶层目录。

  执行完上面的代码后, SRCTREE,src变量就是U-Boot代码顶层目录,而OBJTREE,obj变量就是输出目录,若没有定义BUILD_DIR环境变量,则SRCTREE,src变量与OBJTREE,obj变量都是U-Boot源代码目录。而MKCONFIG则表示U-Boot根目录下的mkconfig脚本。

技术专区

  • mybatis动态sql详解
  • 用VHDL语言设计数据传输系统中的HDB3编码器
  • 裸机程序如何驱动硬件?看前辈是怎么说的
  • 应用面向对象编程SoC原则的典型示例
  • 嵌入式开发之java常用开发工具介绍
  • uboot编译过程详细分析已关闭评论
    A+
发布日期:2019年07月14日  所属分类:物联网