u-boot编译学习--uboot编译链接过程

来源:互联网 发布:袁炜事件 知乎 编辑:程序博客网 时间:2024/05/30 04:25

参考博客:http://blog.chinaunix.net/uid-18921523-id-165078.html


UBOOT是一个LINUX下的工程,在编译之前必须已经安装对应体系结构的交叉编译环境,

这里只针对ARM,编译器系列软件为arm-linux-*


UBOOT的下载地址: http://sourceforge.net/projects/u-boot
下载的是1.1.6版本

u-boot 源码结构:

解压就可以得到全部u-boot源程序。在顶层目录下有18个子目录,分别存放和管理不同的源程序。

这些目录中所要存放的文件有其规则,可以分为3类。

    1类目录: 与处理器体系结构或者开发板硬件直接相关;
    
2类目录: 是一些通用的函数或者驱动程序;
    
3类目录: 是u-boot的应用程序、工具或者文档。

u-boot 的源码顶层目录说明:

[cpp] view plaincopyprint?
  1. 目    录              特    性                解 释 说 明  
  2. board                平台依赖            存放电路板相关的目录文件,  
  3.                                          例如:RPXlite(mpc8xx)、  
  4.                                          smdk2410(arm920t)、  
  5.                                          sc520_cdp(x86) 等目录;  
  6. cpu                  平台依赖            存放CPU相关的目录文件  
  7.                                          例如:mpc8xx、ppc4xx、  
  8.                                          arm720t、arm920t、 xscale、i386等目录;  
  9. lib_ppc              平台依赖            存放对PowerPC体系结构通用的文件,  
  10.                                          主要用于实现PowerPC平台通用的函数;  
  11. lib_arm              平台依赖            存放对ARM体系结构通用的文件,  
  12.                                          主要用于实现ARM平台通用的函数;  
  13. lib_i386             平台依赖            存放对X86体系结构通用的文件,  
  14.                                          主要用于实现X86平台通用的函数;  
  15. include              通用                头文件和开发板配置文件,  
  16.                                          所有开发板的配置文件都在configs目录下;  
  17. common               通用                通用的多功能函数实现  
  18. lib_generic          通用                通用库函数的实现  
  19. net                  通用                存放网络的程序  
  20. fs                   通用                存放文件系统的程序  
  21. post                 通用                存放上电自检程序  
  22. drivers              通用                通用的设备驱动程序,主要有以太网接口的驱动  
  23. disk                 通用                硬盘接口程序  
  24. rtc                  通用                RTC的驱动程序  
  25. dtt                  通用                数字温度测量器或者传感器的驱动  
  26. examples             应用例程            一些独立运行的应用程序的例子,例如helloworld  
  27. tools                工具                存放制作S-Record或者u-boot格式的映像等工具,  
  28.                                          例如mkimage  
  29. doc                  文档                开发使用文档  

u-boot的源代码包含对几十种处理器、数百种开发板的支持。

可是对于特定的开发板,配置编译过程只需要其中部分程序。

这里具体以S3C2410 & arm920t处理器为例,

具体分析S3C2410处理器和开发板所依赖的程序,以及u-boot的通用函数和工具。

编译:

smdk_2410板为例,编译的过程分两部:

# make smdk2410_config
# make

要了解一个LINUX工程的结构必须看懂Makefile

尤其是顶层的,没办法,UNIX世界就是这么无奈,什么东西都用文档去管理、配置。

首先在这方面我是个新手,时间所限只粗浅地看了一些Makefile规则。

smdk_2410为例,顺序分析Makefile大致的流程及结构如下:

1) 定义了源码及生成的目标文件存放的目录:

目标文件存放目录BUILD_DIR可以通过make O=dir 指定。

如果没有指定,则设定为源码顶层目录。一般编译的时候不指定输出目录,则BUILD_DIR为空。

其它目录变量定义如下:

#OBJTREELNDIR为存放生成文件的目录,TOPDIRSRCTREE为源码所在目录:

[cpp] view plaincopyprint?
  1. OBJTREE  := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))  
  2. SRCTREE  := $(CURDIR)  
  3. TOPDIR  := $(SRCTREE)  
  4. LNDIR  := $(OBJTREE)  
  5. export TOPDIR SRCTREE OBJTREE  

2定义变量 MKCONFIG

这个变量指向一个脚本,即顶层目录的mkconfig

[cpp] view plaincopyprint?
  1. MKCONFIG := $(SRCTREE)/mkconfig  
  2. export MKCONFIG  

在编译UBOOT之前,先要执行

[cpp] view plaincopyprint?
  1. # make smdk2410_config  

smdk2410_configMakefile的一个目标,定义如下:

