[uboot] (第四章)uboot流程——uboot編譯流程


http://blog.csdn.net/ooonebook/article/details/53000893

以下例子都以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分離表一

所以在uboot的編譯中,和spl的最大區別是還要編譯dtb。 (前面我們將的spl是沒有使用dtb的,當然好像也可以使用dtb,只是我沒有試過)。

1、編譯方法

在project X項目中,所有鏡像,包括uboot、kernel、rootfs都是放在build目錄下進行編譯的。具體去參考該項目build的Makefile的實現。 
假設config已經配置完成,在build編譯命令如下:

make uboot

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)_defconfig make -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.

最終,相當於進入了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

其中,arch、common、dts、include、board、drivers、fs等等目錄是對應代碼的編譯目錄,各個目錄下都會生成相應的built.o,是由同目錄下的目標文件連接而成。 
重點說一下以下幾個文件:

文件 說明
u-boot 初步鏈接后得到的uboot文件
u-boot-nodtb.bin 在u-boot的基礎上,經過objcopy去除符號表信息之后的可執行程序
u-boot.dtb dtb文件
u-boot-dtb.bin 將u-boot-nodtb.bin和u-boot.dtb打包在一起的文件
u-boot.bin 在需要dtb的情況下,直接由u-boot-dtb.bin復制而來,也就是編譯u-boot的最終目標
u-boot.lds uboot的連接腳本
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

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(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。

 

如上述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__命令

如上,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

 

將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

 

可以看出上述是一條連接命令,以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

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)流程。 
關於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

 

可以觀察到是一堆目標文件的路徑。這些目標文件最終都要被連接到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.o libs-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.o u-boot-main := $(libs-y)

那么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

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)=目標文件

對每一個目標文件依次執行make $(build)=目標文件 
$(build)定義如下: 
project-x/u-boot/scripts/Kbuild.include

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

以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文件編譯而來,這里就不說明了。

后面來看目標文件的編譯流程 
./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(1)流程。


  • (6)u-boot.lds依賴關系 
    這里主要是為了找到一個匹配的連接文件。
u-boot.lds: $(LDSCRIPT) prepare FORCE
        $(call if_changed_dep,cpp_lds) ifndef LDSCRIPT ifeq ($(wildcard $(LDSCRIPT)),) LDSCRIPT := $(srctree)/board/$(BOARDDIR)/u-boot.lds endif ifeq ($(wildcard $(LDSCRIPT)),) LDSCRIPT := $(srctree)/$(CPUDIR)/u-boot.lds endif ifeq ($(wildcard $(LDSCRIPT)),) LDSCRIPT := $(srctree)/arch/$(ARCH)/cpu/u-boot.lds endif endif ## 也就是說依次從board/板級目錄、cpudir目錄、arch/架構/cpu/目錄下去搜索u-boot.lds文件。 ## 例如,tiny210(s5vp210 armv7)最終會在./arch/arm/cpu/下搜索到u-boot.lds

綜上,最終指定了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-y targets += $(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 ## 后續會使用到這個偽目標

接下來看一下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

在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.dtb include scripts/Makefile.lib ## 包含了scripts/Makefile.lib,在編譯dts的時候會用到

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

$(obj)/%.dtb: $(src)/%.dts FORCE
        $(call if_changed_dep,dtc) ## 使用了通配符的方式 ## 這樣就通過dtc對dts編譯生成了dtb文件

對應於上述二、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

連接標識如下:

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

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

‘-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
  • 3、連接腳本 
    在二、2(6)中說明。 
    u-boot/arch/arm/cpu/u-boot.lds
u-boot.lds: $(LDSCRIPT) prepare FORCE
        $(call if_changed_dep,cpp_lds) ifndef LDSCRIPT ifeq ($(wildcard $(LDSCRIPT)),) LDSCRIPT := $(srctree)/board/$(BOARDDIR)/u-boot.lds endif ifeq ($(wildcard $(LDSCRIPT)),) LDSCRIPT := $(srctree)/$(CPUDIR)/u-boot.lds endif ifeq ($(wildcard $(LDSCRIPT)),) LDSCRIPT := $(srctree)/arch/$(ARCH)/cpu/u-boot.lds endif endif

 

綜上,最終指定了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)); } }

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

.vectors是uboot鏈接腳本第一個鏈接的段,也就是_start被鏈接進來的部分,也負責鏈接異常中斷向量表 
先看一下代碼project-x/u-boot/arch/arm/lib/vectors.S

.globl _start
    .section ".vectors", "ax" @@ 定義在.vectors段中 _start: b reset ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _fiq .globl _undefined_instruction .globl _software_interrupt .globl _prefetch_abort .globl _data_abort .globl _not_used .globl _irq .globl _fiq @@ 定義了異常中斷向量表

通過“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, #32 23e00030 <_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} 

 

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

前面我們說過了在tiny210中把連接地址設置為0x23e00000。 
project-x/build/out/u-boot/spl/u-boot.map

Linker script and memory map

Address of section .text set to 0x23e00000 .text 0x23e00000 0x29b28 *(.__image_copy_start) .__image_copy_start 0x23e00000 0x0 arch/arm/lib/built-in.o 0x23e00000 __image_copy_start *(.vectors) .vectors 0x23e00000 0x300 arch/arm/lib/built-in.o 0x23e00000 _start 0x23e00020 _undefined_instruction 0x23e00024 _software_interrupt 0x23e00028 _prefetch_abort 0x23e0002c _data_abort 0x23e00030 _not_used 0x23e00034 _irq 0x23e00038 _fiq 0x23e00040 IRQ_STACK_START_IN *(.__image_copy_end) .__image_copy_end 0x23e36b78 0x0 arch/arm/lib/built-in.o *(.__rel_dyn_start) .__rel_dyn_start 0x23e36b78 0x0 arch/arm/lib/built-in.o *(.__rel_dyn_end) .__rel_dyn_end 0x23e3cbb8 0x0 arch/arm/lib/built-in.o 0x23e3cbb8 _image_binary_end = . *(.__bss_start) .__bss_start 0x23e36b78 0x0 arch/arm/lib/built-in.o 0x23e36b78 __bss_start .__bss_end 0x23e6b514 0x0 arch/arm/lib/built-in.o 0x23e6b514 __bss_end

 

重點關注 
* __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的編譯就完成了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM