4-uboot编译流程

2024-08-30 17:38
文章标签 编译 uboot 流程

本文主要是介绍4-uboot编译流程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

[uboot] (第四章)uboot流程——uboot编译流程

以下例子都以project X项目tiny210(s5pv210平台,armv7架构)为例

[uboot] uboot流程系列: 
[project X] tiny210(s5pv210)上电启动流程(BL0-BL2) 
[project X] tiny210(s5pv210)从存储设备加载代码到DDR 
[uboot] (第一章)uboot流程——概述 
[uboot] (第二章)uboot流程——uboot-spl编译流程 
[uboot] (第三章)uboot流程——uboot-spl代码流程 
[uboot] (第四章)uboot流程——uboot编译流程 
[uboot] (番外篇)global_data介绍 
[uboot] (番外篇)uboot relocation介绍

建议先看《[project X] tiny210(s5pv210)上电启动流程(BL0-BL2)》,根据例子了解一下上电之后的BL0\BL1\BL2阶段,以及各个阶段的运行位置,功能。 
建议先看《[uboot] (第二章)uboot流程——uboot-spl编译流程》,其编译流程基本上是类似的。最大区别在于dtb的编译。

=================================================================================

一、uboot编译和生成文件

0、说明

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

CONFIG_OF_CONTROL=y
// 用于表示是否使用了dtb的方式CONFIG_OF_SEPARATE=y
// 是否将dtb和uboot分离表一
  • 1
  • 2
  • 3
  • 4
  • 5

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

1、编译方法

在project X项目中,所有镜像,包括uboot、kernel、rootfs都是放在build目录下进行编译的。具体去参考该项目build的Makefile的实现。 
假设config已经配置完成,在build编译命令如下:

make uboot
  • 1

Makefile中对应的命令如下: 
project-x/build/Makefile

BUILD_DIR=$(shell pwd)
OUT_DIR=$(BUILD_DIR)/out
UBOOT_OUT_DIR=$(OUT_DIR)/u-boot
UBOOT_DIR=$(BUILD_DIR)/../u-boot
uboot:mkdir -p $(UBOOT_OUT_DIR)make -C $(UBOOT_DIR) CROSS_COMPILE=$(CROSS_COMPILE) KBUILD_OUTPUT=$(UBOOT_OUT_DIR) $(BOARD_NAME)_defconfigmake -C $(UBOOT_DIR) CROSS_COMPILE=$(CROSS_COMPILE) KBUILD_OUTPUT=$(UBOOT_OUT_DIR)
## -C $(UBOOT_DIR) 指定了要在../uboot,也就是uboot的代码根目录下执行make
## CROSS_COMPILE=$(CROSS_COMPILE) 指定了交叉编译器
## KBUILD_OUTPUT=$(UBOOT_OUT_DIR) 指定了最终编译的输出目录是build/out/u-boot.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

最终,相当于进入了uboot目录执行了make动作。

2、生成文件

最终编译完成之后,会在project-x/build/out/u-boot下生成如下文件:

arch   common   dts       include   net         tools       u-boot.cfg      u-boot.lds        u-boot.srec
board  disk     examples  lib       scripts  System.map  u-boot      u-boot.dtb      u-boot.map        u-boot.sym
cmd    drivers  fs        Makefile  source   test        u-boot.bin  u-boot-dtb.bin  u-boot-nodtb.bin
  • 1
  • 2
  • 3

其中,arch、common、dts、include、board、drivers、fs等等目录是对应代码的编译目录,各个目录下都会生成相应的built.o,是由同目录下的目标文件连接而成。 
重点说一下以下几个文件:

文件说明
u-boot初步链接后得到的uboot文件
u-boot-nodtb.bin在u-boot的基础上,经过objcopy去除符号表信息之后的可执行程序
u-boot.dtbdtb文件
u-boot-dtb.bin将u-boot-nodtb.bin和u-boot.dtb打包在一起的文件
u-boot.bin在需要dtb的情况下,直接由u-boot-dtb.bin复制而来,也就是编译u-boot的最终目标
u-boot.ldsuboot的连接脚本
System.map连接之后的符号表文件
u-boot.cfg由uboot配置生成的文件

二、uboot编译流程

1、编译整体流程

根据一、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

2、具体编译流程分析

我们直接从make uboot命令分析,也就是从uboot下的Makefile的依赖关系来分析整个编译流程。 
注意,这个分析顺序和上述的整体编译流程的顺序是反着的。

  • (1)入口分析 
    在project-x/u-boot/Makefile中
all:            $(ALL-y)
ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map u-boot.cfg binary_size_check
  • 1
  • 2

u-boot.bin就是我们的目标,所以后需要主要研究u-boot.bin的依赖关系。


  • (2)u-boot.bin的依赖关系 
    在project-x/u-boot/Makefile中
