U-boot--Makefile分析

2019-07-13 02:29发布

主要参考韦东山的《嵌入式Linux应用开发完全手册》   如果要使用开发板 board/,就先执行“make_config”命令进行配置,然后执 行”make all“,就可以生成 如下3个文件: U-boot.bin:二进制可执行文件,它就是可以直接烧入ROM,NORFlash的文件
u-Boot:ELF格式的可执行文件,
U-Boot.srec:Motorla S-Record格式的可执行文件
对于S3C2410的开发板,执行”make smdk2410_config“."make all"后生成的U-Boot.bin可以烧入NOR Flash中运行,启动后可以看到串口输出一些信息后进行控制界面。 1、U-boot的配置过程 在顶层Makefile中可以看到如下代码:
  1. ...........  
  2. MKCONFIG    := $(SRCTREE)/mkconfig  
  3. ........  
  4. smdk2410_config    :    unconfig  
  5.     @$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 samsung s3c24x0  

 这是在根目录下的MAKEFILE文件中的两个语句,其中的MKCONFIG就是根目录下的mkconfi文件。$(@:_config=)的结 果就是将”smdk2410_config“中的_config去掉,结果为“smdk2410”.所以“make smdk2410_config”实际上就是执行如下命令:
  1. ./mkconfig smdk2410 arm arm920t smdk2410 NULL s3c24x0  

mkconfig的作用,在mkconfig文件开头第6行给出了它的用法
  1. # Parameters: Target Architecture CPU Board [VENDOR] [SOC]  
对于S3C2410 S3C2440,它们被称为Soc(systme on chip),上面除CPU外,还集成了包括 RT,USB控制器,NANDFlash控制器等设备,称为片上外 设。                   下面看一下makeconfig的作用。
(1)确定开发板名称BOARD_NAME,相关代码如下:
  1. APPEND=no    # Default: Create new config file  
  2. BOARD_NAME=""    # Name to print in make output  
  3. while [ $# -gt 0 ] ; do  
  4.     case "$1" in  
  5.     --) shift ; break ;;  
  6.     -a) shift ; APPEND=yes ;;  
  7.     -n) shift ; BOARD_NAME="${1%%_config}" ; shift ;;  
  8.     *) break ;;  
  9.     esac  
  10. done  
  11.    
对于./mkconfig smdk2410 arm arm920t smdk2410 NULL s3c24x0命令,其中没有 "--","-a","-n"等符号,所以上面几行不会执行。
  1. "${BOARD_NAME}" ] || BOARD_NAME="$1"   
执行完上面的这句后,BOADR_NAME的值等于第1个参数,即"s3ck2410"
(2)创建到平台开发板相关折头文件的链接
  1. if [ "$SRCTREE" != "$OBJTREE" ] ; then //判断源代码目录和目标文件目录是否是一样  
  2.     mkdir -p ${OBJTREE}/incl?  
  3.     mkdir -p ${OBJTREE}/incl?2  
  4.     cd ${OBJTREE}/incl?2  
  5.     rm -f asm  
  6.     ln -s ${SRCTREE}/incl?/asm-$2 asm  
  7.     LNPREFIX="../../incl?2/asm/"  
  8.     cd ../incl?  
  9.     rm -rf asm-$2  
  10.     rm -f asm  
  11.     mkdir asm-$2  
  12.     ln -s asm-$2 asm  
  13. else  
  14.     cd ./incl?  
  15.     rm -f asm  
  16.     ln -s asm-$2 asm  
  17. fi  
  18.    

