uboot主Makefile分析

本文对 Uboot 的主 Makefile 进行了分析,以便清晰了解整个 uboot 的编译过程

本文对 Uboot 的主 Makefile 进行了分析,以便清晰了解整个 uboot 的编译过程

Uboot 的主 Makefile 分析

本文对 Uboot 的主 Makefile 进行了分析,以便清晰了解整个 uboot 的编译过程

X210官方uboot配置编译

uboot的来源

  • uboot官网

    源头的源代码是uboot官网下载的。这个下载的源代码可能没有你当前使用的开发板的移植,甚至找不到当前开发板使用的SoC对应的移植版本。

  • SoC厂商

    SoC厂商在推出一款SoC后,工程师会去uboot官网下载一个uboot,根据自己的SoC进行第一步的移植,移植的目标是厂商推出的开发板。(譬如三星的S5PV210芯片厂商出的开发板就叫SMDKV210).所以三星的工程师移植的uboot是根据他们自己的SMDKV210开发板移植的。

  • 具体的开发板供应商(譬如X210的生产商深圳市九鼎科技)

    首先购买三星的SMDKV210开发板,然后进行裁剪(把一些无用的接口功能裁剪去,配置降低一下,某些配置会被替换)。硬件替换和裁剪之后生成的新的开发板(譬如X210)和三星官方的SMDKV210有所不同,因此uboot也不同。但是因为SoC是相同的,所以相似度至少有60%以上。所以具体开发板供应商会以三星SMDKV210中移植的uboot为蓝本来移植得到自己的开发板的一个uboot移植。我们买X210开发板时厂商光盘中带的BSP中的uboot源码就是他移植过的。

X210官方uboot配置编译

  • X210移植过的uboot在开发板光盘的BSP中
  • BSP就是board support package(板级支持包,一般由开发板供应商提供),里面的内容就是这个开发板的所有相关的源代码、文档、教程等。
  • 解压 BSP:tar -jxvf qt_x210v3_130807.tar.bz2
  • 配置 uboot
    • cd进入uboot源码的根目录
    • 然后在根目录下执行:make x210_sd_config
    • 执行配置命令后,出现:Configuring for x210_sd board...,完成配置

uboot的源码目录分析

文件介绍

文件名 作用
gitignore git工具的文件,git是一个版本管理工具(类似的还有个svn)
arm_config.mk 后缀是.mk,是一个Makefile文件,将来在某个Makefile中会去调用它
三个Changelog文件 修改记录文件,该文件记录了这个uboot项目的版本变迁以及每个版本较上个版本修改的记录
config.mk 和arm_config.mk差不多性质
COPYING 版权声明,uboot本身是GPL许可证的
CREDITS 鸣谢,里面记录了对uboot有贡献的人,感谢目录
image_split 一个脚本,用来分割uboot.bin到BL1的
MAINTAINERS 维护者,当前参与维护uboot源码的社区工作者
MAKEALL 编译uboot的脚本
Makefile 重要,是uboot源代码的主Makefile,将来整个uboot被编译时就是用这个Makefile管理编译
mk 快速编译的脚本,先清理后配置最后编译而已
mkconfig 重要,是uboot配置阶段的主要配置脚本
mkmovi 一个脚本,和iNand/SD卡启动有关
README 简单的使用说明书
rules.mk uboot的Makefile使用的规则

开发板越来越多,board目录下文件夹越来越多不方便管控。于是乎uboot就新增了一种机制,在board下放厂家目录(vendor目录,以具体芯片厂商名字命名),然后将这个IC厂商的所有芯片开发板都丢到这个vendor目录下面去。所以大家会发现我们X210对应的开发板目录在 board/samsung/x210。为避免历史原因造成的兼容性麻烦,最开始时board目录下的开发板名字没有挪移到厂商目录下面去。这样就造成后来的人不知道原委的感到很奇怪,感觉很混乱。

源码目录介绍

