u-boot的Makefile分析
U-BOOT是一個LINUX下的工程,在編譯之前必須已經安裝對應體系結構的交叉編譯環(huán)境,這里只針對ARM,編譯器系列軟件為arm-linux-*。
U-BOOT的下載地址: http://sourceforge.net/projects/u-boot
我下載的是1.1.6版本,一開始在FTP上下載了一個次新版,結果編譯失敗。1.1.6是沒問題的。
u-boot源碼結構
??? 解壓就可以得到全部u-boot源程序。在頂層目錄下有18個子目錄,分別存放和管理不同的源程序。這些目錄中所要存放的文件有其規(guī)則,可以分為3類。
??? 第1類目錄與處理器體系結構或者開發(fā)板硬件直接相關;
??? 第2類目錄是一些通用的函數或者驅動程序;
??? 第3類目錄是u-boot的應用程序、工具或者文檔。
u-boot的源碼頂層目錄說明
目??? 錄??? ??? ??? ??? 特??? 性??? ??? ??? ??? 解 釋 說 明
board??? ??? ??? ????? 平臺依賴??? ????? 存放電路板相關的目錄文件,
??? ??? ??? ??? ??? ??? ??? ??? ??? ??? 例如:RPXlite(mpc8xx)、
??? ??? ??? ??? ??? ??? ??? ??? ??? ??? smdk2410(arm920t)、
??? ??? ??? ??? ??? ??? ??? ??? ??? ??? sc520_cdp(x86) 等目錄
cpu??? ??? ??? ??? ??? 平臺依賴??? ????? 存放CPU相關的目錄文件
??? ??? ??? ??? ??? ??? ??? ??? ??? ??? 例如:mpc8xx、ppc4xx、
??? ??? ??? ??? ??? ??? ??? ??? ??? ??? arm720t、arm920t、 xscale、i386等目錄
lib_ppc??? ??? ??? ??? 平臺依賴??? ????? 存放對PowerPC體系結構通用的文件,
??? ??? ??? ??? ??? ??? ??? ??? ??? ??? 主要用于實現PowerPC平臺通用的函數
lib_arm??? ??? ??? ??? 平臺依賴??? ??? ?? 存放對ARM體系結構通用的文件,
??? ??? ??? ??? ??? ??? ??? ??? ??? ???? 主要用于實現ARM平臺通用的函數
lib_i386??? ??? ?????? 平臺依賴??? ??? ?? 存放對X86體系結構通用的文件,
??? ??? ??? ??? ??? ??? ??? ??? ??? ???? 主要用于實現X86平臺通用的函數
include??? ??? ??? ??? 通用??? ??? ??? ??? 頭文件和開發(fā)板配置文件,
??? ??? ??? ??? ??? ??? ??? ??? ??? ????? 所有開發(fā)板的配置文件都在configs目錄下
common?????????????? 通用??? ??? ??? ??? 通用的多功能函數實現
lib_generic??? ??? ??? 通用??? ??? ??? ??? 通用庫函數的實現
net??? ??? ??? ??? ??? 通用??? ??? ??? ??? 存放網絡的程序
fs??? ??? ??? ??? ???? 通用??? ??? ??? ??? 存放文件系統的程序
post??? ??? ??? ?????? 通用??? ??? ??? ??? 存放上電自檢程序
drivers??? ??? ??? ??? 通用??? ??? ??? ??? 通用的設備驅動程序,主要有以太網接口的驅動
disk??? ??? ??? ?????? 通用??? ??? ??? ??? 硬盤接口程序
rtc??? ??? ??? ??? ??? 通用??? ??? ??? ??? RTC的驅動程序
dtt??? ??? ??? ??? ??? 通用??? ??? ??? ??? 數字溫度測量器或者傳感器的驅動
examples??? ??? ?????? 應用例程??? ??? ???? 一些獨立運行的應用程序的例子,例如helloworld
tools??? ??? ??? ????? 工具??? ??? ??? ??? 存放制作S-Record或者u-boot格式的映像等工具,
??? ??? ??? ??? ??? ??? ??? ??? ??? ????? 例如mkimage
doc??? ??? ??? ??? ??? 文檔??? ??? ??? ??? 開發(fā)使用文檔
??? u-boot的源代碼包含對幾十種處理器、數百種開發(fā)板的支持??墒菍τ谔囟ǖ拈_發(fā)板,配置編譯過程只需要其中部分程序。這里具體以S3C2410 & arm920t處理器為例,具體分析S3C2410處理器和開發(fā)板所依賴的程序,以及u-boot的通用函數和工具。
編譯
以smdk_2410板為例,編譯的過程分兩部:
# make smdk2410_config
# make
頂層Makefile分析
要了解一個LINUX工程的結構必須看懂Makefile,尤其是頂層的,沒辦法,UNIX世界就是這么無奈,什么東西都用文檔去管理、配置。首先在這方面我是個新手,時間所限只粗淺地看了一些Makefile規(guī)則。
以smdk_2410為例,順序分析Makefile大致的流程及結構如下:
1) Makefile中定義了源碼及生成的目標文件存放的目錄,目標文件存放目錄BUILD_DIR可以通過make O=dir 指定。如果沒有指定,則設定為源碼頂層目錄。一般編譯的時候不指定輸出目錄,則BUILD_DIR為空。其它目錄變量定義如下:
#OBJTREE和LNDIR為存放生成文件的目錄,TOPDIR與SRCTREE為源碼所在目錄
OBJTREE??:= $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))
SRCTREE??:= $(CURDIR)
TOPDIR??:= $(SRCTREE)
LNDIR??:= $(OBJTREE)
export?TOPDIR SRCTREE OBJTREE
2)定義變量MKCONFIG:這個變量指向一個腳本,即頂層目錄的mkconfig。
MKCONFIG?:= $(SRCTREE)/mkconfig
export MKCONFIG
在編譯U-BOOT之前,先要執(zhí)行
# make smdk2410_config
smdk2410_config是Makefile的一個目標,定義如下:
smdk2410_config?:?unconfig
?@$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0
?unconfig::
?@rm -f $(obj)include/config.h $(obj)include/config.mk \
??$(obj)board/*/config.tmp $(obj)board/*/*/config.tmp
顯然,執(zhí)行# make smdk2410_config時,先執(zhí)行unconfig目標,注意不指定輸出目標時,obj,src變量均為空,unconfig下面的命令清理上一次執(zhí)行make *_config時生成的頭文件和makefile的包含文件。主要是include/config.h 和include/config.mk文件。
然后才執(zhí)行命令
?@$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0
MKCONFIG 是頂層目錄下的mkcofig腳本文件,后面五個是傳入的參數。
對于smdk2410_config而言,mkconfig主要做三件事:
在include文件夾下建立相應的文件(夾)軟連接,
#如果是ARM體系將執(zhí)行以下操作:
#ln -s???? asm-arm??????? asm??#ln -s? arch-s3c24x0??? asm-arm/arch
#ln -s?? proc-armv??? ?? asm-arm/proc
生成Makefile包含文件include/config.mk,內容很簡單,定義了四個變量:
ARCH?? = arm
CPU??? = arm920t
BOARD? = smdk2410
SOC??? = s3c24x0
生成include/config.h頭文件,只有一行:
/* Automatically generated - do not edit */
#include "config/smdk2410.h"
mkconfig腳本文件的執(zhí)行至此結束,繼續(xù)分析Makefile剩下部分。
3)包含include/config.mk,其實也就相當于在Makefile里定義了上面四個變量而已。
4) 指定交叉編譯器前綴:
ifeq ($(ARCH),arm)#這里根據ARCH變量,指定編譯器前綴。
CROSS_COMPILE = arm-linux-
endif
5)包含config.mk:
#包含頂層目錄下的config.mk,這個文件里面主要定義了交叉編譯器及選項和編譯規(guī)則
# load other configuration
include $(TOPDIR)/config.mk
下面分析config.mk的內容:
@包含體系,開發(fā)板,CPU特定的規(guī)則文件:
ifdef?ARCH #指定預編譯體系結構選項
sinclude $(TOPDIR)/$(ARCH)_config.mk?# include architecture dependend rules
endif
ifdef?CPU #定義編譯時對齊,浮點等選項
sinclude $(TOPDIR)/cpu/$(CPU)/config.mk?# include? CPU?specific rules
endif
ifdef?SOC #沒有這個文件
sinclude $(TOPDIR)/cpu/$(CPU)/$(SOC)/config.mk?# include? SoC?specific rules
endififdef?BOARD #指定特定板子的鏡像連接時的內存基地址,重要!
sinclude $(TOPDIR)/board/$(BOARDDIR)/config.mk?# include board specific rules
endif@定義交叉編譯鏈工具
# Include the make variables (CC, etc...)
#
AS?= $(CROSS_COMPILE)as
LD?= $(CROSS_COMPILE)ld
CC?= $(CROSS_COMPILE)gcc
CPP?= $(CC) -E
AR?= $(CROSS_COMPILE)ar
NM?= $(CROSS_COMPILE)nm
STRIP?= $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
RANLIB?= $(CROSS_COMPILE)RANLIB@定義AR選項ARFLAGS,調試選項DBGFLAGS,優(yōu)化選項OPTFLAGS
預處理選項CPPFLAGS,C編譯器選項CFLAGS,連接選項LDFLAGS
LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS) #指定了起始地址TEXT_BASE
@指定編譯規(guī)則:
$(obj)%.s:?%.S
?$(CPP) $(AFLAGS) -o $@ $<
$(obj)%.?%.S
?$(CC) $(AFLAGS) -c -o $@ $<
$(obj)%.?%.c
?$(CC) $(CFLAGS) -c -o $@ $<
回到頂層makefile文件:
6)U-boot需要的目標文件。
OBJS? = cpu/$(CPU)/start.o # 順序很重要,start.o必須放第一位
7)需要的庫文件:
LIBS? = lib_generic/libgeneric.a
LIBS += board/$(BOARDDIR)/lib$(BOARD).a
LIBS += cpu/$(CPU)/lib$(CPU).a
ifdef SOC
LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a
endif
LIBS += lib_$(ARCH)/lib$(ARCH).a
LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a \
?fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a
LIBS += net/libnet.a
LIBS += disk/libdisk.a
LIBS += rtc/librtc.a
LIBS += dtt/libdtt.a
LIBS += drivers/libdrivers.a
LIBS += drivers/nand/libnand.a
LIBS += drivers/nand_legacy/libnand_legacy.a
LIBS += drivers/sk98lin/libsk98lin.a
LIBS += post/libpost.a post/cpu/libcpu.a
LIBS += common/libcommon.a
LIBS += $(BOARDLIBS)LIBS := $(addprefix $(obj),$(LIBS))
.PHONY : $(LIBS)根據上面的include/config.mk文件定義的ARCH、CPU、BOARD、SOC這些變量。硬件平臺依賴的目錄文件可以根據這些定義來確定。SMDK2410平臺相關目錄及對應生成的庫文件如下。
??? board/smdk2410/??????? :庫文件board/smdk2410/libsmdk2410.a
??? cpu/arm920t/????????????? :庫文件cpu/arm920t/libarm920t.a
??? cpu/arm920t/s3c24x0/ :??庫文件cpu/arm920t/s3c24x0/libs3c24x0.a
??? lib_arm/???????????????????? :?庫文件lib_arm/libarm.a
??? include/asm-arm/?????? :下面兩個是頭文件。
??? include/configs/smdk2410.h
8)最終生成的各種鏡像文件:
ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)
all:??$(ALL)
$(obj)u-boot.hex:?$(obj)u-boot
??$(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@$(obj)u-boot.srec:?$(obj)u-boot
??$(OBJCOPY) ${OBJCFLAGS} -O srec $< $@$(obj)u-boot.bin:?$(obj)u-boot
??$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
#這里生成的是U-boot 的ELF文件鏡像
$(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
分析一下最關鍵的u-boot ELF文件鏡像的生成:
?????????? @依賴目標depend :生成各個子目錄的.depend文件,.depend列出每個目標文件的依賴文件。生成方法,調用每個子目錄的make _depend。
depend dep:
??for dir in $(SUBDIRS) ; do $(MAKE) -C $$dir _depend ; done@依賴目標version:生成版本信息到版本文件VERSION_FILE中。
version:
??@echo -n "#define U_BOOT_VERSION "U-Boot " > $(VERSION_FILE); \
??echo -n "$(U_BOOT_VERSION)" >> $(VERSION_FILE); \
??echo -n $(shell $(CONFIG_SHELL) $(TOPDIR)/tools/setlocalversion \
??? $(TOPDIR)) >> $(VERSION_FILE); \
??echo """ >> $(VERSION_FILE)@偽目標SUBDIRS: 執(zhí)行tools ,examples ,post,post\cpu 子目錄下面的make文件。
SUBDIRS?= tools \
?? examples \
?? post \
?? post/cpu
.PHONY : $(SUBDIRS)$(SUBDIRS):
??$(MAKE) -C $@ all@依賴目標$(OBJS),即cpu/start.o
$(OBJS):
??$(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))@依賴目標$(LIBS),這個目標太多,都是每個子目錄的庫文件*.a ,通過執(zhí)行相應子目錄下的make來完成:
$(LIBS):
??$(MAKE) -C $(dir $(subst $(obj),,$@))?@依賴目標$(LDSCRIPT):
LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds
LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)對于smdk2410,LDSCRIPT即連接腳本文件是board/smdk2410/u-boot.lds,定義了連接時各個目標文件是如何組織的。內容如下:
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
?. = 0x00000000;?. = ALIGN(4);
?.text????:/*.text的基地址由LDFLAGS中-Ttext $(TEXT_BASE)指定*/
?{???????????????????? ?/*smdk2410指定的基地址為0x33f80000*/
?? cpu/arm920t/start.o?(.text)???????? /*start.o為首*/
?? *(.text)
?}?. = ALIGN(4);
?.rodata : { *(.rodata) }?. = ALIGN(4);
?.data : { *(.data) }?. = ALIGN(4);
?.got : { *(.got) }?. = .;
?__u_boot_cmd_start = .;
?.u_boot_cmd : { *(.u_boot_cmd) }
?__u_boot_cmd_end = .;?. = ALIGN(4);
?__bss_start = .;
?.bss : { *(.bss) }
?_end = .;
}@執(zhí)行連接命令:
cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
???--start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
???-Map u-boot.map -o u-boot其實就是把start.o和各個子目錄makefile生成的庫文件按照LDFLAGS連接在一起,生成ELF文件u-boot 和連接時內存分配圖文件u-boot.map。
9)對于各子目錄的makefile文件,主要是生成*.o文件然后執(zhí)行AR生成對應的庫文件。如lib_generic文件夾Makefile:
LIB?= $(obj)libgeneric.a
COBJS?= bzlib.o bzlib_crctable.o bzlib_decompress.o \
?? bzlib_randtable.o bzlib_huffman.o \
?? crc32.o ctype.o display_options.o ldiv.o \
?? string.o vsprintf.o zlib.oSRCS ?:= $(COBJS:.o=.c)
OBJS?:= $(addprefix $(obj),$(COBJS))$(LIB):?$(obj).depend $(OBJS) #項層Makefile執(zhí)行make libgeneric.a
?$(AR) $(ARFLAGS) $@ $(OBJS)
整個makefile剩下的內容全部是各種不同的開發(fā)板的*_config:目標的定義了。
概括起來,工程的編譯流程也就是通過執(zhí)行執(zhí)行一個make *_config傳入ARCH,CPU,BOARD,SOC參數,mkconfig根據參數將include頭文件夾相應的頭文件夾連接好,生成config.h。然后執(zhí)行make分別調用各子目錄的makefile 生成所有的obj文件和obj庫文件*.a.? 最后連接所有目標文件,生成鏡像。不同格式的鏡像都是調用相應工具由elf鏡像直接或者間接生成的。
剩下的工作就是分析U-Boot源代碼了。
評論
查看更多