Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1319698
  • 博文数量: 482
  • 博客积分: 13297
  • 博客等级: 上将
  • 技术积分: 2890
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-12 16:25
文章分类

全部博文(482)

文章存档

2012年(9)

2011年(407)

2010年(66)

分类: 嵌入式

2011-02-04 23:59:25

5指定隐含的编译规则

# 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_BOOTCONFIG_ONENAND_U_BOOT。对于s3c2440U-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_NANDU_BOOT_ONENAND 为空,而u-boot.srecu-boot.binSystem.map都依赖与u-boot。因此执行“make all”命令将生成u-bootu-boot.srecu-boot.binSystem.map 。其中u-bootELF文件,u-boot.srecMotorola 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.amake执行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-BootNAND 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.mapU-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,这个文件主要是提供给用户或外部程序调试时使用的。

阅读(1022) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~