[cpp] view plaincopyprint?
  1. smdk2410_config : unconfig  
  2.  @$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0  
  3.   
  4.  unconfig::  
  5.  @rm -f $(obj)include/config.h $(obj)include/config.mk \  
  6.   $(obj)board/*/config.tmp $(obj)board/*/*/config.tmp  

显然,执行# make smdk2410_config时,先执行unconfig目标,

注意不指定输出目标时,objsrc变量均为空,

unconfig下面的命令清理上一次执行make *_config时生成的头文件和makefile的包含文件。

主要是 include/config.h 和 include/config.mk 文件。

然后才执行命令

[cpp] view plaincopyprint?
  1. @$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0  

MKCONFIG 是顶层目录下的mkcofig脚本文件,后面五个是传入的参数。

对于smdk2410_config而言,mkconfig主要做三件事:

include文件夹下建立相应的文件(夹)软连接

#如果是ARM体系将执行以下操作:

[cpp] view plaincopyprint?
  1. #ln -s     asm-arm        asm     
  2.   
  3. #ln -s  arch-s3c24x0    asm-arm/arch   
  4. #ln -s   proc-armv       asm-arm/proc  


生成 Makefile 包含文件 include/config.mk,内容很简单,定义了四个变量:

[cpp] view plaincopyprint?
  1. ARCH   = arm  
  2. CPU    = arm920t  
  3. BOARD  = smdk2410  
  4. SOC    = s3c24x0  

生成 include/config.h 头文件,只有一行:

[cpp] view plaincopyprint?
  1. /* Automatically generated - do not edit */  
  2. #include "config/smdk2410.h"  

mkconfig脚本文件的执行至此结束,继续分析Makefile剩下部分。

3包含 include / config . mk

其实也就相当于在Makefile里定义了上面四个变量而已。

4) 指定交叉编译器前缀 arm - linux - 

[cpp] view plaincopyprint?
  1. ifeq ($(ARCH),arm)#这里根据ARCH变量,指定编译器前缀。  
  2. CROSS_COMPILE = arm-linux-  
  3. endif  

5) 包含顶层 config . mk:

包含顶层目录下的 config.mk ,这个文件里面主要定义了 交叉编译器 及 选项 和 编译规则

[cpp] view plaincopyprint?
  1. # load other configuration   
  2. include $(TOPDIR)/config.mk  

分析 config.mk 的内容:

包含体系,开发板,CPU特定的规则文件:
1,指定预编译体系结构选项:

[cpp] view plaincopyprint?
  1. ifdef ARCH   
  2. sinclude $(TOPDIR)/$(ARCH)_config.mk # include architecture dependend rules  
  3. endif  
PLATFORM_CPPFLAGS += -DCONFIG_ARM -D__ARM__

2,定义编译时对齐,浮点等选项:

[cpp] view plaincopyprint?
  1. ifdef CPU   
  2. sinclude $(TOPDIR)/cpu/$(CPU)/config.mk # include  CPU specific rules  
  3. endif  

[cpp] view plaincopyprint?
  1. ifdef SOC #没有这个文件  
  2. sinclude $(TOPDIR)/cpu/$(CPU)/$(SOC)/config.mk # include  SoC specific rules  
  3. endif  

3,指定开发板代码所在目录

[cpp] view plaincopyprint?
  1. ifdef VENDOR  
  2. BOARDDIR = $(VENDOR)/$(BOARD)  
  3. eles  
  4. BOARDDIR = $(BOARD)  

4,指定特定板子的镜像连接时的内存基地址,重要!:TEXT_BASE = 0x33D00000

[cpp] view plaincopyprint?
  1. ifdef BOARD   
  2. sinclude $(TOPDIR)/board/$(BOARDDIR)/config.mk # include board specific rules  
  3. endif  

5,定义交叉编译链工具:

[cpp] view plaincopyprint?
  1. # Include the make variables (CC, etc...)  
  2. #   
  3. AS = $(CROSS_COMPILE)as  
  4. LD = $(CROSS_COMPILE)ld  
  5. CC = $(CROSS_COMPILE)gcc  
  6. CPP = $(CC) -E  
  7. AR = $(CROSS_COMPILE)ar  
  8. NM = $(CROSS_COMPILE)nm  
  9. STRIP = $(CROSS_COMPILE)strip  
  10. OBJCOPY = $(CROSS_COMPILE)objcopy  
  11. OBJDUMP = $(CROSS_COMPILE)objdump  
  12. RANLIB = $(CROSS_COMPILE)RANLIB  

6,定义AR选项ARFLAGS

调试选项DBGFLAGS,优化选项OPTFLAGS:

[cpp] view plaincopyprint?
  1. AFLAGS := $(AFLAGS_DEBUG) -D__ASSEMBLY__ $(CPPFLAGS)  

7,预处理选项CPPFLAGS

C编译器选项CFLAGS,连接选项LDFLAGS:

[cpp] view plaincopyprint?
  1. LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)  