ifeq ($(CONFIG_OF_SEPARATE),y)
## CONFIG_OF_SEPARATE用于定义是否有DTB并且是否是和uboot分开编译的。
## tiny210是有定义这个宏的,所以走的是上面这路u-boot-dtb.bin: u-boot-nodtb.bin dts/dt.dtb FORCE$(call if_changed,cat)
## 由u-boot-nodtb.bin和dts/dt.dtb连接在一起,先生成u-boot-dtb.bin
## $(call if_changed,cat)会调用到cmd_cat函数,具体实现我们不分析了u-boot.bin: u-boot-dtb.bin FORCE$(call if_changed,copy)
## 直接将u-boot-dtb.bin复制为u-boot.bin
## $(call if_changed,copy)会调用到cmd_copy函数,具体实现我们不分析了else
u-boot.bin: u-boot-nodtb.bin FORCE$(call if_changed,copy)
endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

对应于上述二、1(5)流程和上述二、1(6)流程。 
后续有两个依赖关系要分析,分别是u-boot-nodtb.bin和dts/dt.dtb。 
u-boot-nodtb.bin依赖关系参考下述二、2(3)-2(6). 
dts/dt.dtb依赖关系参考下述二、2(7) 
其中u-boot-nodtb.bin的依赖关系和SPL的相当类似,可以先参考一下《[uboot] (第二章)uboot流程——uboot-spl编译流程》。


  • (3)u-boot-nodtb.bin的依赖关系 
    在project-x/u-boot/Makefile中
u-boot-nodtb.bin: u-boot FORCE$(call if_changed,objcopy)$(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE))$(BOARD_SIZE_CHECK)
## $(call if_changed,objcopy)表示当依赖文件发生变化时,将依赖文件经过objcopy处理之后得到目标文件。
## 也就是通过objcopy把u-boot的符号信息以及一些无用信息去掉之后,得到了u-boot-nodtb.bin。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

如上述Makefile代码u-boot-nodtb.bin依赖于u-boot,并且由u-boot经过objcopy操作之后得到。 
对应于上述二、1(3)流程.

  • (4)u-boot的依赖关系 
    在project-x/u-boot/Makefile中
u-boot: $(u-boot-init) $(u-boot-main) u-boot.lds FORCE$(call if_changed,u-boot__)
## $(call if_changed,u-boot__)来生成目标
## $(call if_changed,u-boot__)对应cmd_u-boot__命令
  • 1
  • 2
  • 3
  • 4

如上,u-boot依赖于$(u-boot-init) 、$(u-boot-main)和u-boot.lds,并且最终会调用cmd_u-boot__来生成u-boot。 
cmd_u-boot__实现如下 
project-x/u-boot/Makefile

      cmd_u-boot__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_u-boot) -o $@ \ -T u-boot.lds $(u-boot-init)                             \    --start-group $(u-boot-main) --end-group                 \    $(PLATFORM_LIBS) -Map u-boot.map
  • 1
  • 2
  • 3
  • 4

将cmd_u-boot__通过echo命令打印出来之后得到如下(拆分出来看的): 
project-x/u-boot/Makefile

/project-x/build/arm-none-linux-gnueabi-4.8/bin/arm-none-linux-gnueabi-ld-pie --gc-sections -Bstatic -Ttext 0x23E00000 -o u-boot 
-T u-boot.lds 
arch/arm/cpu/armv7/start.o 
--start-group 
arch/arm/cpu/built-in.o arch/arm/cpu/armv7/built-in.o arch/arm/lib/built-in.o arch/arm/mach-s5pc1xx/built-in.o board/samsung/common/built-in.o board/samsung/tiny210/built-in.o cmd/built-in.o common/built-in.o disk/built-in.o drivers/built-in.o drivers/dma/built-in.o drivers/gpio/built-in.o drivers/i2c/built-in.o drivers/mmc/built-in.o drivers/mtd/built-in.o drivers/mtd/onenand/built-in.o drivers/mtd/spi/built-in.o drivers/net/built-in.o drivers/net/phy/built-in.o drivers/pci/built-in.o drivers/power/built-in.o drivers/power/battery/built-in.o drivers/power/fuel_gauge/built-in.o drivers/power/mfd/built-in.o drivers/power/pmic/built-in.o drivers/power/regulator/built-in.o drivers/serial/built-in.o drivers/spi/built-in.o drivers/usb/common/built-in.o drivers/usb/dwc3/built-in.o drivers/usb/emul/built-in.o drivers/usb/eth/built-in.o drivers/usb/gadget/built-in.o drivers/usb/gadget/udc/built-in.o drivers/usb/host/built-in.o drivers/usb/musb-new/built-in.o drivers/usb/musb/built-in.o drivers/usb/phy/built-in.o drivers/usb/ulpi/built-in.o fs/built-in.o lib/built-in.o net/built-in.o test/built-in.o test/dm/built-in.o 
--end-group 
arch/arm/lib/eabi_compat.o 
-L /project-x/build/arm-none-linux-gnueabi-4.8/bin/../lib/gcc/arm-none-linux-gnueabi/4.8.3 -lgcc 
-Map u-boot.map
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

