学习 uboot 之一makefile及链接脚本分析

uboot简单介绍:

uboot 即通用的Bootloader,一般有两层含义:1.他可以引导多种操作系统

Linux、VxWorks、等,2.支持多长架构CPU:PowerPC、MIPS、X86、ARM等。

一般情况下:UBOOT分为两个阶段来完成它的工作:
(1)uboot第一阶段:

        硬件初始化
        为加载uboot第二阶段代码初始化RAM空间
        复制第二阶段代码到RAM
        设置堆栈
        跳转到第二阶段

这一阶段的硬件初始化包括:关闭WATCHDOG、关闭中断、设置CPU时钟频率、RAM初始化

(2)uboot第二阶段

    初始化本阶段用到的硬件设备
    检测系统内存映射
    将内核映像文件盒根文件系统从FLASH中加载到RAM中
    为内核设置启动参数
    调用内核

下面来了解下U-Boot的编译连接过程

uboot的源码文件众多,想要了解某个架构,某款开发版如何使用,最直接的方式就是阅读Makefile,这里先以S3C2410举个例子

根据顶层的ReadMe文件说明,我们知道,想要利用某种类型的开发板,首先要执行make board_config 命令进行配置,然后
再执行make all即可以生成如下文件:
U-Boot.bin:二进制可执行文件,直接烧录进ROM或者NORflash的文件
U-Boot:ELF格式的可执行文件

首先我们先来分析makefile 看一看make smdk2410_config做了那些事情。
顶层makefile中有如下规则:

smdk2410_config :   unconfig
    @$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0

可知执行make smdk2410_config后,实际上执行的是

@$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0

$(@:_config=)的结果就是将smdk2410_config中的_config去掉

根据MKCONFIG := $(SRCTREE)/mkconfig,可知$(MKCONFIG)就是源码目录下的mkconfig,接着我们来分析下mkconfig中重要的部分,在文件开头给出了mkconfig的用法

Parameters:  Target  Architecture  CPU  Board [VENDOR] [SOC]
make smdk_2410 arm arm920t smdk2410 NULL s3c24x0

接着我们来分析下mkconfig中重要的部分

[ "${BOARD_NAME}" ] || BOARD_NAME="$1"

[ $# -lt 4 ] && exit 1
[ $# -gt 6 ] && exit 1

echo "Configuring for ${BOARD_NAME} board..."

执行完上面的命令后BOARD_NAME就等于smdk2410,接着分析:

if [ "$SRCTREE" != "$OBJTREE" ] ; then
    mkdir -p ${OBJTREE}/include
    mkdir -p ${OBJTREE}/include2
    ......
    rm -f asm
    mkdir asm-$2
    ln -s asm-$2 asm
else
    cd ./include
    rm -f asm
    ln -s asm-$2 asm
fi

很显然上面这段我们走else分支,创建ln -s asm-$2 asm ($2=arm),执行后,asm就是指向asm-arm的软链接

rm -f asm-$2/arch

if [ -z "$6" -o "$6" = "NULL" ] ; then
    ln -s ${LNPREFIX}arch-$3 asm-$2/arch
else
    ln -s ${LNPREFIX}arch-$6 asm-$2/arch
fi

上面这一段也是走到else分支且${LNPREFIX}为空,故为ln -s arch-$6 asm-$2/arch->ln -s arch-s3c24x0 asm-arm/arch
将asm-arm目录下的arch文件指向同一个目录下arch-s3c24x0

#
# Create include file for Make
#
echo "ARCH   = $2" >  config.mk
echo "CPU    = $3" >> config.mk
echo "BOARD  = $4" >> config.mk

[ "$5" ] && [ "$5" != "NULL" ] && echo "VENDOR = $5" >> config.mk

[ "$6" ] && [ "$6" != "NULL" ] && echo "SOC    = $6" >> config.mk

上面这段在./include目录下创建了,config.mk文件,并写入

    ARCH   = arm
    CPU    = arm920t
    BOARD  = smdk2410
    SOC    = s3c24x0

接着继续分析

#
# Create board specific header file
#
if [ "$APPEND" = "yes" ]   # Append to existing config file
then
    echo >> config.h
else
    > config.h      # Create new config file
fi
echo "/* Automatically generated - do not edit */" >>config.h
echo "#include <configs/$1.h>" >>config.h

exit 0

上面这段在./include目录下创建了,config.h文件,并写入

/* Automatically generated - do not edit */
#include <configs/smdk2410.h>

总结下执行 make smdk2410_config做了那些事情:

1.设置BOARD_NAME=”$1”,即smdk2410
2.创建开发板平台相关的头文件的链接

    ln -s asm-$2 asm
    ln -s arch-$6 asm-$2/arch
    ln -s proc-armv asm-$2/proc

3.创建顶层Makefile包含的include/config.mk,内容如下:

    ARCH   = arm
    CPU    = arm920t
    BOARD  = smdk2410
    SOC    = s3c24x0

4.创建开发板相关的头文件include/config.h,内容如下:

    /* Automatically generated - do not edit */
    #include <configs/smdk2410.h>

从上面的结果能够看出:

如果需要加入一个新的开发板,需要在board目录下新建一个board_name的目录,
同时也需要在include/config目录下建立一个文件board_name.h,里面存放的
就是board_name的配置信息,可以通过手动修改include/config/.h
来裁剪、设置U-BOOT.

配置文件中主要有两类宏
(1)一类是前缀为CONFIG_,它们用于选择CPU、SOC、开发板类型,设置时钟选择设备驱动等。比如:

        #define CONFIG_ARM920T      1   /* This is an ARM920T Core  */
        #define CONFIG_S3C2410      1   /* in a SAMSUNG S3C2410 SoC     */
        #define CONFIG_SMDK2410     1   /* on a SAMSUNG SMDK2410 Board  */

(2)另一类是参数(Setting),前缀”CFG_”,它们用于设置malloc缓冲池的大小U-Boot的提示符、
U-Boot下载文件时的默认加载地址、FLASH的起始地址

   #define CFG_MALLOC_LEN       (CFG_ENV_SIZE + 128*1024)
   #define  CFG_LOAD_ADDR       0x33000000  /* default load address */
   #define PHYS_FLASH_1     0x00000000 /* Flash Bank #1 */

   #define CFG_FLASH_BASE       PHYS_FLASH_1

实际上在编译过程中,大部分的文件都会被编译到,但是是不每个文件的代码都是有效的,主要有功能宏控制,比如:对于网卡驱动CS8900.C,有如下宏判断

#ifdef CONFIG_DRIVER_CS8900
    实际代码
#ENDIF

从上面的分析能够看出CONFIG_的宏,主要用于功能的选择,CFG_主要用于细节参数的设置。

下面来看编译链接的过程。
配置完成以后,执行make all编译,来分析下主目录下Makefile中的内容。

# load ARCH, BOARD, and CPU configuration
include $(OBJTREE)/include/config.mk
export  ARCH CPU BOARD VENDOR SOC
/*包含的就是前面make smdk2410_config在include目录下生成的config.mk,有ARCH CPU BOARD VENDOR SOC等的定义*/

ifeq ($(ARCH),arm)
CROSS_COMPILE = arm-linux-
endif
/*指明交叉编译时用的工具名称*/


# load other configuration
include $(TOPDIR)/config.mk

在顶层目录的config.mk里面有这样一些重要信息:

BOARDDIR = $(BOARD)
ifdef   BOARD
sinclude $(TOPDIR)/board/$(BOARDDIR)/config.mk # include board specific rules
endif

LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)

LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds

在board/smdk2410/config.mk中,定义了“TEXT_BASE=0x33f80000”。所以,最终结果如下:
BOARDDIR 为smdk2410;LDFLAGS中有“-T board/smdk2410/U-boot.lds -Ttext 0x33f80000”

继续往下看makefile

#############################################################
# U-Boot objects....order is important (i.e. start must be first)

OBJS  = cpu/$(CPU)/start.o

LIBS += board/$(BOARDDIR)/lib$(BOARD).a
LIBS += cpu/$(CPU)/lib$(CPU).a
LIBS += drivers/libdrivers.a
LIBS += drivers/nand/libnand.a
LIBS += drivers/nand_legacy/libnand_legacy.a

LIBS变量就是平台/开发板相关的各个目录、通用目录下的库构成

$(OBJS):
        $(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))