目录名 作用
api 硬件无关的功能函数的API,这些函数是uboot本身使用的
api_examples API相关的测试事例代码
board board文件夹下每一个文件都代表一个开发板,这个文件夹下面放的文件就是用来描述这一个开发板的信息的。board目录下有多少个文件夹,就表示当前这个uboot已经被移植到多少个开发板上了(当前的uboot支持多少个开发板)
common 与具体硬件无关的普遍适用的一些代码,譬如控制台实现、crc校验的。主要是两类:</br>- 一类是cmd开头的,是用来实现uboot的命令系统的;</br>- 另一类是env开头的,是用来实现环境变量的
cpu SoC相关,里面存放的代码都是SoC相关初始化和控制代码(譬如CPU的、中断的、串口等SoC内部外设的,包括起始代码start.S也在这里)。里面很多子文件夹,每一个子文件夹就是一个SoC系列,这个文件严格和硬件相关。自己的开发板和三星的开发板虽然板子设计不同但是SoC都是同一个,因此实际移植时这个目录几乎不用动。
disk 磁盘有关
doc 文档目录,很多uboot相关文档
drivers 驱动。从linux源代码中取出来的linux设备驱动,如网卡驱动、Inand/SD卡、NandFlash等的驱动。uboot中的驱动其实就是linux中的驱动,但是linux是操作系统而uboot只是个裸机程序,因此这种移植会有不同
examples 示例代码
fs filesystem,文件系统。从linux源代码中移植过来的,用来管理Flash等资源
include 头文件目录
lib_xxx 架构相关的库文件。 譬如 lib_arm 里面就是arm架构使用的一些库文件。lib_generic 里是所有架构通用的库文件。这类文件夹中的内容移植时基本不用管
libfdt 设备树有关的。linux内核在3.4左右的版本的时候更改了启动传参的机制,改用设备树来进行启动传参,进行硬件信息的描述
nand_spl nand相关代码
net 网络相关的代码,譬如uboot中的 tftp nfs ping 命令 都是在这里实现的
onenand开头 onenand相关的代码,三星独有,标准uboot中应该是没有
post -
sd_fusing 实现了烧录uboot镜像到SD卡的代码
tools 一些工具类的代码。譬如mkimage

uboot主Makefile分析

uboot 版本号的确定(Makefile的24-29行)

VERSION = 1
PATCHLEVEL = 3
SUBLEVEL = 4
EXTRAVERSION =
U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
VERSION_FILE = $(obj)include/version_autogenerated.h
CURDIR = $(shell pwd)
  • uboot的版本号分3个级别:

    • VERSION:主板本号
    • PATCHLEVEL:次版本号
    • SUBLEVEL:再次版本号
    • EXTRAVERSION:另外附加的版本信息
  • Makefile中版本号最终生成了一个变量 U_BOOT_VERSION,这个变量记录了Makefile中配置的版本号。

  • include/version_autogenerated.h 文件是编译过程中自动生成的一个文件。它里面的内容是一个宏定义,宏定义的值内容就是我们在Makefile中配置的uboot的版本号。

    #define U_BOOT_VERSION "U-Boot 1.3.4"
    

HOSTARCH和HOSTOS

HOSTARCH := $(shell uname -m | \
	sed -e s/i.86/i386/ \
	    -e s/x.86_64/i386/ \
	    -e s/sun4u/sparc64/ \
	    -e s/arm.*/arm/ \
	    -e s/macppc/ppc/)

HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
	    sed -e 's/\(cygwin\).*/cygwin/')

export	HOSTARCH HOSTOS
  1. uname -m 得到 CPU 的版本号 i686,后通过文本替换工具 sed 改为 i386
  2. HOSTARCH这个名字:
    • HOST是主机,就是当前在做开发用的这台电脑就叫主机;
    • ARCH是architecture(架构)的缩写,表示CPU的架构
    • 所以HOSTARCH就表示主机的CPU的架构

静默编译(50-54行)

ifeq (,$(findstring s,$(MAKEFLAGS)))
XECHO = echo
else
XECHO = :
endif
  1. 平时默认编译时命令行会打印出来很多编译信息。但是有时候我们不希望看到这些编译信息,就后台编译即可。这就叫静默编译。
  2. 使用方法就是编译时 make -s-s 会作为MAKEFLAGS传给Makefile,在50-54行这段代码作用下XECHO变量就会被变成空(默认等于echo),于是实现了静默编译。

2种编译方法(原地编译和单独输出文件夹编译)

ifdef o
ifeq ("$(origin o)", "command line")
build_dir := $(o)
endif
endif

ifneq ($(build_dir),)
saved-output := $(build_dir)

# attempt to create a output directory.
$(shell [ -d ${build_dir} ] || mkdir -p ${build_dir})

# verify if it was successful.
build_dir := $(shell cd $(build_dir) && /bin/pwd)
$(if $(build_dir),,$(error output directory "$(saved-output)" does not exist))
endif # ifneq ($(build_dir),)
  1. 默认情况下当前文件夹中的.c文件,编译出来的.o文件会放在同一文件夹下。这种方式叫原地编译。

  2. 原地编译有一些坏处:

    • 第一,污染了源文件目录。
    • 第二个缺陷就是一套源代码只能按照一种配置和编译方法进行处理,无法同时维护2个或2个以上的配置编译方式。
  3. 单独输出文件夹:在编译时另外指定一个输出目录,将来所有的编译生成的.o文件或生成的其他文件全部丢到那个输出目录下去。源代码目录不做任何污染,这样输出目录就承载了本次配置编译的所有结果。

  4. 具体用法:如果需要指定具体的输出目录编译则有2种方式来指定输出目录。(具体参考Makefile 56-76行注释内容)

    • 第一种:make O=输出目录
    • 第二种:export BUILD_DIR=输出目录,然后再make
    • 如果两个都指定了(既有BUILD_DIR环境变量存在,又有O=xx),则O=xx具有更高优先级

    这种在配置时也要指定输出目录:make x210_sd_config O=输出目录

OBJTREE、SRCTREE、TOPDIR

OBJTREE		:= $(if $(build_dir),$(build_dir),$(CURDIR))
SRCTREE		:= $(CURDIR)
TOPDIR		:= $(SRCTREE)
LNDIR		:= $(OBJTREE)
export	TOPDIR SRCTREE OBJTREE
  • OBJTREE:编译出的.o文件存放的目录的根目录。在默认编译下,OBJTREE等于当前目录;在O=xx编译下,OBJTREE就等于我们设置的那个输出目录。
  • SRCTREE: 源码目录,其实就是源代码的根目录,也就是当前目录。

MKCONFIG

MKCONFIG	:= $(SRCTREE)/mkconfig
export MKCONFIG

Makefile中定义的一个变量,它的值就是我们源码根目录下面的mkconfig。这个mkconfig是一个脚本,这个脚本就是uboot配置阶段的配置脚本。

include/config.mk

ifeq ($(obj)include/config.mk,$(wildcard $(obj)include/config.mk))
include $(obj)include/config.mk
export	ARCH CPU BOARD VENDOR SOC
  • $(wildcard $(obj)include/config.mk)$(wildcard) 是Makefile中的通配符,也就是如果 $(obj)include/config.mk 存在就执行下面的内容

  • include $(obj)include/config.mk:config.mk 是有关 x210 的 Soc 架构信息

    ARCH   = arm
    CPU    = s5pc11x
    BOARD  = x210
    VENDOR = samsung
    SOC    = s5pc110
    
    • include/config.mk不是源码自带的(你在没有编译过的源码目录下是找不到这个文件的),要在配置过程(make x210_sd_config)中才会生成这个文件。因此这个文件的值和我们配置过程有关,是由配置过程根据我们的配置自动生成的。
  • 这里的配置值来自于2589行那里的配置项。如果我们要更改这里的某个配置值要到2589行那里调用MKCONFIG脚本传参时的参数。

    smdkv210single_config :	unconfig
        @$(MKCONFIG) $(@:_config=) arm s5pc11x smdkc110 samsung s5pc110
        @echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/smdkc110/config.mk
    

CROSS_COMPILE 交叉编译工具链

ifndef CROSS_COMPILE
    ifeq ($(ARCH),arm)
    CROSS_COMPILE = /usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-
    endif
endif

export	CROSS_COMPILE
  • ARCH = arm,值来自于我们的配置过程
  • 我们可以在Makefile中去更改设置CROSS_COMPILE的值,也可以在编译时用 make CROSS_COMPILE=xxxx 来设置,而且编译时传参的方法可以覆盖Makefile里面的设置。