可以看出上述是一条连接命令,以u-boot.lds为链接脚本,把$(u-boot-init) 、$(u-boot-main)的指定的目标文件连接到u-boot中。 
并且已经指定输出文件为u-boot,连接脚本为u-boot.lds。 
连接很重要的东西就是连接标识,也就是 $(LD) $(LDFLAGS) $(LDFLAGS_u-boot)的定义。 
尝试把$(LD) \$(LDFLAGS) \$(LDFLAGS_u-boot)) 打印出来,结果如下:

LD=~/project-x/build/arm-none-linux-gnueabi-4.8/bin/arm-none-linux-gnueabi-ld
LDFLAGS=
LDFLAGS_u-boot=-pie --gc-sections -Bstatic -Ttext 0x23E00000
  • 1
  • 2
  • 3

LDFLAGS_u-boot定义如下

LDFLAGS_u-boot += -pie
LDFLAGS_u-boot += $(LDFLAGS_FINAL)
ifneq ($(CONFIG_SYS_TEXT_BASE),)
LDFLAGS_u-boot += -Ttext $(CONFIG_SYS_TEXT_BASE)
endif## 当指定CONFIG_SYS_TEXT_BASE时,会配置连接地址。在tiny210项目中,定义如下:
## ./include/configs/tiny210.h:52:#define CONFIG_SYS_TEXT_BASE            0x23E00000## $(LDFLAGS_FINAL)在如下几个地方定义了
## ./config.mk:19:LDFLAGS_FINAL :=
## ./config.mk:80:LDFLAGS_FINAL += -Bstatic
## ./arch/arm/config.mk:16:LDFLAGS_FINAL += --gc-sections
## 通过上述LDFLAGS_u-boot=-pie --gc-sections -Bstatic -Ttext 0x23E00000也就可以理解了
## 对应于上述二、1(2)流程。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

对应于上述二、1(2)流程。 
关于u-boot依赖的说明在(5)、(6)中继续介绍


  • (5)u-boot-init & u-boot-main依赖关系(代码是如何被编译的) 
    先看一下这两个值打印出来的
u-boot-init=arch/arm/cpu/armv7/start.o
u-boot-main= arch/arm/cpu/built-in.o arch/arm/cpu/armv7/built-in.o arch/arm/lib/built-in.o arch/arm/mach-s5pc1xx/built-in.o board/samsung/common/built-in.o board/samsung/tiny210/built-in.o cmd/built-in.o common/built-in.o disk/built-in.o drivers/built-in.o drivers/dma/built-in.o drivers/gpio/built-in.o drivers/i2c/built-in.o drivers/mmc/built-in.o drivers/mtd/built-in.o drivers/mtd/onenand/built-in.o drivers/mtd/spi/built-in.o drivers/net/built-in.o drivers/net/phy/built-in.o drivers/pci/built-in.o drivers/power/built-in.o drivers/power/battery/built-in.o drivers/power/fuel_gauge/built-in.o drivers/power/mfd/built-in.o drivers/power/pmic/built-in.o drivers/power/regulator/built-in.o drivers/serial/built-in.o drivers/spi/built-in.o drivers/usb/common/built-in.o drivers/usb/dwc3/built-in.o drivers/usb/emul/built-in.o drivers/usb/eth/built-in.o drivers/usb/gadget/built-in.o drivers/usb/gadget/udc/built-in.o drivers/usb/host/built-in.o drivers/usb/musb-new/built-in.o drivers/usb/musb/built-in.o drivers/usb/phy/built-in.o drivers/usb/ulpi/built-in.o fs/built-in.o lib/built-in.o net/built-in.o test/built-in.o test/dm/built-in.o
  • 1
  • 2

可以观察到是一堆目标文件的路径。这些目标文件最终都要被连接到u-boot中。 
u-boot-init & u-boot-main的定义如下代码: 
project-x/u-boot/Makefile

u-boot-init := $(head-y)
## head-y定义在如下位置
## ./arch/arm/Makefile:73:head-y := arch/arm/cpu/$(CPU)/start.olibs-y += lib/
libs-y += fs/
libs-y += net/
libs-y += disk/
libs-y += drivers/
libs-y += drivers/dma/
libs-y += drivers/gpio/
libs-y += drivers/i2c/
...
u-boot-dirs     := $(patsubst %/,%,$(filter %/, $(libs-y))) tools examples
## 过滤出路径之后,加上tools目录和example目录libs-y          := $(patsubst %/, %/built-in.o, $(libs-y)) 
## 先加上后缀built-in.ou-boot-main := $(libs-y)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

