分类: 嵌入式
2011-02-04 23:59:25
# Allow boards to use custom optimize
flags on a per dir/file basis
BCURDIR := $(notdir $(CURDIR))
$(obj)%.s: %.S
$(CPP)
$(AFLAGS) $(AFLAGS_$(@F)) $(AFLAGS_$(BCURDIR)) -o $@ $<
$(obj)%.o: %.S
$(CC) $(AFLAGS) $(AFLAGS_$(@F))
$(AFLAGS_$(BCURDIR)) -o $@ $< -c
$(obj)%.o: %.c
$(CC) $(CFLAGS) $(CFLAGS_$(@F))
$(CFLAGS_$(BCURDIR)) -o $@ $< -c
$(obj)%.i: %.c
$(CPP)
$(CFLAGS) $(CFLAGS_$(@F)) $(CFLAGS_$(BCURDIR)) -o $@ $< -c
$(obj)%.s: %.c
$(CC) $(CFLAGS) $(CFLAGS_$(@F))
$(CFLAGS_$(BCURDIR)) -o $@ $< -c -S
例如:根据以上的定义,以“.s”结尾的目标文件将根据第一条规则由同名但后缀为“.S”的源文件来生成,若不存在“.S”结尾的同名文件则根据最后一条规则由同名的“.c”文件生成。
下面回来接着分析Makefile的内容:
# U-Boot objects....order is important
(i.e. start must be first)
OBJS
= cpu/$(CPU)/start.o
LIBS += cpu/$(CPU)/lib$(CPU).a
ifdef SOC
LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a
endif
ifeq ($(CPU),ixp)
LIBS += cpu/ixp/npe/libnpe.a
endif
LIBS += lib_$(ARCH)/lib$(ARCH).a
LIBS += fs/cramfs/libcramfs.a
fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a \
fs/reiserfs/libreiserfs.a
fs/ext2/libext2fs.a fs/yaffs2/libyaffs2.a \
fs/ubifs/libubifs.a
… …
LIBS += common/libcommon.a
LIBS += libfdt/libfdt.a
LIBS += api/libapi.a
LIBS += post/libpost.a
LIBS := $(addprefix $(obj),$(LIBS))
LIBS变量指明了U-Boot需要的库文件,包括平台/开发板相关的目录、通用目录下相应的库,都通过相应的子目录编译得到的。
对于mini2440开发板,以上跟平台相关的有以下几个:
cpu/$(CPU)/start.o
board/$(VENDOR)/common/lib$(VENDOR).a
cpu/$(CPU)/lib$(CPU).a
cpu/$(CPU)/$(SOC)/lib$(SOC).a
lib_$(ARCH)/lib$(ARCH).a
其余都是与平台无关的。
ifeq ($(CONFIG_NAND_U_BOOT),y)
NAND_SPL = nand_spl
U_BOOT_NAND = $(obj)u-boot-nand.bin
endif
ifeq ($(CONFIG_ONENAND_U_BOOT),y)
ONENAND_IPL = onenand_ipl
U_BOOT_ONENAND = $(obj)u-boot-onenand.bin
ONENAND_BIN ?=
$(obj)onenand_ipl/onenand-ipl-2k.bin
endif
对于有的开发板,U-Boot支持在NAND Flash启动,这些开发板的配置文件定义了CONFIG_NAND_U_BOOT,CONFIG_ONENAND_U_BOOT。对于s3c2440,U-Boot原始代码并不支持NAND Flash启动,因此也没有定义这两个宏。
ALL += $(obj)u-boot.srec $(obj)u-boot.bin
$(obj)System.map $(U_BOOT_NAND) $(U_BOOT_ONENAND)
all: $(ALL)
其中U_BOOT_NAND与U_BOOT_ONENAND 为空,而u-boot.srec,u-boot.bin,System.map都依赖与u-boot。因此执行“make all”命令将生成u-boot,u-boot.srec,u-boot.bin,System.map 。其中u-boot是ELF文件,u-boot.srec是Motorola S-Record format文件,System.map 是U-Boot的符号表,u-boot.bin是最终烧写到开发板的二进制可执行的文件。
下面再来分析u-boot.bin文件生成的过程。ELF格式“u-boot”文件生成规则如下:
$(obj)u-boot: depend $(SUBDIRS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT)
$(obj)u-boot.lds
$(GEN_UBOOT)
ifeq ($(CONFIG_KALLSYMS),y)
smap=`$(call
SYSTEM_MAP,u-boot) | \
awk
'$$2 ~ /[tTwW]/ {printf $$1 $$3 "\\\\000"}'` ; \
$(CC)
$(CFLAGS) -DSYSTEM_MAP="\"$${smap}\"" \
-c
common/system_map.c -o $(obj)common/system_map.o
$(GEN_UBOOT)
$(obj)common/system_map.o
endif
这里生成的$(obj)u-boot目标就是ELF格式的U-Boot文件了。由于CONFIG_KALLSYMS未定义,因此ifeq ($(CONFIG_KALLSYMS),y)与endif间的代码不起作用。
其中depend,$(SUBDIRS),$(OBJS),$(LIBBOARD),$(LIBS),$(LDSCRIPT), $(obj)u-boot.lds是$(obj)u-boot的依赖,而$(GEN_UBOOT)编译命令。
下面分析$(obj)u-boot的各个依赖:
1依赖目标depend
# Explicitly make _depend in subdirs
containing multiple targets to prevent
# parallel sub-makes creating .depend files
simultaneously.
depend dep: $(TIMESTAMP_FILE) $(VERSION_FILE) $(obj)include/autoconf.mk
for
dir in $(SUBDIRS) cpu/$(CPU) $(dir $(LDSCRIPT)) ; do \
$(MAKE)
-C $$dir _depend ; done
对于$(SUBDIRS),cpu/$(CPU),$(dir $(LDSCRIPT))中的每个元素都进入该目录执行“make
_depend”,生成各个子目录的.depend文件,.depend列出每个目标文件的依赖文件。
2依赖SUBDIRS
SUBDIRS = tools \
examples/standalone \
examples/api
$(SUBDIRS): depend
$(MAKE)
-C $@ all
执行tools ,examples/standalone ,examples/api目录下的Makefile。
3OBJS
OBJS的值是“cpu/arm920t/start.o”。它使用如下代码编译得到:
$(OBJS): depend
$(MAKE)
-C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))
以上规则表明,对于OBJS包含的每个成员,都进入cpu/$(CPU)目录(即cpu/arm920t)编译它们。
4LIBBOARD
LIBBOARD = board/$(BOARDDIR)/lib$(BOARD).a
LIBBOARD := $(addprefix
$(obj),$(LIBBOARD))
… …
$(LIBBOARD): depend $(LIBS)
$(MAKE)
-C $(dir $(subst $(obj),,$@))
这里LIBBOARD的值是
$(obj)board/samsung/mini2440/libmini2440.a。make执行board/samsung/mini2440/目录下的Makefile,生成libmini2440.a 。
5LIBS
LIBS变量中的每个元素使用如下的规则编译得到:
$(LIBS): depend
$(SUBDIRS)
$(MAKE)
-C $(dir $(subst $(obj),,$@))
上面的规则表明,对于LIBS中的每个成员,都进入相应的子目录执行“make”命令编译它们。例如对于LIBS中的“common/libcommon.a”成员,程序将进入common目录执行Makefile,生成libcommon.a 。
6LDSCRIPT
LDSCRIPT := $(SRCTREE)/cpu/$(CPU)/u-boot.lds
… …
$(LDSCRIPT): depend
$(MAKE)
-C $(dir $@) $(notdir $@)
“$(MAKE) -C $(dir $@)
$(notdir $@)”命令经过变量替换后就是“make
-C cpu/arm920t/ u-boot.lds”。也就是转到cpu/arm920t/目录下,执行“make u-boot.lds”命令。
7$(obj)u-boot.lds
$(obj)u-boot.lds: $(LDSCRIPT)
$(CPP)
$(CPPFLAGS) $(LDPPFLAGS) -ansi -D__ASSEMBLY__ -P - <$^ >$@
以上执行结果实质上是将cpu/arm920t/u-boot.lds经编译器简单预处理后输出到U-Boot顶层目录下的u-boot.lds文件。其中的cpu/arm920t/u-boot.lds文件内容如下:
/* 输出为ELF文件,小端方式, */
OUTPUT_FORMAT("elf32-littlearm",
"elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
.
= 0x00000000;
.
= ALIGN(4);
.text
:
{
/* cpu/arm920t/start.o放在最前面,保证最先执行的是start.o */
cpu/arm920t/start.o (.text)
/*以下2个文件必须放在前4K,因此也放在前面,其中board/samsung/mini2440/lowlevel_init.o
包含内存初始化所需代码,而
board/samsung/mini2440/nand_read.o 包含U-Boot从NAND Flash搬运自身的代码 */
board/samsung/mini2440/lowlevel_init.o
(.text)
board/samsung/mini2440/nand_read.o
(.text)
/* 其他文件的代码段 */
*(.text)
}
/* 只读数据段 */
.
= ALIGN(4);
.rodata
: { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
/* 代码段 */
.
= ALIGN(4);
.data
: { *(.data) }
/* u-boot自定义的got段 */
.
= ALIGN(4);
.got
: { *(.got) }
.
= .;
__u_boot_cmd_start
= .; /*将 __u_boot_cmd_start指定为当前地址 */
.u_boot_cmd
: { *(.u_boot_cmd) } /* 存放所有U-Boot命令对应的cmd_tbl_t结构体 */
__u_boot_cmd_end
= .; /* 将__u_boot_cmd_end指定为当前地址 */
/* bss段 */
.
= ALIGN(4);
__bss_start
= .;
.bss
(NOLOAD) : { *(.bss) . = ALIGN(4); }
_end
= .; /* 将_end指定为当前地址 */
}
u-boot.lds实质上是U-Boot连接脚本。对于生成的U-Boot编译生成的“u-boot”文件,可以使用objdump命令可以查看它的分段信息:
$
objdump -x u-boot | more
部分输出信息如下:
u-boot:
file format elf32-little
u-boot
architecture: UNKNOWN!, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x33f80000
Program Header:
LOAD off 0x00008000 vaddr
0x33f80000 paddr 0x33f80000 align 2**15
filesz 0x0002f99c memsz 0x00072c94 flags rwx
STACK off 0x00000000 vaddr
0x00000000 paddr 0x00000000 align 2**2
filesz 0x00000000 memsz 0x00000000 flags rwx
Sections:
Idx Name Size VMA
LMA File off
Algn
0
.text 00024f50 33f80000
33f80000 00008000 2**5
CONTENTS, ALLOC, LOAD,
READONLY, CODE
1
.rodata 00008b78 33fa4f50
33fa4f50 0002cf50 2**3
CONTENTS, ALLOC, LOAD, READONLY,
DATA
2
.data 00001964 33fadac8
33fadac8 00035ac8 2**2
CONTENTS, ALLOC, LOAD, DATA
3
.u_boot_cmd 00000570 33faf42c
33faf42c 0003742c 2**2
CONTENTS, ALLOC, LOAD, DATA
4
.bss 00043294 33fafa00
33fafa00 0003799c 2**8
ALLOC
… …
u-boot.lds还跟U-Boot启动阶段复制代码到RAM空间的过程以及U-Boot命令执行过程密切相关,具体请结合U-Boot源代码理解。
编译命令GEN_UBOOT
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) $$UNDEF_SYM $(__OBJS) \
--start-group
$(__LIBS) --end-group $(PLATFORM_LIBS) \
-Map
u-boot.map -o u-boot
以上命令使用$(LDFLAGS)作为连接脚本,最终生成“u-boot”文件。
u-boot.bin文件生成过程
生成u-boot.bin文件的规则如下:
$(obj)u-boot.bin: $(obj)u-boot
$(OBJCOPY)
${OBJCFLAGS} -O binary $< $@
从U-Boot编译输出信息中可以知道上面的命令实质上展开为:
arm-linux-objcopy
--gap-fill=0xff -O binary u-boot u-boot.bin
编译命令中的“-O binary”选项指定了输出的文件为二进制文件。而“--gap-fill=0xff”选项指定使用“0xff”填充段与段间的空闲区域。这条编译命令实现了ELF格式的U-Boot文件到BIN格式的转换。
System.map文件的生成
System.map是U-Boot的符号表,它包含了U-Boot的全局变量和函数的地址信息。将System.map生成的规则如下:
SYSTEM_MAP = \
$(NM)
$1 | \
grep
-v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | \
LC_ALL=C
sort
$(obj)System.map: $(obj)u-boot
@$(call
SYSTEM_MAP,$<) > $(obj)System.map
arm-linux-nm u-boot | grep -v
'\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | LC_ALL=C
sort > System.map
也就是将arm-linux-nm命令查看u-boot的输出信息经过过滤和排序后输出到System.map。为了了解System.map文件的作用,打开System.map:
33f80000 T _start
33f80020 t _undefined_instruction
33f80024 t _software_interrupt
33f80028 t _prefetch_abort
33f8002c t _data_abort
33f80030 t _not_used
33f80034 t _irq
33f80038 t _fiq
33f80040 t _TEXT_BASE
33f80044 T _armboot_start
33f80048 T _bss_start
33f8004c T _bss_end
… …
System.map表示的是地址标号到该标号表示的地址的一个映射关系。System.map每一行的格式都是“addr type name”,addr是标号对应的地址值,name是标号名,type表示标号的类型。
U-Boot的编译和运行并不一定要生成System.map,这个文件主要是提供给用户或外部程序调试时使用的。