配置文件 config.mk

include $(TOPDIR)/config.mk
  1. 它是当前uboot项目目录下的 config.mk 文件

  2. 这个文件首先确定了编译工具链的具体路径和编译选项

    HOSTCFLAGS	= -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
    HOSTSTRIP	= strip
       
    AS	= $(CROSS_COMPILE)as
    LD	= $(CROSS_COMPILE)ld
    CC	= $(CROSS_COMPILE)gcc
    CPP	= $(CC) -E
    AR	= $(CROSS_COMPILE)ar
    NM	= $(CROSS_COMPILE)nm
    LDR	= $(CROSS_COMPILE)ldr
    STRIP	= $(CROSS_COMPILE)strip
    OBJCOPY = $(CROSS_COMPILE)objcopy
    OBJDUMP = $(CROSS_COMPILE)objdump
    RANLIB	= $(CROSS_COMPILE)RANLIB
    
  3. 最主要的是它引入了 autoconf.mk 这个文件

    sinclude $(OBJTREE)/include/autoconf.mk
    
    • autoconfig.mk文件不是源码提供的,是配置过程自动生成的。

      CONFIG_CMD_FAT=y
      CONFIG_USB_OHCI=y
      CONFIG_SYS_CLK_FREQ=24000000
      CONFIG_CMD_ITEST=y
      # 省略 ......
      CONFIG_BOOTARGS="console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3"
      # 省略 ......
      CONFIG_BOOTCOMMAND="movi read kernel 30008000; movi read rootfs 30B00000 300000; bootm 30008000 30B00000"
      CONFIG_BAUDRATE=115200
      
    • 这个文件的作用就是用来指导整个uboot的编译过程。这个文件的内容其实就是很多 CONFIG_ 开头的宏(可以理解为变量),这些宏/变量会影响我们uboot编译过程的走向(原理就是条件编译)。在uboot代码中有很多地方使用条件编译进行编写,这个条件编译是用来实现可移植性的

    • 这个文件不是凭空产生的,原材料在源码目录的 inlcude/configs/xxx.h 头文件。(X210开发板中为 include/configs/x210_sd.h)。这个h头文件里面全都是宏定义,这些宏定义就是我们对当前开发板的移植。每一个开发板的移植都对应这个目录下的一个头文件,这个头文件里每一个宏定义都很重要,这些配置的宏定义就是我们移植uboot的关键所在。

  4. 指定了uboot的链接脚本 142 行,关于连接脚本的内容见附录

    ifndef LDSCRIPT
    LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds
       
    LDFLAGS += -Bstatic -T $(LDSCRIPT) $(PLATFORM_LDFLAGS)
    LDFLAGS += -Ttext $(TEXT_BASE)
    endif
    

各要素的配置文件 (config.mk)

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	VENDOR
BOARDDIR = $(VENDOR)/$(BOARD)
else
BOARDDIR = $(BOARD)
endif
ifdef	BOARD
sinclude $(TOPDIR)/board/$(BOARDDIR)/config.mk	# include board specific rules
endif
  • ARCH、CPU、SOC 都是设置一些编译相关的选项,比如arm_config.mk 的内容如下:

    PLATFORM_CPPFLAGS += -DCONFIG_ARM -D__ARM__
    
  • 重要的是 board/samsung/x210/config.mk,它规定了镜像文件的链接地址

    TEXT_BASE = 0xc3e00000
    
    • TEXT_BASE 是将来我们整个uboot链接时指定的链接地址。因为uboot中启用了虚拟地址映射,因此这个C3E00000地址就等于0x23E00000(也可能是33E00000具体地址要取决于uboot中做的虚拟地址映射关系)。

自动推导规则(config.mk)

$(obj)%.s:	%.S
	$(CPP) $(AFLAGS) -o $@ $<
$(obj)%.o:	%.S
	$(CC) $(AFLAGS) -c -o $@ $<
$(obj)%.o:	%.c
	$(CC) $(CFLAGS) -c -o $@ $<

设置好各个编译选项之后,就可以开始编译了

定义各个需要编译的目标文件

