u-boot的Makefile分析

來自:湛藍思微 blog

U BOOT 是一個LINUX 下的工程,在編譯之前必須已經安裝對應體系結構的交叉編譯環境,這裏只針對ARM ,編譯器系列軟件爲arm-linux-*

U BOOT 的下載地址: h t t p ://sourceforge.net/projects/u-boot
我下載的是1.1.6 版本,一開始在FTP 上下載了一個次新版,結果編譯失敗。1.1.6 是沒問題的。

u-boot 源碼結構

   
解壓就可以得到全部u-boot 源程序。在頂層目錄下有18 個子目錄,分別存放和管理不同的源程序。這些目錄中所要存放的文件有其規則,可以分爲3 類。
   
1 類目錄與處理器體系結構或者開發板硬件直接相關;
   
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               
通用                頭文件和開發板配置文件,
                                         
所有開發板的配置文件都在configs 目錄下

common              
通用                通用的多功能函數實現
lib_generic           
通用                通用庫函數的實現
net                   
 通用                存放網絡的程序
fs                    
 通用                存放文件系統的程序
post                  
 通用                存放上電自檢程序
drivers               
  通用                通用的設備驅動程序,主要有以太網接口的驅動
disk                  
  通用                硬盤接口程序
rtc                   
  通用                RTC 的驅動程序
dtt                   
  通用                數字溫度測量器或者傳感器的驅動
examples              
應用例程             一些獨立運行的應用程序的例子,例如helloworld
tools                 
  工具                存放製作S-Record 或者u-boot 格式的映像等工具,
                                         
例如mkimage

doc                   
  文檔                開發使用文檔

    u-boot
的源代碼包含對幾十種處理器、數百種開發板的支持。可是對於特定的開發板,配置編譯過程只需要其中部分程序。這裏具體以S3C2410 & arm920t 處理器爲例,具體分析S3C2410 處理器和開發板所依賴的程序,以及u-boot 的通用函數和工具。



編譯

smdk_2410 板爲例,編譯的過程分兩部:

# make smdk2410_config
# make

頂層Makefile 分析

要了解一個LINUX 工程的結構必須看懂Makefile ,尤其是頂層的,沒辦法,UNIX 世界就是這麼無奈,什麼東西都用文檔去管理、配置。首先在這方面我是個新手,時間所限只粗淺地看了一些Makefile 規則。

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 之前,先要執行

# 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

顯然,執行# make smdk2410_config 時,先執行unconfig 目標,注意不指定輸出目標時,obj src 變量均爲空,unconfig 下面的命令清理上一 次執行make *_config 時生成的頭文件和makefile 的包含文件。主要是include/config.h include/config.mk 文件。

然後才執行命令

  @$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0
MKCONFIG
是頂層目錄下的mkcofig 腳本文件,後面五個是傳入的參數。

對於smdk2410_config 而言,mkconfig 主要做三件事:

include 文件夾下建立相應的文件(夾) 軟連接,

# 如果是ARM 體系將執行以下操作:
#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 腳本文件的執行至此結束,繼續分析Makefile 剩下部分。

3 包含include/config.mk ,其實也就相當於在Makefile 裏定義了上面四個變量而已。

4) 指定交叉編譯器前綴:

ifeq ($(ARCH),arm)# 這裏根據ARCH 變量,指定編譯器前綴。
CROSS_COMPILE = arm-linux-
endif

5) 包含config.mk:

# 包含頂層目錄下的config.mk ,這個文件裏面主要定義了交叉編譯器及選項和編譯規則
# load other configuration
include $(TOPDIR)/config.mk

下面分析 config.mk 的內容:

    @包含體系,開發板,CPU 特定的規則文件:

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
endif

ifdef 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 ,優化選項OPTFLAGS

 預處理選項CPPFLAGS C 編譯器選項CFLAGS ,連接選項LDFLAGS

  LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)  # 指定了起始地址TEXT_BASE

@指定編譯規則:

$(obj)%.s: %.S
 $(CPP) $(AFLAGS) -o $@ $<
$(obj)%.o: %.S
 $(CC) $(AFLAGS) -c -o $@ $<
$(obj)%.o: %.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 : 執行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 ,通過執行相應子目錄下的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 = .;
}

@ 執行連接命令:

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 文件然後執行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.o

SRCS  := $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))

$(LIB): $(obj).depend $(OBJS) # 項層 Makefile 執行 make libgeneric.a
 $(AR) $(ARFLAGS) $@ $(OBJS)

整個 makefile 剩下的內容全部是各種不同的開發板的 *_config: 目標的定義了。

概括起來 工程的編譯流程也就是通過執行執行一個 make *_config 傳入 ARCH CPU BOARD SOC 參數 mkconfig 根據參數將 include 頭文件夾相應的頭文件夾連接好 生成 config.h 。然後執行make 分別調用各子目錄的makefile 生成所有的obj 文件和obj 庫文件*.a.  最後連接所有目標文件,生成鏡像。不同格式的鏡像都是調用相應工具由elf 鏡像直接或者間接生成的。

剩下的工作就是分析U-Boot 源代碼了。

限於個人水平,有謬誤之處,歡迎指正交流。。

 

 

 

發佈了30 篇原創文章 · 獲贊 5 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章