直接在源代码目录下编译时,条件不满足,将执行else分支的代码,在else分支中,进入incl?目录,删除asm文件,然后再次建立 asm文件,并令它链接向asm-$2目录,即asm-arm。
  1. rm -f asm-$2/arch //删除asm-$2/arch目录,即asm-arm/arch  
  2.   
  3. if [ -z "$6" -o "$6" = "NULL" ] ; then //$6="s3c24x0"不为空,也不为NULL,执行else分支  
  4.   
  5.     ln -s ${LNPREFIX}arch-$3 asm-$2/arch //LNPREFIX 为空,这个命令实际上等同于"ln - s arch-s3c24x0 asm-arm/arch"  
  6.   
  7. else  
  8.     ln -s ${LNPREFIX}arch-$6 asm-$2/arch  
  9. fi  
  10.   
  11. if [ "$2" = "arm" ] ; then //重新建立/asm-arm/proc文件,并让它链接向proc-armv目录   
  12.   
  13.     rm -f asm-$2/proc  
  14.     ln -s ${LNPREFIX}proc-armv asm-$2/proc  
  15. fi  
  16.    
(3)创建顶层MAKEFILE包含的文件incl?/config.mk
  1. #  
  2. # Create incl? file for Make  
  3. #  
  4. echo "ARCH = $2" > config.mk  
  5. echo "CPU = $3" >> config.mk  
  6. echo "BOARD = $4" >> config.mk  
  7.   
  8. "$5" ] && [ "$5" != "NULL" ] && echo "VENDOR = $5" >> config.mk  
  9.   
  10. "$6" ] && [ "$6" != "NULL" ] && echo "SOC = $6" >> config.mk  
  11.    
   对于./mkconfig smdk2410 arm arm920t smdk2410 NULL s3c24x0命令,上面几行创建的config.mk文件的内容如下
  1. ARCH = arm  
  2. CPU = arm920t  
  3. BOARD = smdk2410  
  4. SOC =s3c24x0  
  5.    
(4)创建开发板相关的头文件incl?/config.h
  1. #  
  2. # Create board specific header file  
  3. #  
  4. if [ "$APPEND" = "yes" ]    # Append to existing config file  
  5. then  
  6.     echo >> config.h  
  7. else  
  8.     > config.h        # Create new config file  
  9. fi  
  10. echo "/* Automatically generated - do not edit */" >>config.h  
  11. echo "#incl? " >>config.h  
  12. echo "#incl? " >>config.h  
  13.   
  14. exit 0  
  15.    
APPEND维持原值"NO",所以config.h被重新建立,也就是执行echo "#incl? " >>config.h
#incl?
总之,当你执行make smdk2410_config ,实际的作用就是执行./mkconfig smdk2410 arm arm920t smdk2410 NULL s3c24x0,它将产生如下的 几种作用
(1) 开发板的名称 BOARD_NAME等于 $1
(2)创建到平台,开发板相关的头文件的链接,如下所示
ln -s asm-$2 asm
ln -s arch-$6 asm-S2/arch
ln - s proc-armv asmn-$2/proc 如果$2不是arm的话,此行没有
(3)创建顶层Makefile包含的incld /config.mk,如下所示
ARCH = $2
CPU = $3
BOARD = $4
VENDOR = $ $5  为空,或者NULL的话,些行没有
SOC = $6
(4) 创建开发板相关的头文件incl?/config.h,如下 所示
#incl?
   从上面执行完命令后的结果,可以看出来,如果要在board目录下新建一个开发板的目录,则在 incl?/configs 目录下也要建立一个文件.h,里面存放的就是开发板的配置信息。
3.U-Boot的编译,连接过程
  1. # load ARCH, BOARD, and CPU configuration  
  2. incl? $(obj)incl?/config.mk  
  3. export    ARCH CPU BOARD VENDOR SOC  
  4.   
  5. # set default to nothing for native builds  
  6. ifeq ($(HOSTARCH),$(ARCH))  
  7. CROSS_COMPILE ?=  
  8. endif  
  9.   
  10. # load other configuration  
  11. incl? $(TOPDIR)/config.mk  
  12.    