那么u-boot-init & u-boot-main是如何生成的呢? 
需要看一下对应的依赖如下:

$(sort $(u-boot-init) $(u-boot-main)): $(u-boot-dirs) ;
## 也就是说$(u-boot-init) $(u-boot--main)依赖于$(u-boot-dirs) 
## sort函数根据首字母进行排序并去除掉重复的。
##u-boot-dirs     := $(patsubst %/,%,$(filter %/, $(libs-y))) tools examples
## $(filter %/, $(libs-y)过滤出'/'结尾的字符串,注意,此时$(libs-y)的内容还没有加上built-in.o文件后缀
## patsubst去掉字符串中最后的'/'的字符。
## 最后u-boot-dirs打印出来如下:
## u-boot-dirs=arch/arm/cpu arch/arm/cpu/armv7 arch/arm/lib arch/arm/mach-s5pc1xx board/samsung/common board/samsung/tiny210 cmd common disk drivers drivers/dma drivers/gpio drivers/i2c drivers/mmc drivers/mtd drivers/mtd/onenand drivers/mtd/spi drivers/net drivers/net/phy drivers/pci drivers/power drivers/power/battery drivers/power/fuel_gauge drivers/power/mfd drivers/power/pmic drivers/power/regulator drivers/serial drivers/spi drivers/usb/common drivers/usb/dwc3 drivers/usb/emul drivers/usb/eth drivers/usb/gadget drivers/usb/gadget/udc drivers/usb/host drivers/usb/musb-new drivers/usb/musb drivers/usb/phy drivers/usb/ulpi fs lib net test test/dm tools examples
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

u-boot-dirs依赖规则如下:

PHONY += $(u-boot-dirs)
$(u-boot-dirs): prepare scripts$(Q)$(MAKE) $(build)=$@
## 依赖于prepare scripts
## prepare会导致prepare0、prepare1、prepare2、prepare3目标被执行,最终编译了tools目录下的东西,生成了一些工具
## 然后执行$(Q)$(MAKE) $(build)=$@
## 也就是会对每一个目标文件依次执行make \$(build)=目标文件
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

对每一个目标文件依次执行make $(build)=目标文件 
$(build)定义如下: 
project-x/u-boot/scripts/Kbuild.include

build := -f $(srctree)/scripts/Makefile.build obj
  • 1

以arch/arm/mach-s5pc1xx为例 
“$(MAKE) $(build)=$@”展开后格式如下 
make -f project-x/u-boot/scripts/Makefile.build obj=arch/arm/mach-s5pc1xx。

Makefile.build定义built-in.o、.lib以及目标文件.o的生成规则。这个Makefile文件生成了子目录的.lib、built-in.o以及目标文件.o。 
Makefile.build第一个编译目标是__build,如下