OBJS  = cpu/$(CPU)/start.o
ifeq ($(CPU),i386)
OBJS += cpu/$(CPU)/start16.o
OBJS += cpu/$(CPU)/reset.o
endif
# 省略 ......

从config.mk中返回后,Makefile就给出了需要编译的各个目标文件和库文件

主Makefile中的第一个目标 all

all:		$(ALL)

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

$(obj)u-boot.srec:	$(obj)u-boot
		$(OBJCOPY) ${OBJCFLAGS} -O srec $< $@
# 省略 ......
  1. 291行出现了整个主Makefile中第一个目标all(也就是默认目标,我们直接在uboot根目录下make其实就等于make all,就等于make这个目标)

  2. 目标中有一些比较重要的。譬如:u-boot是最终编译链接生成的elf格式的可执行文件,

  3. unconfig字面意思来理解就是未配置。这个符号用来做为我们各个开发板配置目标的依赖。目标是当我们已经配置过一个开发板后再次去配置时还可以配置。

    unconfig:
        @rm -f $(obj)include/config.h $(obj)include/config.mk \
            $(obj)board/*/config.tmp $(obj)board/*/*/config.tmp \
            $(obj)include/autoconf.mk $(obj)include/autoconf.mk.dep \
            $(obj)board/$(VENDOR)/$(BOARD)/config.mk
    
  4. 我们配置开发板时使用:make x210_sd_config,因此分析 x210_sd_config 肯定是主Makefile中的一个目标。

    x210_sd_config :	unconfig
        @$(MKCONFIG) $(@:_config=) arm s5pc11x x210 samsung s5pc110
        @echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/x210/config.mk
    

x210_sd_config 目标的分析

  1. mkconfig脚本的6个参数

    @$(MKCONFIG) $(@:_config=) arm s5pc11x x210 samsung s5pc110
    
    • $(MKCONFIG) = $(SRCTREE)/mkconfig = mkconfig

    • @ 表示目标,这里就是 x210_sd_config

    • : 表示字符串处理

    • _config= 表示把 @ 字符串中的 _config 用空替换。所以 x210_sd_config 里的 _config 部分用空替换,得到:x210_sd,这就是第一个参数,所以:

      $1:	x210_sd
      $2:	arm
      $3: s5pc11x
      $4:	x210
      $5: samsumg
      $6:	s5pc110
      $# = 6
      

经过执行 @$(MKCONFIG) $(@:_config=) arm s6pc11x x210 samsung s5pc110 之后,我们得到:

  1. 创建include/config.mk文件(mkconfig文件123-129行)

    include/config.mk文件是为了让主Makefile在第133行去包含的(详解见2.4.3.3节)。

  2. 创建(默认情况)/追加(make -a时追加)include/config.h文件(mkconfig文件的134-141行)。

    • 这个文件里面的内容就一行 #include <configs/x210_sd.h>,这个头文件是我们移植x210开发板时,对开发板的宏定义配置文件。这个文件是我们移植x210时最主要的文件。
  3. x210_sd.h 文件会被用来生成一个 autoconfig.mk 文件,这个文件会被主Makefile引入,指导整个编译过程。这里面的这些宏定义会影响我们对uboot中大部分.c文件中一些条件编译的选择。从而实现最终的可移植性。

至此 uboot 的主 Makefile 文件分析完成。

附录

MKCONFIG 脚本分析

判断输入参数

while [ $# -gt 0 ] ; do
	case "$1" in
	--) shift ; break ;;
	-a) shift ; APPEND=yes ;;
	-n) shift ; BOARD_NAME="${1%%_config}" ; shift ;;
	*)  break ;;
	esac
done
  1. break 是跳出整个 while 循环,而不是 case 语句
  2. shift 是把参数左移

判断输入参数

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

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

echo "Configuring for ${BOARD_NAME} board..."
  • 第23行:其实就是看 BOARD_NAME 变量是否有值,如果有值就维持不变;如果无值就给他赋值为 $1,实际分析结果:BOARD_NAME=x210_sd
  • 第25行:如果$#小于4,则exit 1(mkconfig脚本返回1)
  • 第26行:如果$#大于6,则也返回1.

所以:mkconfig脚本传参只能是4、5、6,如果大于6或者小于4都不行。

