U-Boot编译系统简要分析

U-Boot编译系统简要分析

注:本文的编写参考了以下网络文章:

  • u-boot顶层的makefile分析
  • 跟我一起写Makefile
  • U-Boot最新源代码(v2012.07)

1.U-Boot简介

在此不废话,相信大家都已经了解。
在此要说明的是,其源代码目录下的README对需要了解U-Boot及其移植方法的人有很大帮助。其中有说U-Boot的目录结构如下:
Directory Hierarchy:====================

    /arch           Architecture specific files
      /arm          Files generic to ARM architecture
        /cpu        CPU specific files
          /arm720t      Files specific to ARM 720 CPUs
          /arm920t      Files specific to ARM 920 CPUs
        /at91       Files specific to Atmel AT91RM9200 CPU
        /imx        Files specific to Freescale MC9328 i.MX CPUs
        /s3c24x0    Files specific to Samsung S3C24X0 CPUs
          /arm925t      Files specific to ARM 925 CPUs
          /arm926ejs    Files specific to ARM 926 CPUs
          /arm1136      Files specific to ARM 1136 CPUs
          /ixp      Files specific to Intel XScale IXP CPUs
          /pxa      Files specific to Intel XScale PXA CPUs
          /s3c44b0      Files specific to Samsung S3C44B0 CPUs
          /sa1100       Files specific to Intel StrongARM SA1100 CPUs
        /lib        Architecture specific library files
      /avr32        Files generic to AVR32 architecture
        /cpu        CPU specific files
        /lib        Architecture specific library files
      /blackfin     Files generic to Analog Devices Blackfin architecture
        /cpu        CPU specific files
        /lib        Architecture specific library files
      /x86          Files generic to x86 architecture
        /cpu        CPU specific files
        /lib        Architecture specific library files
      /m68k         Files generic to m68k architecture
        /cpu        CPU specific files
          /mcf52x2      Files specific to Freescale ColdFire MCF52x2 CPUs
          /mcf5227x     Files specific to Freescale ColdFire MCF5227x CPUs
          /mcf532x      Files specific to Freescale ColdFire MCF5329 CPUs
          /mcf5445x     Files specific to Freescale ColdFire MCF5445x CPUs
          /mcf547x_8x   Files specific to Freescale ColdFire MCF547x_8x CPUs
        /lib        Architecture specific library files
      /microblaze       Files generic to microblaze architecture
        /cpu        CPU specific files
        /lib        Architecture specific library files
      /mips         Files generic to MIPS architecture
        /cpu        CPU specific files
          /mips32       Files specific to MIPS32 CPUs
          /xburst       Files specific to Ingenic XBurst CPUs
        /lib        Architecture specific library files
      /nds32        Files generic to NDS32 architecture
        /cpu        CPU specific files
          /n1213        Files specific to Andes Technology N1213 CPUs
        /lib        Architecture specific library files
      /nios2        Files generic to Altera NIOS2 architecture
        /cpu        CPU specific files
        /lib        Architecture specific library files
      /powerpc      Files generic to PowerPC architecture
        /cpu        CPU specific files
          /74xx_7xx     Files specific to Freescale MPC74xx and 7xx CPUs
          /mpc5xx       Files specific to Freescale MPC5xx CPUs
          /mpc5xxx      Files specific to Freescale MPC5xxx CPUs
          /mpc8xx       Files specific to Freescale MPC8xx CPUs
          /mpc8220      Files specific to Freescale MPC8220 CPUs
          /mpc824x      Files specific to Freescale MPC824x CPUs
          /mpc8260      Files specific to Freescale MPC8260 CPUs
          /mpc85xx      Files specific to Freescale MPC85xx CPUs
          /ppc4xx       Files specific to AMCC PowerPC 4xx CPUs
        /lib        Architecture specific library files
      /sh           Files generic to SH architecture
        /cpu        CPU specific files
          /sh2      Files specific to sh2 CPUs
          /sh3      Files specific to sh3 CPUs
          /sh4      Files specific to sh4 CPUs
        /lib        Architecture specific library files
      /sparc        Files generic to SPARC architecture
        /cpu        CPU specific files
          /leon2        Files specific to Gaisler LEON2 SPARC CPU
          /leon3        Files specific to Gaisler LEON3 SPARC CPU
        /lib        Architecture specific library files
    /api            Machine/arch independent API for external apps
    /board          Board dependent files
    /common         Misc architecture independent functions
    /disk           Code for disk drive partition handling
    /doc            Documentation (don't expect too much)
    /drivers        Commonly used device drivers
    /examples       Example code for standalone applications, etc.
    /fs         Filesystem code (cramfs, ext2, jffs2, etc.)
    /include        Header Files
    /lib            Files generic to all architectures
      /libfdt       Library files to support flattened device trees
      /lzma         Library files to support LZMA decompression
      /lzo          Library files to support LZO decompression
    /net            Networking code
    /post           Power On Self Test
    /rtc            Real Time Clock drivers
    /tools          Tools to build S-Record or U-Boot images, etc.

而其中的配置也分为两种,并详细介绍了各配置项:
There are two classes of configuration variables:

  • Configuration OPTIONS: These are selectable by the user and have names beginning with "CONFIG_".

  • Configuration SETTINGS: These depend on the hardware etc. and should not be meddled with if you don't know what you're doing; they have names beginning with "CONFIG_SYS_".

另外对U-boot的编译、移植方法及生成的映像的格式也作了介绍。

2.U-Boot的编译过程

  • 编译步骤
    根据U-Boot源代码README之介绍,U-Boot的编译分为两个步骤:
    make NAME_CONFIG
    make all
    那么,这两部都做了什么操作呢,既然执行的是make命令,那么我们就不得不打开Makefile一探究竟了。
  • Makefile初探
    打开Makefile,我们首先是奔着make NAME_CONFIG来的,搜索xx_config发现,MAkefile里面只有为数不多的几个带xx_config的target,而有一个很特别:%_config。
    根据《跟我一起写Makefile》p36之模式变量介绍:
  • 在GNU的make中,还支持模式变量(Pattern-specific Variable ),模式变量的好处就是,我们可以给定一种“ 模式” ,可以把变量定义在符合这种模式的所有目标上。
    我们知道,make的“ 模式” 一般是至少含有一个“%” 的,所以,我们可以以如下方式给所有以[.o] 结尾的目标定义目标变量: %.o : CFLAGS = -O
    由此可见,只要看%_config的命令即可。
    %_config依赖于unconfig,即配置之前先清除之前的配置,然后执行下面的命令:
    @$(MKCONFIG) -A $(@:_config=)
    事实上是执行了源代码根目录下之脚本,并把你传入的NAME_config去掉结尾的config传递过去( $(@:_config=) 参考《跟我一起写Makefile》p30之变量高级用法:
    我们可以替换变量中的共有的部分,其格式是“$(var:a=b)” 或是“${var:a=b}” ,其意思是,把变量“var”中所有以“a”字串“ 结尾” 的“a”替换成“b”字串。这里的“ 结尾” 意思是“ 空格” 或是“ 结束符” 。*):
    mkconfig -A NAME
  • mkconfig详解
    于是我们来到了mkconfig,shell脚本,哈哈,没什么好怕的了。
    首先是在board.cfg中寻找你传进来的NAME的配置行,board.cfg的格式类似如下: smdk2410 arm arm920t - samsung s3c24x
    第一个是配置文件名称,第二个是体系结构,第三个是cpu,第四个是board名称,若-,则board名称为去掉配置文件名_config后的部分。第五个是厂商vendor,第六个是soc的名称。如果有第七个参数则指定了选项,这里不做详细介绍。
    得到这些参数之后干什么了呢?
    仍以smdk2410为例,将arch目录下的inlcude/asm链接到include/asm:

        cd ./include
        rm -f asm
        ln -s ../arch/${arch}/include/asm asm
    

    将链接过来的asm目录里的arch目录删除,然后根据soc更新:

        if [ -z "${soc}" ] ; then
            ln -s ${LNPREFIX}arch-${cpu} asm/arch
        else
            ln -s ${LNPREFIX}arch-${soc} asm/arch
    

    如果是arm体系,还要更新asm目录下的proc目录:

        if [ "${arch}" = "arm" ] ; then
            rm -f asm/proc
            ln -s ${LNPREFIX}proc-armv asm/proc
        fi
    

    接着创建了include/config.mk文件,config.mk文件在后边编译时会被Makefile包含,并写入以下内容:

        echo "ARCH   = ${arch}"  >  config.mk
        echo "CPU    = ${cpu}"   >> config.mk
        echo "BOARD  = ${board}" >> config.mk
        [ "${vendor}" ] && echo "VENDOR = ${vendor}" >> config.mk
        [ "${soc}"    ] && echo "SOC    = ${soc}"    >> config.mk
    

    在此vendor指没指定有一个很重要的区别,其board目录可能是vendor/board或者board:

        if [ -z "${vendor}" ] ; then
            BOARDDIR=${board}
        else
            BOARDDIR=${vendor}/${board}
        fi
    

    接下来生成include/config.h:

        for i in ${TARGETS} ; do
            i="`echo ${i} | sed '/=/ {s/=/  /;q; } ; { s/$/ 1/; }'`"
            echo "#define CONFIG_${i}" >>config.h ;
        done
    
        echo "#define CONFIG_SYS_ARCH  \"${arch}\""  >> config.h
        echo "#define CONFIG_SYS_CPU   \"${cpu}\""   >> config.h
        echo "#define CONFIG_SYS_BOARD \"${board}\"" >> config.h
    
        [ "${vendor}" ] && echo "#define CONFIG_SYS_VENDOR \"${vendor}\"" >> config.h
    
        [ "${soc}"    ] && echo "#define CONFIG_SYS_SOC    \"${soc}\""    >> config.h
    
        cat << EOF >> config.h
        #define CONFIG_BOARDDIR board/$BOARDDIR
        #include 
        #include 
        #include 
        #include 
        #include 
        EOF
    

    config.h包含了arch,cpu,board,vendor,soc,board_dir这些宏定义。
    关于一开始的TARGETS循环,在boards.cfg中的选项字段即会转化为此处的宏定义,一可以使用-t传递参数给mkconfig。
    至此,配置工作基本完成了,生成了include目录下的config.mk和config.h两个文件。

  • Makefile 二探
    a.根据顶层README的介绍,编译U-Boot的第二个阶段就是

        make all
    

    于是再次打开Makefile

        all:        $(ALL-y) $(SUBDIR_EXAMPLES)
        $(obj)u-boot.srec:  $(obj)u-boot
                $(OBJCOPY) -O srec $< $@
        $(obj)u-boot.bin:   $(obj)u-boot
                $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
                $(BOARD_SIZE_CHECK)
        $(SUBDIR_EXAMPLES): $(obj)u-boot
        ALL-y += $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map
    

    由此可见,编译的话首先是编译生成u-boot,然后objcopy出另外的两个镜像类型的文件。关键是u-boot的生成:

        $(obj)u-boot:   depend \
                $(SUBDIR_TOOLS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT) $(obj)u-boot.lds
                $(GEN_UBOOT)
    

    b.$(SUBDIR_TOOLS)

        SUBDIR_TOOLS = tools
        SUBDIRS = $(SUBDIR_TOOLS)
        $(SUBDIRS): depend
                $(MAKE) -C $@ all
    

    因此SUBDIR_TOOLS是进入tools目录执行make命令(make -C tools)生成的。此目录的主要作用,就是会生成一系列的工具:

        BIN_FILES-$(CONFIG_LCD_LOGO) += bmp_logo$(SFX)
        BIN_FILES-$(CONFIG_VIDEO_LOGO) += bmp_logo$(SFX)
        BIN_FILES-$(CONFIG_BUILD_ENVCRC) += envcrc$(SFX)
        BIN_FILES-$(CONFIG_CMD_NET) += gen_eth_addr$(SFX)
        BIN_FILES-$(CONFIG_CMD_LOADS) += img2srec$(SFX)
        BIN_FILES-$(CONFIG_XWAY_SWAP_BYTES) += xway-swap-bytes$(SFX)
        BIN_FILES-y += mkenvimage$(SFX)
        BIN_FILES-y += mkimage$(SFX)
        BIN_FILES-$(CONFIG_SMDK5250) += mksmdk5250spl$(SFX)
        BIN_FILES-$(CONFIG_MX28) += mxsboot$(SFX)
        BIN_FILES-$(CONFIG_NETCONSOLE) += ncb$(SFX)
        BIN_FILES-$(CONFIG_SHA1_CHECK_UB_IMG) += ubsha1$(SFX)
        BIN_FILES-$(CONFIG_KIRKWOOD) += kwboot$(SFX)
    

    当然,最重要的是mkimage,还有bmp_logo等等,此处不详解了。
    c.$(OBJS)

        OBJS  = $(CPUDIR)/start.o
        ifeq ($(CPU),x86)
        OBJS += $(CPUDIR)/start16.o
        OBJS += $(CPUDIR)/resetvec.o
        endif
        ifeq ($(CPU),ppc4xx)
        OBJS += $(CPUDIR)/resetvec.o
        endif
        ifeq ($(CPU),mpc85xx)
        OBJS += $(CPUDIR)/resetvec.o
        endif
        OBJS := $(addprefix $(obj),$(OBJS))
        $(OBJS):    depend
                $(MAKE) -C $(CPUDIR) $(if $(REMOTE_BUILD),$@,$(notdir $@))
    

    由此可见,OBJS最最重要的就是进入CPUDIR编译出start.o了。
    那么,CPUDIR是怎么定义的呢,顶层的Makefile中有:

        include $(obj)include/config.mk
        export  ARCH CPU BOARD VENDOR SOC 
        include $(TOPDIR)/config.mk
    

    include/config.mk根据之前的解析是在make NAME_config时生成了,里面定义了ARCH,CPU,BOARD,VENDOR,SOC等这几个变量。
    根据顶层目录下config之定义:

        CPUDIR=arch/$(ARCH)/cpu/$(CPU)
        ifneq ($(SRCTREE)/$(CPUDIR),$(wildcard $(SRCTREE)/$(CPUDIR)))
        CPUDIR=arch/$(ARCH)/cpu
        endif
    

    由此就进入了正确的某个CPU的目录并编译了正确start.o启动代码。
    d.$(LIBBOARD)

        LIBBOARD = board/$(BOARDDIR)/lib$(BOARD).o
        LIBBOARD := $(addprefix $(obj),$(LIBBOARD))
        $(LIBBOARD):    depend $(LIBS)
                $(MAKE) -C $(dir $(subst $(obj),,$@))
    

    同理,顶层目录的config.mk对BOARDDIR的定义如下:

        ifdef   VENDOR
        BOARDDIR = $(VENDOR)/$(BOARD)
        else
        BOARDDIR = $(BOARD)
        endif
    

    因此就是进入开发板的目录编译并链接,仍以smdk2410为例:

        include $(TOPDIR)/config.mk
        LIB = $(obj)lib$(BOARD).o
        COBJS   := smdk2410.o
        SOBJS   := lowlevel_init.o
        SRCS    := $(SOBJS:.o=.S) $(COBJS:.o=.c)
        OBJS    := $(addprefix $(obj),$(COBJS))
        SOBJS   := $(addprefix $(obj),$(SOBJS))
        $(LIB): $(obj).depend $(OBJS) $(SOBJS)
            $(call cmd_link_o_target, $(OBJS) $(SOBJS))
    

    $(obj)lib$(BOARD).o依赖于smdk2410.o和lowlevel_init.o,编译吧,然后cmd_link_o_target进行链接。

        cmd_link_o_target = $(if $(strip $1),\
                      $(LD) $(LDFLAGS) -r -o $@ $1,\
                      rm -f $@; $(AR) rcs $@ )
    

    e.$(LIBS)
    呃,这个是最麻烦的啦,哎。

        LIBS  = lib/libgeneric.o
        LIBS += lib/lzma/liblzma.o
        LIBS += lib/lzo/liblzo.o
        LIBS += lib/zlib/libz.o
        ......
        LIBS += $(CPUDIR)/lib$(CPU).o
        ifdef SOC 
        LIBS += $(CPUDIR)/$(SOC)/lib$(SOC).o
        endif
        ......
        LIBS += arch/$(ARCH)/lib/lib$(ARCH).o
        LIBS += fs/cramfs/libcramfs.o fs/fat/libfat.o fs/fdos/libfdos.o fs/jffs2/libjffs2.o \
            fs/reiserfs/libreiserfs.o fs/ext2/libext2fs.o fs/yaffs2/libyaffs2.o \
            fs/ubifs/libubifs.o
        LIBS += net/libnet.o
        LIBS += disk/libdisk.o
        LIBS += drivers/bios_emulator/libatibiosemu.o
        LIBS += drivers/block/libblock.o
        ......(省略N多驱动)
        LIBS += common/libcommon.o
        LIBS += lib/libfdt/libfdt.o
        LIBS += api/libapi.o
        LIBS += post/libpost.o
        ......
        ifeq ($(SOC),s5pc1xx)
        LIBS += $(CPUDIR)/s5p-common/libs5p-common.o
        endif
        ifeq ($(SOC),exynos)
        LIBS += $(CPUDIR)/s5p-common/libs5p-common.o
        endif
        $(LIBS):    depend $(SUBDIR_TOOLS)
                $(MAKE) -C $(dir $(subst $(obj),,$@))
    

    虽然繁琐,但不复杂,就是编译N多个lib,分别进入其源目录执行make -C。
    需要指出的是,LIBS += $(CPUDIR)/$(SOC)/lib$(SOC).o,即CPU目录的子目录是作为lib在此编译的,在编译start.o时并没有编译。
    这样大部分的编译工作就完成了,CPU的初始化,板子的初始化,再到包含很多驱动的LIBS。
    接下来就该链接了吧。
    f. $(LDSCRIPT)

        # If there is no specified link script, we look in a number of places for it
        ifndef LDSCRIPT
            ifeq ($(CONFIG_NAND_U_BOOT),y)
                LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot-nand.lds
                ifeq ($(wildcard $(LDSCRIPT)),)
                    LDSCRIPT := $(TOPDIR)/$(CPUDIR)/u-boot-nand.lds
                endif
            endif
            ifeq ($(wildcard $(LDSCRIPT)),)
                LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds
            endif
            ifeq ($(wildcard $(LDSCRIPT)),)
                LDSCRIPT := $(TOPDIR)/$(CPUDIR)/u-boot.lds
            endif
            ifeq ($(wildcard $(LDSCRIPT)),)
                LDSCRIPT := $(TOPDIR)/arch/$(ARCH)/cpu/u-boot.lds
                # We don't expect a Makefile here
                LDSCRIPT_MAKEFILE_DIR =
            endif
            ifeq ($(wildcard $(LDSCRIPT)),)
        $(error could not find linker script)
            endif
        endif
        $(LDSCRIPT):    depend
                $(MAKE) -C $(dir $@) $(notdir $@)
    

    首先在N多个地方寻找链接脚本,找到之后,进入其目录执行make -Cg.$(obj)u-boot.lds
    执行了一条命令:

        $(obj)u-boot.lds: $(LDSCRIPT)
                $(CPP) $(CPPFLAGS) $(LDPPFLAGS) -ansi -D__ASSEMBLY__ -P - <$^ >$@
    

    将LDSCRIPT预处理下,然后生成$(obj)u-boot.ldsi.$(GEN_UBOOT)
    这个简单,是结束,也是开始吧,仅仅一个命令。

        ifeq ($(CONFIG_SANDBOX),y)
        GEN_UBOOT = \
                cd $(LNDIR) && $(CC) $(SYMS) -T $(obj)u-boot.lds \
                    -Wl,--start-group $(__LIBS) -Wl,--end-group \
                    $(PLATFORM_LIBS) -Wl,-Map -Wl,u-boot.map -o u-boot
        else
        GEN_UBOOT = \
                UNDEF_SYM=`$(OBJDUMP) -x $(LIBBOARD) $(LIBS) | \
                sed  -n -e 's/.*\($(SYM_PREFIX)__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
                cd $(LNDIR) && $(LD) $(LDFLAGS) $(LDFLAGS_$(@F)) $$UNDEF_SYM $(__OBJS) \
                    --start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
                    -Map u-boot.map -o u-boot
        endif
    

    因为编译已经完成,剩下的只是把编译出来的文件链接成u-boot。

    3.总结

    至此,U-Boot就编译完了。  呵呵,看起来总是比写起来简单,咱看人家写好的编译配置都这么麻烦,想来足见此工程之艰巨。  不管怎么说,还是让咱给大概看懂了。接下来就该是代码的事了吧,阅读代码,然后移植。。。

你可能感兴趣的:(U-BOOT移植)