这是根目录下的Makefile中与ARM相关的代码。
第 一行中包含的config.mk文件,就是在第一开始配置过程中制作出来的incl?/conifg.mk文件,我们在一开始配置U-boot时执行 过mkconfig。mini2440 时生成的文件,其中定义了ARCH,CPU,BOARD,SOC等。4个变量的值为arm,arm920t,smdk2410,s3c24x0.我们在执 行mkconfig。mini2440时,其实执行的是如下的命令:
  1. ./mkconfig smdk2410 arm arm920t smdk2410 NULL s3c24x0  
最后一句话incl? $(TOPDIR)/config.mk 包含顶层目录的config.mk文件。它根据上面4个变量的值确定了编译器。编译选项等。在顶层的config.mk中可以看到:
  1. fdef    VENDOR  
  2. BOARDDIR = $(VENDOR)/$(BOARD)  
  3. else  
  4. BOARDDIR = $(BOARD)  
  5. endif  
  6. ifdef    BOARD  
  7. sincl? $(TOPDIR)/board/$(BOARDDIR)/config.mk    # incl? board specific rules  
  8. endif  
  9.   
  10. LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds  
  11.   
  12. LDFLAGS += -Bstatic -T $(obj)u-boot.lds $(PLATFORM_LDFLAGS)  
  13. ifneq ($(TEXT_BASE),)  
  14. LDFLAGS += -Ttext $(TEXT_BASE)  
  15. endif  
  16.    
   在u-boot-2009.08oardsamsungsmdk2410config.mk中定义了“TEXT_BASE = 0x33F80000”.所以最终结果是:BOARDDIR为smdk2410;LDFLAGS中有“-T cpuarm920tu-boot.lds -Ttext 0x33f80000”.其中的-Ttext $(TEXT_BASE),这句指明了代码段的起始地址。为什么是0x33F8 0000呢?这是将NAND中 oot拷贝到RAM中的起始地址,所以在代码拷贝到RAM之前不能使用绝对地址来寻址数据,只能用相对地址,在以下将用 虚拟地址来指 oot在RAM中的地址,也就是0x33F80000
继续分析MAKEFIle文件:
  1. OBJS = cpu/$(CPU)/start.o  
  2. LIBS = lib_generic/libgeneric.a  
  3. LIBS += lib_generic/lzma/liblzma.a  
  4. LIBS += lib_generic/lzo/liblzo.a  
  5. LIBS += $(shell if [ -f board/$(VENDOR)/common/Makefile ]; then echo   
  6.     "board/$(VENDOR)/common/lib$(VENDOR).a"; fi)  
  7. LIBS += cpu/$(CPU)/lib$(CPU).a  