PHONY := __build
__build:
## 所以会直接编译执行__build这个目标,其依赖如下
__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \$(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \$(subdir-ym) $(always)@:
## 和built-in.o相关的是依赖builtin-target。下面来看这个依赖。
builtin-target := $(obj)/built-in.o
## 以obj=arch/arm/mach-s5pc1xx为例,那么builtin-target就是arch/arm/mach-s5pc1xx/built-in.o.## 依赖关系如下:
$(builtin-target): $(obj-y) FORCE$(call if_changed,link_o_target)
## $(call if_changed,link_o_target)将所有依赖连接到$(builtin-target),也就是相应的built-in.o中了。
## 具体实现可以查看cmd_link_o_target的实现,这里不详细说明了。## 那么$(obj-y)是从哪里来的呢?是从相应目录下的Makefile中include得到的。
# The filename Kbuild has precedence over Makefile
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
include $(kbuild-file)
## 当obj=arch/arm/mach-s5pc1xx时,得到对应的kbuild-file=u-boot/arch/arm/mach-s5pc1xx/Makefile
## 而在u-boot/arch/arm/mach-s5pc1xx/Makefile中定义了obj-y如下:
## obj-y   = cache.o
## obj-y   += reset.o
## obj-y   += clock.o
## 对应obj-y对应一些目标文件,由C文件编译而来,这里就不说明了。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

后面来看目标文件的编译流程 
./scripts/Makefile.build/scripts/Makefile.build

# Built-in and composite module parts
$(obj)/%.o: $(src)/%.c $(recordmcount_source) FORCE$(call cmd,force_checksrc)$(call if_changed_rule,cc_o_c)
## 调用cmd_cc_o_c对.c文件进行编译## cmd_cc_o_c格式如下:
cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<
## $(CC) $(c_flags)打印出来如下:
## CC=/home/disk3/xys/temp/project-x/build/arm-none-linux-gnueabi-4.8/bin/arm-none-linux-gnueabi-gcc
## c_flags=-Wp,-MD,arch/arm/mach-s5pc1xx/.clock.o.d -nostdinc -isystem /home/disk3/xys/temp/project-x/build/arm-none-linux-gnueabi-4.8/bin/../lib/gcc/arm-none-linux-gnueabi/4.8.3/include -Iinclude -I/home/disk3/xys/temp/project-x/u-boot/include -I/home/disk3/xys/temp/project-x/u-boot/arch/arm/include -include /home/disk3/xys/temp/project-x/u-boot/include/linux/kconfig.h -I/home/disk3/xys/temp/project-x/u-boot/arch/arm/mach-s5pc1xx -Iarch/arm/mach-s5pc1xx -D__KERNEL__ -D__UBOOT__ -Wall -Wstrict-prototypes -Wno-format-security -fno-builtin -ffreestanding -Os -fno-stack-protector -fno-delete-null-pointer-checks -g -fstack-usage -Wno-format-nonliteral -D__ARM__ -marm -mno-thumb-interwork -mabi=aapcs-linux -mword-relocations -fno-pic -mno-unaligned-access -ffunction-sections -fdata-sections -fno-common -ffixed-r9 -msoft-float -pipe -march=armv7-a -I/home/disk3/xys/temp/project-x/u-boot/arch/arm/mach-s5pc1xx/include -DKBUILD_STR(s)=#s -DKBUILD_BASENAME=KBUILD_STR(clock) -DKBUILD_MODNAME=KBUILD_STR(clock)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

对应于上述二、1(1)流程。


  • (6)u-boot.lds依赖关系 
    这里主要是为了找到一个匹配的连接文件。
u-boot.lds: $(LDSCRIPT) prepare FORCE$(call if_changed_dep,cpp_lds)
ifndef LDSCRIPTifeq ($(wildcard $(LDSCRIPT)),)LDSCRIPT := $(srctree)/board/$(BOARDDIR)/u-boot.ldsendififeq ($(wildcard $(LDSCRIPT)),)LDSCRIPT := $(srctree)/$(CPUDIR)/u-boot.ldsendififeq ($(wildcard $(LDSCRIPT)),)LDSCRIPT := $(srctree)/arch/$(ARCH)/cpu/u-boot.ldsendif
endif
## 也就是说依次从board/板级目录、cpudir目录、arch/架构/cpu/目录下去搜索u-boot.lds文件。
## 例如,tiny210(s5vp210 armv7)最终会在./arch/arm/cpu/下搜索到u-boot.lds
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

综上,最终指定了project-X/u-boot/arch/arm/cpu/u-boot.lds作为连接脚本。


  • (7)dts/dt.dtb依赖关系 
    该依赖关系的主要目的是生成dtb文件。 
    首先了解dts文件被放在了arch/arm/dts里面,并通过dts下的Makefile进行选择。 
    Makefile如下(剪切出一部分) 
    project-X/u-boot/arch/arm/dts/Makefile
dtb-$(CONFIG_S5PC110) += s5pc1xx-goni.dtb
dtb-$(CONFIG_EXYNOS5) += exynos5250-arndale.dtb \exynos5250-snow.dtb \exynos5250-spring.dtb \exynos5250-smdk5250.dtb \exynos5420-smdk5420.dtb \exynos5420-peach-pit.dtb \exynos5800-peach-pi.dtb \exynos5422-odroidxu3.dtb
dtb-$(CONFIG_TARGET_TINY210) += \s5pv210-tiny210.dtb
## 填充选择dtb-ytargets += $(dtb-y)# Add any required device tree compiler flags here
DTC_FLAGS +=
## 用于添加DTC编译选项PHONY += dtbs
dtbs: $(addprefix $(obj)/, $(dtb-y))@:
## 伪目标,其依赖为$(dtb-y)加上了源路径,如下
## arch/arm/dts/s5pc1xx-goni.dtb
## arch/arm/dts/s5pv210-tiny210.dtb
## 后续会使用到这个伪目标
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

接下来看一下dts/dt.dtb的依赖关系

dtbs dts/dt.dtb: checkdtc u-boot$(Q)$(MAKE) $(build)=dts dtbs
## checkdtc依赖用于检查dtc的版本
## u-boot一旦发生变化那么就重新编译一遍dtb
## 重点关注命令 $(Q)$(MAKE) $(build)=dts dtbs
## 展开来就是make -f ~/project-x/u-boot/scripts/Makefile.build obj=dts dtbs
## 我们相当于值在/scripts/Makefile.build下执行了目标dtbs
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

在scripts/Makefile.build中dtbs的目标定义在哪里呢 
project-X/u-boot/scripts/Makefile.build

kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
include $(kbuild-file)
## 把对应的Makefile路径包含了进去,也就是arch/arm/dts/Makefile
## 如前面所说,arch/arm/dts/Makefile中定义了dtbs的目标
## dtbs: $(addprefix $(obj)/, $(dtb-y))
##        @:
## 这里我们就找到对应的依赖关系了,依赖就是$(obj)/, $(dtb-y),举个例子就是arch/arm/dts/s5pv210-tiny210.dtbinclude scripts/Makefile.lib
## 包含了scripts/Makefile.lib,在编译dts的时候会用到
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

接下来就是$(obj)/, $(dtb-y)的依赖关系了 
project-X/u-boot/scripts/Makefile.lib

$(obj)/%.dtb: $(src)/%.dts FORCE$(call if_changed_dep,dtc)
## 使用了通配符的方式
## 这样就通过dtc对dts编译生成了dtb文件
  • 1
  • 2
  • 3
  • 4

对应于上述二、1(4)流程。


三、一些重点定义

  • 1、连接标志 
    在二、2(4)中说明。 
    连接命令在cmd_u-boot__中,如下
      cmd_u-boot__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_u-boot) -o $@ \ -T u-boot.lds $(u-boot-init)                             \    --start-group $(u-boot-main) --end-group                 \    $(PLATFORM_LIBS) -Map u-boot.map
  • 1
  • 2
  • 3
  • 4

