五.linux開發之uboot移植(五)——uboot配置和編譯過程詳解
一.uboot主Makefile分析1
1、uboot version確定(Makefile的24-29行)
Makefile程式碼部分
(1)uboot的版本號分3個級別:
VERSION:主機板本號
PATCHLEVEL:次版本號,即補丁版本號
SUBLEVEL:再次版本號
EXTRAVERSION:另外附加的版本資訊
這4個用.分隔開共同構成了最終的版本號U_BOOT_VERSION ,這個變數記錄了Makefile中配置的版本號。
2、include/version_autogenerated.h檔案是編譯過程中自動生成的一個檔案,所以源目錄中沒有,但是編譯過後的uboot中就有了。它裡面的內容是一個巨集定義,巨集定義的值內容就是我們在Makefile中配置的uboot的版本號。
2、HOSTARCH和HOSTOS
- Makefile程式碼部分:
注:sed的替換功能
test = abcdefgabc
Test1 = \$(test) \| sed -e s/abc/123/
Test2 = \$(test) \| sed -e s/abc/123/g
\@echo \$(Test1 )
\@echo \$(Test2 )
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
結果:
123defabc
123def123
abc被替換成了123,如果不加字母g,結果就變成了只有第一個abc被替換
1、HOSTARCH這個名字:HOST是主機,就是當前在做開發用的這臺電腦就叫主機;ARCH是architecture(架構)的縮寫,表示CPU的架構。所以HOSTARCH就表示主機的CPU的架構。
2、直接在shell中執行uname -m得到i686,得到的值其實你當前執行這個命令的電腦的CPU的版本號。
3、shell中的|叫做管道,管道的作用就是把管道前面一個運算式的輸出作為後面一個的輸入再去做處理,最終的輸出才是我們整個式子的輸出。
4、這兩個環境變數是主機的作業系統和主機的CPU架構,得出後儲存備用,後面自然會用到。
uboot主Makefile分析2
1、靜默編譯(50-54行)
Makefile程式碼部分:
1、平時預設編譯時命令列會打印出來很多編譯資訊。但是有時候我們不希望看到這些編譯資訊,就後臺編譯即可。這就叫靜默編譯。
2、使用方法就是編譯時make -s,-s會作為MAKEFLAGS傳給Makefile,在50-54行這段程式碼作用下XECHO變數就會被變成空(預設等於echo),於是實現了靜默編譯。
2、2種編譯方法(原地編譯和單獨輸出資料夾編譯)
1、編譯複雜專案,Makefile提供2種編譯管理方法。預設情況下是當前資料夾中的.c檔案,編譯出來的.o檔案會放在同一資料夾下。這種方式叫原地編譯。原地編譯的好處就是處理起來簡單。
2、原地編譯有一些壞處:第一,汙染了原始檔目錄。第二的缺陷就是一套原始碼只能按照一種配置和編譯方法進行處理,無法同時維護2個或2個以上的配置編譯方式。
3、為了解決以上2種缺陷,uboot支援單獨輸出資料夾方式的編譯(linux kernel也支援,而且uboot的這種技術就是從linux kernel學習來的)。基本思路就是在編譯時另外指定一個輸出目錄,將來所有的編譯生成的.o檔案或生成的其他檔案全部丟到那個輸出目錄下去。原始碼目錄不做任何汙染,這樣輸出目錄就承載了本次配置編譯的所有結果。
(4)具體用法:預設的就是原地編譯。如果需要指定具體的輸出目錄編譯則有2種方式來指定輸出目錄。(具體參考Makefile 56-76行註釋內容)
第一種:make O=輸出目錄
第二種:export BUILD_DIR=輸出目錄 然後再make
如果兩個都指定了(既有BUILD_DIR環境變數存在,又有O=xx),則O=xx具有更高優先順序,聽他的。
(5)兩種編譯的實現程式碼在Makefile的78-123行,如下
3.uboot主Makefile分析3
1、OBJTREE、SRCTREE、TOPDIR(環境變數)
(1)OBJTREE:編譯出的.o檔案存放的目錄的根目錄。
在預設編譯下,OBJTREE等於當前目錄;
在O=xx編譯下,OBJTREE就等於我們設定的那個輸出目錄。
(2)SRCTREE: 原始碼目錄,其實就是原始碼的根目錄,也就是當前目錄。
總結:在預設編譯下,OBJTREE和SRCTREE相等;在O=xx這種編譯下OBJTREE和SRCTREE不相等。Makefile中定義這兩個變數,其實就是為了記錄編譯後的.o檔案往哪裡放,就是為了實現O=xx的這種編譯方式的。
2、MKCONFIG(Makefile的101行)
Makefile中定義的一個變數(在這裡定義,在後面使用),它的值就是我們原始碼根目錄下面的mkconfig。這個mkconfig是一個指令碼,這個指令碼就是uboot配置階段的配置指令碼。
3、include $(obj)include/config.mk(133行)
Makefile程式碼部分:
# load ARCH, BOARD, and CPU configuration
include $(obj)include/config.mk
export ARCH CPU BOARD VENDOR SOC
(1)include/config.mk不是原始碼自帶的(你在沒有編譯過的原始碼目錄下是找不到這個檔案的),要在配置過程(make x210_sd_config)中才會生成這個檔案。因此這個檔案的值和我們配置過程有關,是由配置過程根據我們的配置自動生成的。
(2)我們X210在iNand情況下配置生成的config.mk內容為:
**ARCH = arm**
**CPU = s5pc11x**
**BOARD = x210**
**VENDOR = samsung**
**SOC = s5pc110**
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
(3)我們在下一行(134行)export匯出了這5個變數作為環境變數。所以著兩行加起來其實就是為當前makefile定義了5個環境變數而已。之所以不直接給出這5個環境變數的值,是因為我們希望這5個值是可以被人很容易的、集中的配置的。
(4)這裡的配置值來自於2589行那裡的配置項。如果我們要更改這裡的某個配置值要到2589行那裡呼叫MKCONFIG指令碼傳參時的引數。
Makefile程式碼部分:
x210_sd_config : unconfig
\@$(MKCONFIG) $(\@:_config=) arm s5pc11x x210 samsung s5pc110
\@echo “TEXT_BASE = 0xc3e00000” > $(obj)board/samsung/x210/config.mk
4、ARCH CROSS_COMPILE
(1)接下來有2個很重要的環境變數。一個是ARCH,上面匯出的,值來自於我們的配置過程,它的值會影響後面的CROSS_COMPILE環境變數的值。ARCH的意義是定義當前編譯的目標CPU的架構。
(2)CROSS_COMPILE是定義交叉編譯工具鏈的字首的。定義這些字首是為了在後面用(用字首加上字尾來定義編譯過程中用到的各種工具鏈中的工具)。我們把字首和字尾分開還有一個原因就是:在不同CPU架構上的交叉編譯工具鏈,只是字首不一樣,字尾都是一樣的。因此定義時把字首和字尾分開,只需要在定義字首時區分各種架構即可實現可移植性。
(3)CROSS_COMPILE在136-182行來確定。CROSS_COMPILE是被ARCH所確定的,只要配置了ARCH=arm,那麼我們就只能在ARM的那個分支去設定CROSS_COMPILE的值。這個設定值只要能保證找到那個交叉編譯工具鏈即可,不一定非得是全路徑的,相對路徑也可以。(如果已經將工具鏈匯出到環境變數,並且設定了符號連結,這樣CROSS_COMPILE = arm-linux-就可以)
1. **Makefile程式碼部分: **
2. **ifndef CROSS_COMPILE **
3. **ifeq (\$(HOSTARCH),\$(ARCH)) **
4. **CROSS_COMPILE = **
5. **else **
6. **ifeq (\$(ARCH),ppc) **
7. **CROSS_COMPILE = ppc_8xx- **
8. **endif **
9. **ifeq (\$(ARCH),arm) **
10. **\#CROSS_COMPILE = arm-linux- **
11. **\#CROSS_COMPILE = /usr/local/arm/4.4.1-eabi-cortex-a8/usr/bin/arm-linux- **
12. **\#CROSS_COMPILE = /usr/local/arm/4.2.2-eabi/usr/bin/arm-linux- **
13. **CROSS_COMPILE = /usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi- **
14. **endif **
15. **ifeq (\$(ARCH),i386) **
16. **CROSS_COMPILE = i386-linux- **
17. **endif **
18. **ifeq (\$(ARCH),mips) **
19. **CROSS_COMPILE = mips_4KC- **
20. **endif **
21. **ifeq (\$(ARCH),nios) **
22. **CROSS_COMPILE = nios-elf- **
23. **endif **
24. **ifeq (\$(ARCH),nios2) **
25. **CROSS_COMPILE = nios2-elf- **
26. **endif **
27. **ifeq (\$(ARCH),m68k) **
28. **CROSS_COMPILE = m68k-elf- **
29. **endif **
30. **ifeq (\$(ARCH),microblaze) **
31. **CROSS_COMPILE = mb- **
32. **endif **
33. **ifeq (\$(ARCH),blackfin) **
34. **CROSS_COMPILE = bfin-uclinux- **
35. **endif **
36. **ifeq (\$(ARCH),avr32) **
37. **CROSS_COMPILE = avr32-linux- **
38. **endif **
39. **ifeq (\$(ARCH),sh) **
40. **CROSS_COMPILE = sh4-linux- **
41. **endif **
42. **ifeq (\$(ARCH),sparc) **
43. **CROSS_COMPILE = sparc-elf- **
44. **endif \# sparc **
45. **endif \# HOSTARCH,ARCH **
46. **endif \# CROSS_COMPILE **
47. **export CROSS_COMPILE **
- 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
- 88
- 89
- 90
- 91
- 92
- 93
(4)實際運用時,我們可以在Makefile中去更改設定CROSS_COMPILE的值,也可以在編譯時用make CROSS_COMPILE=xxxx來設定,而且編譯時傳參的方法可以覆蓋Makefile裡面的設定。
4.uboot主Makefile分析4
1、$(TOPDIR)/config.mk(主Makefile的185行)
2、編譯工具定義(config.mk 94-107行)
3、包含開發板配置專案(config.mk, 112行)
(1)autoconfig.mk檔案不是原始碼提供的,是配置過程自動生成的。
(2)這個檔案的作用就是用來指導整個uboot的編譯過程。
這個檔案的內容其實就是很多CONFIG_開頭的巨集(可以理解為變數),這些巨集/變數會影響我們uboot編譯過程的走向(原理就是條件編譯)。在uboot程式碼中有很多地方使用條件編譯進行編寫,這個條件編譯是用來實現可移植性的。(可以說uboot的原始碼在很大程度來說是拼湊起來的,同一個程式碼包含了各種不同開發板的適用程式碼,用條件編譯進行區別。)
(3)這個檔案不是憑空產生的,配置過程也是需要原材料來產生這個檔案的。原材料在原始碼目錄的inlcude/configs/xxx.h標頭檔案。(X210開發板中為include/configs/x210_sd.h)。這個h標頭檔案裡面全都是巨集定義,這些巨集定義就是我們對當前開發板的移植。每一個開發板的移植都對應這個目錄下的一個頭檔案,這個標頭檔案裡每一個巨集定義都很重要,這些配置的巨集定義就是我們移植uboot的關鍵所在。
5.uboot主Makefile分析5
1、連結指令碼(config.mk 142-149行)
(1)如果定義了CONFIG_NAND_U_BOOT巨集,則連結指令碼叫u-boot-nand.lds,如果未定義這個巨集則連結指令碼叫u-boot.lds。
(2)從字面意思分析,即可知:CONFIG_NAND_U_BOOT是在Nand版本情況下才使用的,我們使用的X210都是iNand版本的,因此這個巨集沒有的。
(3)實際在board\samsung\x210目錄下有u-boot.lds,這個就是連結指令碼。我們在分析uboot的編譯連結過程時就要考慮這個連結指令碼。
2、TEXT_BASE(config.mk 156-158行)
(1)Makefile中在配置X210開發板時,在board/samsung/x210目錄下生成了一個檔案config.mk,其中的內容就是:TEXT_BASE = 0xc3e00000相當於定義了一個變數。
(2)TEXT_BASE是將來我們整個uboot連結時指定的連結地址。因為uboot中啟用了虛擬地址對映,因此這個C3E00000地址就等於0x23E00000(也可能是33E00000具體地址要取決於uboot中做的虛擬地址對映關係)。
(3)回顧裸機中講的連結地址的問題,再想想dnw方式先下載x210_usb.bin然後再下載uboot.bin時為什麼第二個地址是23E00000.
6.uboot主Makefile分析6
1. Makefile程式碼部分:
2. ALL += \$(obj)u-boot.srec \$(obj)u-boot.bin \$(obj)System.map \$(U_BOOT_NAND) \$(U_BOOT_ONENAND) \$(obj)u-boot.dis
3. ifeq (\$(ARCH),blackfin)
4. ALL += \$(obj)u-boot.ldr
5. endif
6. all: \$(ALL)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
(1)291行出現了整個主Makefile中第一個目標all(也就是預設目標,我們直接在uboot根目錄下make其實就等於make all,就等於make這個目標)
(2)目標中有一些比較重要的。譬如:u-boot是最終編譯連結生成的elf格式的可執行檔案,
(3)unconfig字面意思來理解就是未配置。這個符號用來做為我們各個開發板配置目標的依賴。目標是當我們已經配置過一個開發板後再次去配置時還可以配置。
(4)我們配置開發板時使用:make x210_sd_config,因此分析x210_sd_config肯定是主Makefile中的一個目標。
7.uboot配置過程詳解1
(1)mkconfig指令碼的6個引數
$(\@:_config=) arm s5pc11x x210 samsung s5pc110
x210_sd_config裡的_config部分用空替換,得到:x210_sd,這就是第一個引數,所以:
\$1: x210_sd
\$2: arm
\$3: s5pc11x
\$4: x210
\$5: samsumg
\$6: s5pc110
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
所以,$# = 6
(2)第23行:[ “${BOARD_NAME}” ] || BOARD_NAME=”$1”
其實就是看BOARD_NAME變數是否有值,如果有值就維持不變;如果無值就給他賦值為$1,實際分析結果:BOARD_NAME=x210_sd
(3)第25行:[ $# -lt 4 ] && exit 1
如果$#小於4,則exit 1(mkconfig指令碼返回1)
(4)第26行:[ $# -gt 6 ] && exit 1
如果$#大於6,則也返回1.
所以:mkconfig指令碼傳參只能是4、5、6,如果大於6或者小於4都不行。
(5)從第33行到第118行,
(6)都是在建立符號連結。為什麼要建立符號連結?這些符號連結檔案的存在就是整個配置過程的核心,這些符號連結檔案(資料夾)的主要作用是給標頭檔案包含等過程提供指向性連線。根本目的是讓uboot具有可移植性。
uboot可移植性的實現原理:在uboot中有很多彼此平行的程式碼,各自屬於各自不同的架構/CPU/開發板,我們在具體到一個開發板的編譯時用符號連線的方式提供一個具體的名字的資料夾供編譯時使用。這樣就可以在配置的過程中通過不同的配置使用不同的檔案,就可以正確的包含正確的檔案。
建立的符號連結:
第一個:
1. **cd ./include **
2. **rm -f asm **
3. **ln -s asm-\$2 asm **
- 1
- 2
- 3
- 4
- 5
在include目錄下建立asm檔案,指向asm-arm。(46-48行)
**第二個:
**ln -s \${LNPREFIX}arch-\$3 asm-\$2/arch
在inlcude/asm-arm下建立一個arch檔案,指向include/asm-arm/arch-s5pc110
第三個:
1. **\# create link for s5pc1xx SoC **
2. **if [ "\$3" = "s5pc1xx" ] ; then **
3. ** rm -f regs.h **
4. ** ln -s \$6.h regs.h **
5. ** rm -f asm-\$2/arch **
6. ** ln -s arch-\$3 asm-\$2/arch **
7. **fi **
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
在include目錄下建立regs.h檔案,指向include/s5pc110.h
刪除第二個
在inlcude/asm-arm下建立一個arch檔案,指向include/asm-arm/arch-s5pc11x
第四個:在include/asm-arm下建立一個proc檔案,指向include/asm-arm/proc-armv
總結:一共建立了4個符號連結。這4個符號連結將來在寫程式碼過程中,標頭檔案包含時非常有用。譬如一個頭檔案包含可能是:#include \
8.uboot配置過程詳解2
1. **\# **
2. **\# Create include file for Make **
3. **\# **
4. **echo "ARCH = \$2" \> config.mk **
5. **echo "CPU = \$3" \>\> config.mk **
6. **echo "BOARD = \$4" \>\> config.mk **
7. **[ "\$5" ] && [ "\$5" != "NULL" ] && echo "VENDOR = \$5" \>\> config.mk **
8. **[ "\$6" ] && [ "\$6" != "NULL" ] && echo "SOC = \$6" \>\> config.mk **
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
(1)建立include/config.mk檔案(mkconfig檔案123-129行)
(2)建立include/config.mk檔案是為了讓主Makefile在第133行去包含的
(3)思考:uboot的配置和編譯過程的配合。編譯的時候需要ARCH=arm、CPU=xx等這些變數來指導編譯,配置的時候就是為編譯階段提供這些變數。那為什麼不在Makefile中直接定義這些變數去使用,而要在mkconfig指令碼中建立config.mk檔案然後又在Makefile中include這些檔案呢?(一方面是為了可移植性)
(4)理解這些指令碼時,時刻要注意自己當前所處的路徑。
(5)建立(預設情況)/追加(**make -a時追加**)include/config.h檔案(mkconfig檔案的134-141行)。
(6)**這個檔案裡面的內容就一行#include \
9.uboot的連結指令碼
1. /\*
2. \* (C) Copyright 2002
3. \* Gary Jennejohn, DENX Software Engineering, \<gj\@denx.de\>
4. \*
5. \* See file CREDITS for list of people who contributed to this
6. \* project.
7. \*
8. \* This program is free software; you can redistribute it and/or
9. \* modify it under the terms of the GNU General Public License as
10. \* published by the Free Software Foundation; either version 2 of
11. \* the License, or (at your option) any later version.
12. \*
13. \* This program is distributed in the hope that it will be useful,
14. \* but WITHOUT ANY WARRANTY; without even the implied warranty of
15. \* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16. \* GNU General Public License for more details.
17. \*
18. \* You should have received a copy of the GNU General Public License
19. \* along with this program; if not, write to the Free Software
20. \* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21. \* MA 02111-1307 USA
22. \*/
23. OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
24. /\*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")\*/
25. OUTPUT_ARCH(arm)
26. ENTRY(_start)
27. SECTIONS
28. {
29. . = 0x00000000;
30. . = ALIGN(4);
31. .text :
32. {
33. cpu/s5pc11x/start.o (.text)
34. cpu/s5pc11x/s5pc110/cpu_init.o (.text)
35. board/samsung/x210/lowlevel_init.o (.text)
36. cpu/s5pc11x/onenand_cp.o (.text)
37. cpu/s5pc11x/nand_cp.o (.text)
38. cpu/s5pc11x/movi.o (.text)
39. common/secure_boot.o (.text)
40. common/ace_sha1.o (.text)
41. cpu/s5pc11x/pmic.o (.text)
42. \*(.text)
43. }
44. . = ALIGN(4);
45. .rodata : { \*(.rodata) }
46. . = ALIGN(4);
47. .data : { \*(.data) }
48. . = ALIGN(4);
49. .got : { \*(.got) }
50. __u_boot_cmd_start = .;
51. .u_boot_cmd : { \*(.u_boot_cmd) }
52. __u_boot_cmd_end = .;
53. . = ALIGN(4);
54. .mmudata : { \*(.mmudata) }
55. . = ALIGN(4);
56. __bss_start = .;
57. .bss : { \*(.bss) }
58. _end = .;
59. }
60. \</gj\@denx.de\>
- 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
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
(1)uboot的連結指令碼和我們之前裸機中的連結指令碼並沒有本質區別,只是複雜度高一些,檔案多一些,使用到的技巧多一些。
(2)ENTRY(_start)用來指定整個程式的入口地址。所謂入口地址就是整個程式的開頭地址,可以認為就是整個程式的第一句指令。有點像C語言中的main。
(3)之前在裸機中告訴大家,指定程式的連結地址有2種方法:一種是在Makefile中ld的flags用-Ttext 0x20000000來指定;第二種是在連結指令碼的SECTIONS開頭用.=0x20000000來指定。兩種都可以實現相同效果。其實,這兩種技巧是可以共同配合使用的,也就是說既在連結指令碼中指定也在ld flags中用-Ttext來指定。兩個都指定以後以-Ttext指定的為準。
(4)uboot的最終連結起始地址就是在Makefile中用-Ttext 來指定的,具體參見2.4.5.2節,注意TEXT_BASE變數。最終來源是Makefile中配置對應的命令中,在make xxx_config時得到的。
(5)在程式碼段中注意檔案排列的順序。指定必須放在前面部分的那些檔案就是那些必須安排在前16KB內的檔案,這些檔案中的函式在前16KB會被呼叫。在後面第二部分(16KB之後)中呼叫的程式,前後順序就無所謂了。
(6)連結指令碼中除了.text .data .rodata .bss段等編譯工具自帶的段之外,編譯工具還允許我們自定義段。譬 uboot總的.u_boot_cmd段就是自定義段。自定義段很重要。