从上面的第一行我们可以看到OBJS的第一个值为"cpu/$(CPU)/start.o",即"cpu/arm920t/start.o"。下面的几行 指定了LIBS变量,也就是平台,开发板相关的各个目录,通用目录下相应的库。OBJS LIBS所代表的.o,.a文件构成了U-Boot,它们通过下面相应的源文件编译得到。
  1. $(OBJS):    depend  
  2.         $(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))  
  3.   
  4. $(LIBS):    depend $(S DIRS)  
  5.         $(MAKE) -C $(dir $(s st $(obj),,$@))  
  6.    
  对于OBJS中的每个成员,都将进入cpu/$(CPU)目录编译它们,现在的OBJS为cpu/arm920t/start.o。它由cpu /arm920t/start.S编译得到。对于LIBS中的每个成员,都将进入相应的子目录执行"make命令"。当所有的OBJS,LIBS所表示 的.o .a文件都生成后,就剩最后的连接了,这对应MAKEFILE中的下面几行:

  1. $(obj)u-boot.srec:    $(obj)u-boot  
  2.         $(OBJCOPY) -O srec {1}lt; $@  
  3.   
  4. $(obj)u-boot.bin:    $(obj)u-boot  
  5.         $(OBJCOPY) ${OBJCFLAGS} -O binary {1}lt; $@  
  6.   
  7. $(obj)u-boot.ldr:    $(obj)u-boot  
  8.         $(obj)tools/envcrc --binary > $(obj)env-ldr.o  
  9.         $(LDR) -T $(CONFIG_BFIN_CPU) -c $@ {1}lt; $(LDR_FLAGS)  
  10.   
  11. $(obj)u-boot.ldr.hex:    $(obj)u-boot.ldr  
  12.         $(OBJCOPY) ${OBJCFLAGS} -O ihex {1}lt; $@ -I binary  
  13.   
  14. $(obj)u-boot.ldr.srec:    $(obj)u-boot.ldr  
  15.         $(OBJCOPY) ${OBJCFLAGS} -O srec {1}lt; $@ -I binary  
  16.   
  17. $(obj)u-boot.img:    $(obj)u-boot.bin  
  18.         ./tools/mkimage -A $(ARCH) -T firmware -C none   
  19.         -a $(TEXT_BASE) -e 0   
  20.         -n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' $(VERSION_FILE) |   
  21.             sed -e 's/"[     ]*$/ for $(BOARD) board"/')   
  22.         -d {1}lt; $@  
  23. ................  
  24.   
  25. GEN_ OOT =   
  26.         UNDEF_SYM=`$(OBJDUMP) -x $(LIBBOARD) $(LIBS) |   
  27.         sed -n -e 's/.*($(SYM_PREFIX)__u_boot_cmd_.*)/-u1/p'|sort|uniq`;  
  28.         cd $(LNDIR) && $(LD) $(LDFLAGS) $UNDEF_SYM $(__OBJS)   
  29.             --start-group $(__LIBS) --end-group $(PLATFORM_LIBS)   
  30.             -Map u-boot.map -o u-boot  
  31. $(obj)u-boot:    depend $(S DIRS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT) $(obj)u-boot.lds  
  32.         $(GEN_ OOT)  
  33.    

先使用$(obj)u-boot:规则连接得到ELF格式的U-Boot,最后转换为二进制格式u-boot.bin.S-Record格式u- Boot.srec.其中LDFLAGS确定了连接方式,也就是-T cpuarm920tu-boot.lds -Ttext 0x33f80000指定了程序的布局地址,cpuarm920tU-Boot.lds文件如下:
  1. UTPUT_FORMAT("elf32-littlearm""elf32-littlearm""elf32-littlearm")  
  2. OUTPUT_ARCH(arm)  
  3. ENTRY(_start)  
  4. SECTIONS  
  5. {  
  6.     . = 0x00000000;  
  7.   
  8.     . = ALIGN(4);  
  9.     .text :  
  10.     {  
  11.         cpu/arm920t/start.o    (.text)  
  12.         *(.text)  
  13.     }  
  14.   
  15.     . = ALIGN(4);  
  16.     .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }  
  17.   
  18.     . = ALIGN(4);  
  19.     .data : { *(.data) }  
  20.   
  21.     . = ALIGN(4);  
  22.     .got : { *(.got) }  
  23.   
  24.     . = .;  
  25.     __u_boot_cmd_start = .;  
  26.     .u_boot_cmd : { *(.u_boot_cmd) }  
  27.     __u_boot_cmd_end = .;  
  28.   
  29.     . = ALIGN(4);  
  30.     __bss_start = .;  
  31.     .bss (NOLOAD) : { *(.bss) . = ALIGN(4); }  
  32.     _end = .;  
  33. }  
  34.    
从cpu/arm920t/start.o (.text) 被放在程序的最前面,所以U-Boot的入口点在cpu/arm920t/start.s中,
总结一下U-Boot的编译流程:
(1)首先编译cpu/$(CPU)/start.s,对于不同的CPU,还可能编译cpu/$(CPU)下面的其他文件。
(2)然后,对于平台开发板相关的每个目录,每个通用目录都使用它们各自的MAKEFILE生成相应和库。
(3)将1,2步骤生成的.o.a文件按照$(BOARDDIR)/config.mk 文件中指定的代码段起始地址。$(obj)u-boot.lds 连接脚本进行连接。
(4)第3步得到的是ELF格式的U-Boot,后面MAKEFILE还会将它转换为二进制格式 S-Record格式。