连接标识如下:

LD=~/project-x/build/arm-none-linux-gnueabi-4.8/bin/arm-none-linux-gnueabi-ld
LDFLAGS=
LDFLAGS_u-boot=-pie --gc-sections -Bstatic -Ttext 0x23E00000
  • 1
  • 2
  • 3

LDFLAGS_u-boot定义如下

LDFLAGS_u-boot += -pie
LDFLAGS_u-boot += $(LDFLAGS_FINAL)
ifneq ($(CONFIG_SYS_TEXT_BASE),)
LDFLAGS_u-boot += -Ttext $(CONFIG_SYS_TEXT_BASE)
endif
  • 1
  • 2
  • 3
  • 4
  • 5

‘-o’指定了输出文件是u-boot,’-T’是指定了连接脚本是当前目录下的u-boot.lds, -Ttext指定了连接地址是CONFIG_SYS_TEXT_BASE。

  • 2、连接地址 
    在二、2(4)中说明。 
    CONFIG_SYS_TEXT_BASE指定了u-boot.bin的连接地址。这个地址也就是uboot的起始运行地址。 
    对于tiny210,其定义如下(可以进行修改) 
    /include/configs/tiny210.h
#define CONFIG_SYS_TEXT_BASE            0x23E00000
  • 1
  • 3、连接脚本 
    在二、2(6)中说明。 
    u-boot/arch/arm/cpu/u-boot.lds
u-boot.lds: $(LDSCRIPT) prepare FORCE$(call if_changed_dep,cpp_lds)
ifndef LDSCRIPTifeq ($(wildcard $(LDSCRIPT)),)LDSCRIPT := $(srctree)/board/$(BOARDDIR)/u-boot.ldsendififeq ($(wildcard $(LDSCRIPT)),)LDSCRIPT := $(srctree)/$(CPUDIR)/u-boot.ldsendififeq ($(wildcard $(LDSCRIPT)),)LDSCRIPT := $(srctree)/arch/$(ARCH)/cpu/u-boot.ldsendif
endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

综上,最终指定了project-X/u-boot/arch/arm/cpu/u-boot.lds作为连接脚本。

四、uboot链接脚本说明

1、连接脚本整体分析

相对比较简单,直接看连接脚本的内容project-x/u-boot/arch/arm/cpu/u-boot.lds 
前面有一篇分析连接脚本的文章了《[kernel 启动流程] 前篇——vmlinux.lds分析》,可以参考一下。 
参考如下,只提取了一部分:

