4-uboot编译流程
[uboot] (第四章)uboot流程——uboot編譯流程
2016年11月01日 21:23:36閱讀數:2983以下例子都以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
- 1
- 2
- 3
- 4
- 5
所以在uboot的編譯中,和spl的最大區別是還要編譯dtb。?(前面我們將的spl是沒有使用dtb的,當然好像也可以使用dtb,只是我沒有試過)。
1、編譯方法
在project X項目中,所有鏡像,包括uboot、kernel、rootfs都是放在build目錄下進行編譯的。具體去參考該項目build的Makefile的實現。?
假設config已經配置完成,在build編譯命令如下:
- 1
Makefile中對應的命令如下:?
project-x/build/Makefile
- 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.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的生成
(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.bin2、具體編譯流程分析
我們直接從make uboot命令分析,也就是從uboot下的Makefile的依賴關系來分析整個編譯流程。?
注意,這個分析順序和上述的整體編譯流程的順序是反著的。
- (1)入口分析?
在project-x/u-boot/Makefile中
- 1
- 2
u-boot.bin就是我們的目標,所以后需要主要研究u-boot.bin的依賴關系。
- (2)u-boot.bin的依賴關系?
在project-x/u-boot/Makefile中
- 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中
- 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中
- 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
- 1
- 2
- 3
- 4
將cmd_u-boot__通過echo命令打印出來之后得到如下(拆分出來看的):?
project-x/u-boot/Makefile
- 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)) 打印出來,結果如下:
- 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依賴關系(代碼是如何被編譯的)?
先看一下這兩個值打印出來的
- 1
- 2
可以觀察到是一堆目標文件的路徑。這些目標文件最終都要被連接到u-boot中。?
u-boot-init & u-boot-main的定義如下代碼:?
project-x/u-boot/Makefile
- 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是如何生成的呢??
需要看一下對應的依賴如下:
- 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
- 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,如下
- 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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
對應于上述二、1(1)流程。
- (6)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
- 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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
接下來就是$(obj)/,?$(dtb-y)的依賴關系了?
project-X/u-boot/scripts/Makefile.lib
- 1
- 2
- 3
- 4
對應于上述二、1(4)流程。
三、一些重點定義
- 1、連接標志?
在二、2(4)中說明。?
連接命令在cmd_u-boot__中,如下
- 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
- 1
- 3、連接腳本?
在二、2(6)中說明。?
u-boot/arch/arm/cpu/u-boot.lds
- 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分析》,可以參考一下。?
參考如下,只提取了一部分:
- 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
- 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
- 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编译流程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 3-uboot-spl代码流程
- 下一篇: 5-global_data介绍