其中  LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds   再  BOARDDIR = $(VENDOR)/$(BOARD)  即是

u-boot.lds 为连接脚本文件.

8,指定编译规则:

[cpp] view plaincopyprint?
  1. $(obj)%.s: %.S  
  2.  $(CPP) $(AFLAGS) -o $@ $<  
  3. $(obj)%.o: %.S  
  4.  $(CC) $(AFLAGS) -c -o $@ $<  
  5. $(obj)%.o: %.c  
  6.  $(CC) $(CFLAGS) -c -o $@ $<  

回到顶层makefile文件:

6U-boot 需要的目标文件 start.o:

[cpp] view plaincopyprint?
  1. OBJS  = cpu/$(CPU)/start.o # 顺序很重要,start.o必须放第一位  

7)需要的库文件:

[cpp] view plaincopyprint?
  1. LIBS  = lib_generic/libgeneric.a  
  2. LIBS += board/$(BOARDDIR)/lib$(BOARD).a  
  3. LIBS += cpu/$(CPU)/lib$(CPU).a  
  4. ifdef SOC  
  5. LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a  
  6. endif  
  7. LIBS += lib_$(ARCH)/lib$(ARCH).a  
  8. LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a \  
  9.  fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a  
  10. LIBS += net/libnet.a  
  11. LIBS += disk/libdisk.a  
  12. LIBS += rtc/librtc.a  
  13. LIBS += dtt/libdtt.a  
  14. LIBS += drivers/libdrivers.a  
  15. LIBS += drivers/nand/libnand.a  
  16. LIBS += drivers/nand_legacy/libnand_legacy.a  
  17. LIBS += drivers/sk98lin/libsk98lin.a  
  18. LIBS += post/libpost.a post/cpu/libcpu.a  
  19. LIBS += common/libcommon.a  
  20. LIBS += $(BOARDLIBS)  
  21.   
  22. LIBS := $(addprefix $(obj),$(LIBS))  
  23. .PHONY : $(LIBS)  

根据上面的 include/config.mk 文件定义的 ARCHCPUBOARDSOC 这些变量。

硬件平台依赖的目录文件可以根据这些定义来确定。

SMDK2410平台相关目录及对应生成的库文件如下:

[cpp] view plaincopyprint?
  1. board/smdk2410/        :库文件board/smdk2410/libsmdk2410.a  
  2. cpu/arm920t/           :库文件cpu/arm920t/libarm920t.a  
  3. cpu/arm920t/s3c24x0/   :库文件cpu/arm920t/s3c24x0/libs3c24x0.a  
  4. lib_arm/               :库文件lib_arm/libarm.a  
  5. include/asm-arm/       :下面两个是头文件。  
  6. include/configs/smdk2410.h  

8)最终生成的各种镜像文件 ALL:

[cpp] view plaincopyprint?
  1. ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)  
  2.   
  3. all:  $(ALL)  
  4.   
  5. $(obj)u-boot.hex: $(obj)u-boot  
  6.   $(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@  
  7.   
  8. $(obj)u-boot.srec: $(obj)u-boot  
  9.   $(OBJCOPY) ${OBJCFLAGS} -O srec $< $@  
  10.   
  11. $(obj)u-boot.bin: $(obj)u-boot  
  12.   $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@  
  13.  #这里生成的是U-boot 的ELF文件镜像   
  14. $(obj)u-boot:  depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)  
  15.   UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed  -n -e '''''''''''''''''''''''''''''''  
  16.   cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \  
  17.    --start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \  
  18.    -Map u-boot.map -o u-boot  

9) 对于各子目录的makefile文件:

主要是生成 *.o 文件然后执行 AR 生成对应的库文件。如 lib_generic 文件夹 Makefile

[cpp] view plaincopyprint?
  1. LIB = $(obj)libgeneric.a  
  2.   
  3. COBJS = bzlib.o bzlib_crctable.o bzlib_decompress.o \  
  4.    bzlib_randtable.o bzlib_huffman.o \  
  5.    crc32.o ctype.o display_options.o ldiv.o \  
  6.    string.o vsprintf.o zlib.o  
  7.   
  8. SRCS  := $(COBJS:.o=.c)  
  9. OBJS := $(addprefix $(obj),$(COBJS))  
  10.   
  11. $(LIB): $(obj).depend $(OBJS) #项层Makefile执行make libgeneric.a  
  12.  $(AR) $(ARFLAGS) $@ $(OBJS)  

u-boot ELF 文件镜像的生成:

分析一下最关键的 u-boot ELF 文件镜像的生成:

依赖目标 depend:

生成各个子目录的.depend文件,.depend 列出每个目标文件的依赖文件。

生成方法,调用每个子目录的 make _depend 

[cpp] view plaincopyprint?
  1. depend dep:  
  2.   for dir in $(SUBDIRS) ; do $(MAKE) -C $$dir _depend ; done  