$(LIBS):
        $(MAKE) -C $(dir $(subst $(obj),,$@))

$(SUBDIRS):
        $(MAKE) -C $@ all

从规则能够看出,OBJS成的每个成员都需要进入到cpu/$(CPU)目录,即cpu/arm920t目录编译
现在OBJS为cpu/arm920t/start.o,它将由cpu/arm920t/start.S编译得到。

对于LIBS中的每个成员,都将进入相应的子目录执行make命令,生成对应的库文件

当所有的OBJS、LIBS所表示.a和.o文件都生成后,就剩最后的链接,对应下面的几行:

$(obj)u-boot.bin:	$(obj)u-boot
        $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@

$(obj)u-boot.img:	$(obj)u-boot.bin
        ./tools/mkimage -A $(ARCH) -T firmware -C none \
        -a $(TEXT_BASE) -e 0 \
        -n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' $(VERSION_FILE) | \
            sed -e 's/"[     ]*$$/ for $(BOARD) board"/') \
        -d $< $@

$(obj)u-boot.dis:	$(obj)u-boot
        $(OBJDUMP) -d $< > $@

$(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

先由上面的规则生成ELF格式的uboot文件,然后生成可用于烧写的二进制文件,其中LDFLAGS确定了链接方式
其中有-T board/smdk2410/U-boot.lds -Ttext 0x33F800指明了程序的布局、地址,U-Boot.lds的内容如下:

OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
    . = 0x00000000;

    . = ALIGN(4);
    .text      :
    {
      cpu/arm920t/start.o   (.text)
      *(.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 = .;
}

.text      :
{
  cpu/arm920t/start.o   (.text)
  *(.text)
}

从上面这一段能够看出,cpu/arm920t/start.o会放在代码段的开始位置

总结下编译流程:
1.首先编译cpu/$(CPU)/start.S,对于不同的CPU,还可能编译不同目录下的其它文件

2.然后,对于平台/开发板相关目录、每个通用的目录使用它们自己各自的Makefile生成

3.将步骤1,2生成的.o、.a文件按照board/$(BOARDDIR)/config.mk文件中指定的代码起始地址、board/$(BOARDDIR)/U-Boot.lds链接脚本进行链接

4.第3步得到的ELF格式的U-boot然后转换为二进制格式的uboot

在分析完makefile和链接脚本后,下面我们就正式开始分析uboot代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值