ENTRY(_start)
//定义了地址为_start的地址,所以我们分析代码就是从这个函数开始分析的!!!. = 0x00000000;//以下定义文本段. = ALIGN(4);.text :{__image_copy_start = .;
//定义__image_copy_start这个标号地址为当前地址*(.vectors)
//所有目标文件的vectors段,也就是中断向量表连接到这里来CPUDIR/start.o (.text*)
//start.o文件的.text段链接到这里来*(.text*)
//所有目标文件的.text段链接到这里来}//以下定义只读数据段. = ALIGN(4);.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }//以下定义数据段. = ALIGN(4);.data : {*(.data*)
//所有目标文件的.data段链接到这里来}. = ALIGN(4);//以下定义u_boot_list段,具体功能未知. = ALIGN(4);.u_boot_list : {KEEP(*(SORT(.u_boot_list*)));}. = ALIGN(4);.image_copy_end :{*(.__image_copy_end)}
//定义__image_copy_end符号的地址为当前地址
//从__image_copy_start 到__image_copy_end的区间,包含了代码段和数据段。.rel_dyn_start :{*(.__rel_dyn_start)}
//定义__rel_dyn_start 符号的地址为当前地址,后续在代码中会使用到.rel.dyn : {*(.rel*)}.rel_dyn_end :{*(.__rel_dyn_end)}
//定义__rel_dyn_end 符号的地址为当前地址,后续在代码中会使用到
//从__rel_dyn_start 到__rel_dyn_end 的区间,应该是在代码重定向的过程中会使用到,后续遇到再说明。.end :{*(.__end)}_image_binary_end = .;
//定义_image_binary_end 符号的地址为当前地址// 以下定义堆栈段.bss_start __rel_dyn_start (OVERLAY) : {KEEP(*(.__bss_start));__bss_base = .;}.bss __bss_base (OVERLAY) : {*(.bss*). = ALIGN(4);__bss_limit = .;}.bss_end __bss_limit (OVERLAY) : {KEEP(*(.__bss_end));}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87

2、以下以.vectors段做说明,

.vectors是uboot链接脚本第一个链接的段,也就是_start被链接进来的部分,也负责链接异常中断向量表 
先看一下代码project-x/u-boot/arch/arm/lib/vectors.S

.globl _start.section ".vectors", "ax"
@@ 定义在.vectors段中_start:b   resetldr pc, _undefined_instructionldr pc, _software_interruptldr pc, _prefetch_abortldr pc, _data_abortldr pc, _not_usedldr pc, _irqldr pc, _fiq.globl  _undefined_instruction.globl  _software_interrupt.globl  _prefetch_abort.globl  _data_abort.globl  _not_used.globl  _irq.globl  _fiq
@@ 定义了异常中断向量表
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

通过“arm-none-linux-gnueabi-objdump -D u-boot > uboot_objdump.txt”进行反编译之后,得到了如下指令