依赖目标 version

生成版本信息到版本文件VERSION_FILE中。

[cpp] view plaincopyprint?
  1. version:  
  2.   @echo -n "#define U_BOOT_VERSION \"U-Boot " > $(VERSION_FILE); \  
  3.   echo -n "$(U_BOOT_VERSION)" >> $(VERSION_FILE); \  
  4.   echo -n $(shell $(CONFIG_SHELL) $(TOPDIR)/tools/setlocalversion \  
  5.     $(TOPDIR)) >> $(VERSION_FILE); \  
  6.   echo "\"" >> $(VERSION_FILE)  

伪目标 SUBDIRS:

执行tools ,examples ,post,post\cpu 子目录下面的make文件。

[cpp] view plaincopyprint?
  1. SUBDIRS = tools \  
  2.    examples \  
  3.    post \  
  4.    post/cpu  
  5. .PHONY : $(SUBDIRS)  
  6.   
  7. $(SUBDIRS):  
  8.   $(MAKE) -C $@ all  

依赖目标 $(OBJS)

cpu/start.o

[cpp] view plaincopyprint?
  1. $(OBJS):  
  2.   $(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))  

依赖目标 $(LIBS)

这个目标太多,都是每个子目录的库文件*.a ,通过执行相应子目录下的make来完成:

[cpp] view plaincopyprint?
  1. $(LIBS):  
  2.   $(MAKE) -C $(dir $(subst $(obj),,$@))   

依赖目标 $(LDSCRIPT)

[cpp] view plaincopyprint?
  1. LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds  
  2. LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)  

连接脚本文件 /u-boot.lds:

对于smdk2410,LDSCRIPT即连接脚本文件是board/smdk2410/u-boot.lds,定义了连接时各个目标文件是如何组织的。

内容如下:

[cpp] view plaincopyprint?
  1. OUTPUT_FORMAT("elf32-littlearm""elf32-littlearm""elf32-littlearm")  
  2. /*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/  
  3. OUTPUT_ARCH(arm)  
  4. ENTRY(_start)  
  5. SECTIONS  
  6. {  
  7.  . = 0x00000000;  
  8.   
  9.  . = ALIGN(4);  
  10.  .text    :/*.text的基地址由LDFLAGS中-Ttext $(TEXT_BASE)指定*/  
  11.  {                      /*smdk2410指定的基地址为0x33f80000*/  
  12.    cpu/arm920t/start.o (.text)         /*start.o为首*/  
  13.    *(.text)  
  14.  }  
  15.   
  16.  . = ALIGN(4);  
  17.  .rodata : { *(.rodata) }  
  18.   
  19.  . = ALIGN(4);  
  20.  .data : { *(.data) }  
  21.   
  22.  . = ALIGN(4);  
  23.  .got : { *(.got) }  
  24.   
  25.  . = .;  
  26.  __u_boot_cmd_start = .;  
  27.  .u_boot_cmd : { *(.u_boot_cmd) }  
  28.  __u_boot_cmd_end = .;  
  29.   
  30.  . = ALIGN(4);  
  31.  __bss_start = .;  
  32.  .bss : { *(.bss) }  
  33.  _end = .;  
  34. }  

执行连接命令:

[cpp] view plaincopyprint?
  1. cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \  
  2.    --start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \  
  3.    -Map u-boot.map -o u-boot  

其实就是把 start.o 和各个子目录 makefile 生成的库文件按照   LDFLAGS   连接在一起

生成ELF文件 u-boot  和连接时内存分配图文件 u-boot.map

u-boot.map 表示的是地址标号到该标号表示的地址的一个映射关系。

整个 Makefile 剩下的内容全部是各种不同的开发板的 *_config: 目标的定义了。

概括总结起来:

工程的编译流程也就是通过执行执行一个 make *_config 传入ARCHCPUBOARDSOC参数,

mkconfig 根据参数将 include 头文件夹相应的头文件夹连接好,生成 config.h 

然后执行 make 分别调用各子目录的 makefile  生成所有的obj文件和obj库文件*.a.  最后连接所有目标文件,生成镜像。

不同格式的镜像都是调用相应工具由 elf 镜像直接或者间接生成的。

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


总结 uboot 的编译流程:

1、   首先编译cpu/$(CPU)start.S

2、   然后,对于平台相关的每个目录,每个通用的目录都使用他们各自得Makefile生成相应得库

3、   12步骤生成的.0.a文件按照board/$(BOARDDIR)/config.mk文件中指定的代码段起始地址、board/$(BOARDDIR)/u-boot.lds链接脚本进行连接。

4、   3步得到的是ELF格式的uboot,后面的Makefile还会将它转换为二进制格式(bin格式)、s-record格式。






0 0
原创粉丝点击