创建符号链接

cd ./include
rm -f asm
ln -s asm-$2 asm
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

if [ "$3" = "s5pc11x" ] ; then
        rm -f regs.h
        ln -s $6.h regs.h
        rm -f asm-$2/arch
        ln -s arch-$3 asm-$2/arch
fi
  • 从第33行到第118行,都是在创建符号链接。这些符号链接文件的存在就是整个配置过程的核心,这些符号链接文件(文件夹)的主要作用是给头文件包含等过程提供指向性连接。根本目的是让uboot具有可移植性。
  • uboot可移植性的实现原理:在uboot中有很多彼此平行的代码,各自属于各自不同的架构/CPU/开发板,我们在具体到一个开发板的编译时用符号连接的方式提供一个具体的名字的文件夹供编译时使用。这样就可以在配置的过程中通过不同的配置使用不同的文件,就可以正确的包含正确的文件。
  • 创建的符号链接:
    • 第一个:在include目录下创建asm文件,指向asm-arm。(46-48行)
    • 第二个:在inlcude/asm-arm下创建一个arch文件,指向include/asm-arm/arch-s5pc110
    • 第三个:在include目录下创建regs.h文件,指向include/s5pc110.h
    • 删除第二个。
    • 第四个:在inlcude/asm-arm下创建一个arch文件,指向include/asm-arm/arch-s5pc11x
    • 第五个:在include/asm-arm下创建一个proc文件,指向include/asm-arm/proc-armv
    • 总结:一共创建了4个符号链接。这4个符号链接将来在写代码过程中,头文件包含时非常有用。譬如一个头文件包含可能是:#include <asm/xx.h>

生成 include/config.mk 文件

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

#
# 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

这就是 include/config.mk 文件的由来

生成 include/x210_sd.h 文件

echo "/* Automatically generated - do not edit */" >>config.h
echo "#include <configs/$1.h>" >>config.h

这个文件中包含的是各种宏开关

#define CONFIG_MPAD		1		//lxg added
#define CONFIG_S5PC110	1		/* in a SAMSUNG S3C6410 SoC */
#define CONFIG_S5PC11X	1		/* in a SAMSUNG S3C64XX Family  */
#define CONFIG_X210		1
//省略 ....

Uboot 的链接脚本

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
	. = 0x00000000;

	. = ALIGN(4);
	.text      :
	{
	  cpu/s5pc11x/start.o	(.text)
	  cpu/s5pc11x/s5pc110/cpu_init.o	(.text)
	  board/samsung/x210/lowlevel_init.o	(.text)
          cpu/s5pc11x/onenand_cp.o      (.text)                 
          cpu/s5pc11x/nand_cp.o (.text)                     
          cpu/s5pc11x/movi.o (.text) 
          common/secure_boot.o (.text) 
	  common/ace_sha1.o (.text)
	  cpu/s5pc11x/pmic.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);
	.mmudata : { *(.mmudata) }

	. = ALIGN(4);
	__bss_start = .;
	.bss : { *(.bss) }
	_end = .;
}
  1. ENTRY(_start)用来指定整个程序的入口地址。所谓入口地址就是整个程序的开头地址,可以认为就是整个程序的第一句指令。有点像C语言中的main。
  2. 指定程序的链接地址有2种方法:
    • 一种是在Makefile中ld的flags用 -Ttext 0x20000000来指定;
    • 第二种是在链接脚本的SECTIONS开头用 .=0x20000000 来指定。
    • 既可在链接脚本中指定也可在ld flags中用-Ttext来指定。两个都指定以后以-Ttext指定的为准。
  3. uboot的最终链接起始地址就是在Makefile中用-Ttext来指定的,注意TEXT_BASE变量。最终来源是Makefile中配置对应的命令中,在 make xxx_config 时得到的。
  4. 在代码段中注意文件排列的顺序。指定必须放在前面部分的那些文件就是那些必须安排在前16KB内的文件,这些文件中的函数在前16KB会被调用。
  5. 链接脚本中除了.text .data .rodata .bss段等编译工具自带的段之外,编译工具还允许我们自定义段。譬如uboot总的 .u_boot_cmd 段就是自定义段。