23e00000 <__image_copy_start>:
23e00000:   ea0000be    b   23e00300 <reset>
23e00004:   e59ff014    ldr pc, [pc, #20]   ; 23e00020 <_undefined_instruction>                                                                                                              
23e00008:   e59ff014    ldr pc, [pc, #20]   ; 23e00024 <_software_interrupt>
23e0000c:   e59ff014    ldr pc, [pc, #20]   ; 23e00028 <_prefetch_abort>
23e00010:   e59ff014    ldr pc, [pc, #20]   ; 23e0002c <_data_abort>
23e00014:   e59ff014    ldr pc, [pc, #20]   ; 23e00030 <_not_used>
23e00018:   e59ff014    ldr pc, [pc, #20]   ; 23e00034 <_irq> 
23e0001c:   e59ff014    ldr pc, [pc, #20]   ; 23e00038 <_fiq> // 可以看出以下是异常终端向量表
23e00020 <_undefined_instruction>:
23e00020:   23e00060    mvncs   r0, #96 ; 0x60 
// 其中,23e00020存放的是未定义指令处理函数的地址,也就是23e00060
// 以下以此类推23e00024 <_software_interrupt>:
23e00024:   23e000c0    mvncs   r0, #192    ; 0xc0 23e00028 <_prefetch_abort>:
23e00028:   23e00120    mvncs   r0, #8 23e0002c <_data_abort>:
23e0002c:   23e00180    mvncs   r0, #3223e00030 <_not_used>:
23e00030:   23e001e0    mvncs   r0, #56 ; 0x38 23e00034 <_irq>:
23e00034:   23e00240    mvncs   r0, #4 23e00038 <_fiq>:
23e00038:   23e002a0    mvncs   r0, #10
23e0003c:   deadbeef    cdple   14, 10, cr11, cr13, cr15, {7}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

3、符号表中需要注意的符号

前面我们说过了在tiny210中把连接地址设置为0x23e00000。 
project-x/build/out/u-boot/spl/u-boot.map

Linker script and memory mapAddress of section .text set to 0x23e00000
.text           0x23e00000    0x29b28*(.__image_copy_start).__image_copy_start0x23e00000        0x0 arch/arm/lib/built-in.o0x23e00000                __image_copy_start*(.vectors).vectors       0x23e00000      0x300 arch/arm/lib/built-in.o0x23e00000                _start0x23e00020                _undefined_instruction0x23e00024                _software_interrupt0x23e00028                _prefetch_abort0x23e0002c                _data_abort0x23e00030                _not_used0x23e00034                _irq0x23e00038                _fiq0x23e00040                IRQ_STACK_START_IN*(.__image_copy_end).__image_copy_end0x23e36b78        0x0 arch/arm/lib/built-in.o*(.__rel_dyn_start).__rel_dyn_start0x23e36b78        0x0 arch/arm/lib/built-in.o*(.__rel_dyn_end).__rel_dyn_end0x23e3cbb8        0x0 arch/arm/lib/built-in.o0x23e3cbb8                _image_binary_end = .*(.__bss_start).__bss_start   0x23e36b78        0x0 arch/arm/lib/built-in.o0x23e36b78                __bss_start.__bss_end     0x23e6b514        0x0 arch/arm/lib/built-in.o0x23e6b514                __bss_end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

重点关注 
* __image_copy_start & __image_copy_end 
界定了代码空间的位置,用于重定向代码的时候使用,在uboot relocate的过程中,需要把这部分拷贝到uboot的新的地址空间中,后续在新地址空间中运行。 
具体可以参考《[uboot] (番外篇)uboot relocation介绍》。 
* _start 
在u-boot-spl.lds中ENTRY(_start),也就规定了代码的入口函数是_start。所以后续分析代码的时候就是从这里开始分析。 
* __rel_dyn_start & __rel_dyn_end 
由链接器生成,存放了绝对地址符号的label的地址,用于修改uboot relocate过程中修改绝对地址符号的label的值。 
具体可以参考《[uboot] (番外篇)uboot relocation介绍》。 
* _image_binary_end

综上,u-boot的编译就完成了。

这篇关于4-uboot编译流程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1121491

相关文章

Security OAuth2 单点登录流程

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一注销(single sign-off)就是指

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

maven 编译构建可以执行的jar包

💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」👈,「stormsha的知识库」👈持续学习,不断总结,共同进步,为了踏实,做好当下事儿~ 专栏导航 Python系列: Python面试题合集,剑指大厂Git系列: Git操作技巧GO

kubelet组件的启动流程源码分析

概述 摘要: 本文将总结kubelet的作用以及原理,在有一定基础认识的前提下,通过阅读kubelet源码,对kubelet组件的启动流程进行分析。 正文 kubelet的作用 这里对kubelet的作用做一个简单总结。 节点管理 节点的注册 节点状态更新 容器管理(pod生命周期管理) 监听apiserver的容器事件 容器的创建、删除(CRI) 容器的网络的创建与删除

火语言RPA流程组件介绍--浏览网页

🚩【组件功能】:浏览器打开指定网址或本地html文件 配置预览 配置说明 网址URL 支持T或# 默认FLOW输入项 输入需要打开的网址URL 超时时间 支持T或# 打开网页超时时间 执行后后等待时间(ms) 支持T或# 当前组件执行完成后继续等待的时间 UserAgent 支持T或# User Agent中文名为用户代理,简称 UA,它是一个特殊字符串头,使得服务器

Windows环境利用VS2022编译 libvpx 源码教程

libvpx libvpx 是一个开源的视频编码库,由 WebM 项目开发和维护,专门用于 VP8 和 VP9 视频编码格式的编解码处理。它支持高质量的视频压缩,广泛应用于视频会议、在线教育、视频直播服务等多种场景中。libvpx 的特点包括跨平台兼容性、硬件加速支持以及灵活的接口设计,使其可以轻松集成到各种应用程序中。 libvpx 的安装和配置过程相对简单,用户可以从官方网站下载源代码

Golang test编译使用

创建文件my_test.go package testsimport "testing"func TestMy(t *testing.T) {t.Log("TestMy")} 通常用法: $ go test -v -run TestMy my_test.go=== RUN TestMyTestMy: my_test.go:6: TestMy--- PASS: TestMy (0.

UMI复现代码运行逻辑全流程(一)——eval_real.py(尚在更新)

一、文件夹功能解析 全文件夹如下 其中,核心文件作用为: diffusion_policy:扩散策略核心文件夹,包含了众多模型及基础库 example:标定及配置文件 scripts/scripts_real:测试脚本文件,区别在于前者倾向于单体运行,后者为整体运行 scripts_slam_pipeline:orb_slam3运行全部文件 umi:核心交互文件夹,作用在于构建真

C++/《C/C++程序编译流程》

程序的基本流程如图:   1.预处理        预处理相当于根据预处理指令组装新的C/C++程序。经过预处理,会产生一个没有宏定义,没有条件编译指令,没有特殊符号的输出文件,这个文件的含义同原本的文件无异,只是内容上有所不同。 读取C/C++源程序,对其中的伪指令(以#开头的指令)进行处理将所有的“#define”删除,并且展开所有的宏定义处理所有的条件编译指令,如:“#if”、“

编译linux内核出现 arm-eabi-gcc: error: : No such file or directory

external/e2fsprogs/lib/ext2fs/tdb.c:673:29: warning: comparison between : In function 'max2165_set_params': -。。。。。。。。。。。。。。。。。。 。。。。。。。。。。。。。 。。。。。。。。 host asm: libdvm <= dalvik/vm/mterp/out/Inte