由於u-boot比較龐大,所以我們分開來分析,對於一個大型的項目我們想快速的了解其代碼架構和內容,最方便的方法就是分析Makefile,所以我們今天以三星的s3c2440來分析Makefile。我們今天通過對u-boot的分析要得到以下內容:
1. U-boot的入口
2. 鏈接地址
l U-boot配置過程分析
我們在編譯u-boot之前首先要進行u-boot的配置,以三星的s3c2440為例我們的配置命令是make smdk2440_config,所以我們在u-boot頂層的Makefile中搜索smdk2440_config我們可以輕松的找到下面代碼:
1 smdk2440_config : unconfig 2 @$(MKCONFIG) $(@:_config=) arm s3c24xx smdk2440 samsung s3c2440
從上面代碼我們可以確定當我們執行make smdk2440_config的時候其執行了@$(MKCONFIG) $(@:_config=) arm s3c24xx smdk2440 samsung s3c2440在Makefile中我們又可以找到MKCONFIG := $(SRCTREE)/mkconfig所以上面的命令就可以替換為mkconfig smdk2440 arm s3c24xx smdk2440 samsung s3c2440 也就是說我們執行make smdk2440_config的時候其就是執行上面的命令。
接下來我們打開mkconfig
1. 分析傳入的參數,確定開發板的名稱,由於我們沒有-- 、-a、-n參數所以下面代碼可以忽略。
1 APPEND=no # Default: Create new config file 2 BOARD_NAME=""# Name to print in make output 3 SETMMU="no"# use mmu in nand uboot,but do not use in mmc.bin 4 while[ $# -gt 0 ] ; do 5 case"$1" in 6 --) shift ;break;; 7 -a) shift ; APPEND=yes ;; 8 -n) shift ; BOARD_NAME="${1%%_config}"; shift ;; 9 *)break;; 10 esac 11 done 12 ["${BOARD_NAME}"]|| BOARD_NAME="$1"//如果BOARD_NAME已定義后面的代碼就不會執行,但是我們這里沒有定義,所以執行BOARD_NAME="$1",然而$1 就是smdk2440所以執行完后BOARD_NAME= smdk2440 13 [ $# -lt 4 ] && exit 1//參數的個數小於4退出 14 [ $# -gt 9 ] && exit 1//參數的個數大於9退出 15 echo "Configuring for ${BOARD_NAME} board which boot from $7 $8 $9..."
-
1 # 2 # Create link to architecture specific headers 3 # 4 if["$SRCTREE"!="$OBJTREE"]; then 5 mkdir -p ${OBJTREE}/include 6 mkdir -p ${OBJTREE}/include2 7 cd ${OBJTREE}/include2 8 rm -f asm 9 ln -s ${SRCTREE}/include/asm-$2 asm 10 LNPREFIX="../../include2/asm/" 11 cd ../include 12 rm -rf asm-$2 13 rm -f asm 14 mkdir asm-$2 15 ln -s asm-$2 asm 16 else 17 cd ./include 18 rm -f asm 19 ln -s asm-$2 asm 20 fi
上面的這個if [ "$SRCTREE" != "$OBJTREE" ] 然而我們在頂層的Makefile中可以找到
OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))//如果定義BUILD_DIR則OBJTREE就為BUILD_DIR沒定義就為$(CURDIR)
SRCTREE := $(CURDIR)
通過上面兩行代碼顯然我們可以知道OBJTREE和SRCTREE是相等的,所以執行else分支
-
1 cd ./include 2 rm -f asm 3 ln -s asm-$2 asm#就是ln –s asm-arm asm
1 1 rm -f asm-$2/arch //rm –f asm-arm/arch 2 2 if[-z "$6"-o "$6"="NULL"]; then//第6個參數為空或者為NULL顯然不成立執行else分支 3 3 ln -s ${LNPREFIX}arch-$3 asm-$2/arch 4 4 else 5 5 ln -s ${LNPREFIX}arch-$6 asm-$2/arch//因為LNPREFIX未定義所以該命令類似與ln –s arch- s3c2440 asm-arm/arch 6 6 fi
這里我們發現其建立的鏈接竟然不是連到arch- s3c2440的而是連到arch- s3c24xx的難道我們錯了?這個問題下面會解釋到。
1 # create link for s3c24xx SoC 2 if["$3"="s3c24xx"]; then 3 rm -f regs.h 4 ln -s $6.h regs.h//建立鏈接文件ln –s s3c2440.h regs.h 5 rm -f asm-$2/arch 6 ln -s arch-$3 asm-$2/arch//建立鏈接文件ln –s arch- s3c24xx asm-arm/arch 7 fi
上面的最后一個連接文件,也就是我們剛才為什么看到不正確的鏈接了,因為其在這里更改了指向。
-
1 # create link for s3c64xx SoC 2 if["$3"="s3c64xx"];then//我們的"$3"=”s3c24xx”的所以不執行 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 8 if["$2"="arm"];then 9 rm -f asm-$2/proc 10 ln -s ${LNPREFIX}proc-armv asm-$2/proc//建立鏈接文件ln -s proc-armv asm-arm/proc 11 fi
-
# create link for s3c64xx-mp SoC if["$3"="s3c64xx-mp"];then//不相等不執行 rm -f regs.h ln -s $6.h regs.h rm -f asm-$2/arch ln -s arch-$3 asm-$2/arch fi # # Create include file for Make # echo "ARCH = $2"> config.mk//輸出ARCH = arm到config.mk echo "CPU = $3">> config.mk//追加CPU = s3c24xx到 config.mk echo "BOARD = $4">> config.mk//追加BOARD = smdk2440 到 config.mk ["$5"]&&["$5"!="NULL"]&& echo "VENDOR = $5">> config.mk//我們第5個參數為samsung追加VENDOR =samsung 到 config.mk ["$6"]&&["$6"!="NULL"]&& echo "SOC = $6">> config.mk//我們第6個參數為s3c2440追加SOC = s3c2440 到 config.mk
-
1 # 2 # Create board specific header file 3 # 4 if["$APPEND"="yes"]// Append to existing config file在開始時我們定義APPEND=no所以執行else分支 5 then 6 echo >> config.h 7 else 8 > config.h // Create new config file創建一個config.h文件 9 fi 10 echo "/* Automatically generated - do not edit */">>config.h//追加/*Automatically generated -do not edit */到config.h 11 case $7 in//我們沒有第7個參數所以不執行 12 SD) 13 echo "#define FORLINX_BOOT_SD">> config.h 14 SETMMU="no" 15 ;; 16 NAND) 17 echo "#define FORLINX_BOOT_NAND">> config.h 18 SETMMU="yes" 19 ;; 20 *) 21 ;; 22 esac 23 case $8 in//我們沒有第8個參數所以不執行 24 ram128) 25 echo "#define FORLINX_BOOT_RAM128">> config.h 26 >../board/samsung/smdk6410/config.mk # clear file context 27 echo "ifndef TEXT_BASE">>../board/samsung/smdk6410/config.mk 28 if[ ${SETMMU}="yes"] 29 then 30 echo "TEXT_BASE = 0xC7E00000">>../board/samsung/smdk6410/config.mk 31 else 32 echo "TEXT_BASE = 0x57E00000">>../board/samsung/smdk6410/config.mk 33 fi 34 echo "endif">>../board/samsung/smdk6410/config.mk 35 ;; 36 ram256) 37 echo "#define FORLINX_BOOT_RAM256">> config.h 38 >../board/samsung/smdk6410/config.mk # clear file context 39 echo "ifndef TEXT_BASE">>../board/samsung/smdk6410/config.mk 40 if[ ${SETMMU}="yes"] 41 then 42 echo "TEXT_BASE = 0xCFE00000">>../board/samsung/smdk6410/config.mk 43 else 44 echo "TEXT_BASE = 0x5FE00000">>../board/samsung/smdk6410/config.mk 45 fi 46 echo "endif">>../board/samsung/smdk6410/config.mk 47 ;; 48 *) 49 ;; 50 esac 51 if["$9"="hdmi"];then//我們沒有第9個參數所以不執行 52 echo "#define FORLINX_LCDOUT_HDMI">> config.h 53 fi 54 echo "#include <configs/$1.h>">>config.h//追加#include <configs/ smdk2440.h>到 config.h 55 56 exit 0
在我們的配置過程中我們主要有一下工作;
1. 創建於開發板相關的頭文件的鏈接
2. 創建include/config.mk文件
3. 創建include/config.h頭文件
l U-boot編譯
在我們執行make的時候其將會生成第一個目標也就是all,以all為突破口我們找到all又依賴於$(ALL)而ALL又等於
-
1 ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)
而u-boot.srec u-boot.bin又依賴於u-boot
-
$(obj)u-boot: depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT) UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\ cd $(LNDIR)&& $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \ --start-group $(__LIBS)--end-group $(PLATFORM_LIBS) \ -Map u-boot.map -o u-boot
通過展開編譯命令(當然有點麻煩)我們可以直接執行make然后找到和這條命令相似的命令,通過得到的命令我們可以順利的找到其在編譯過程中所用到的鏈接器腳本
-
1 OUTPUT_FORMAT("elf32-littlearm","elf32-littlearm","elf32-littlearm") 2 OUTPUT_ARCH(arm) 3 ENTRY(_start)//入口地址_start 4 SECTIONS 5 { 6 .=0x00000000; 7 .= ALIGN(4); 8 .text ://代碼段排布 9 { 10 cpu/s3c24xx/start.o (.text)//先是該代碼,最先運行 11 cpu/s3c24xx/s3c2440/cpu_init.o (.text) 12 *(.text) 13 } 14 .= ALIGN(4); 15 .rodata :{*(.rodata)} 16 .= ALIGN(4); 17 .data :{*(.data)} 18 .= ALIGN(4); 19 .got :{*(.got)} 20 .=.; 21 __u_boot_cmd_start =.; 22 .u_boot_cmd :{*(.u_boot_cmd)} 23 __u_boot_cmd_end =.; 24 .= ALIGN(4); 25 .mmudata :{*(.mmudata)} 26 .= ALIGN(4); 27 __bss_start =.; 28 .bss :{*(.bss)} 29 _end =.; 30 }
在cpu/s3c24xx/start.o我們可以輕松找到其所有代碼的入口
-
1 .globl _start 2 _start: 3 b reset 4 ldr pc, _undefined_instruction 5 ldr pc, _software_interrupt 6 ldr pc, _prefetch_abort 7 ldr pc, _data_abort 8 ldr pc, _not_used 9 ldr pc, _irq 10 ldr pc, _fiq
至此我們找到了正個程序的入口,但是其的鏈接地址又在什么地方呢?
我們在頂層的config.mk中找到
-
1 LDFLAGS +=-Bstatic-T $(LDSCRIPT)-Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)
其實整個程序的鏈接地址就在TEXT_BASE通過全局搜索我們在\board\samsung\smdk2440\config.mk 中找到TEXT_BASE = 0x30008000至此我們今天的任務完成。