U-Boot-1.1.6顶层Makefile分析(三)————make all

  U-Boot-1.16 Makefile分析第三篇,按照make all的思路来分析。

1、执行make all

ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)
all:        $(ALL)

  u-boot.srec为S-Record格式的image;
  u-boot.bin为原始二进制文件的image;
  System.map按链接地址由小到大的顺序列出了所有符号,可以称之为系统映射表;
  U_BOOT_NAND = u-boot-nand.bin,应该是烧录到nand flash的二进制格式的image;
all: $(ALL):就是编译uboot的目标all了,all目标没有相应命令,所以要实现的就是相关的依赖文件,也就是变量ALL中的内容u-boot.srec 、u-boot.bin、System.map、u-boot-nand.bin。
  接下来对这四个依赖单独分析

2、u-boot.srec(依赖在下级标题)

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

   $(OBJCOPY) = $(CROSS_COMPILE)objcopy ,用来把一种目标文件中的内容复制到另一种类型的目标文件中。
   ${OBJCFLAGS} = DBGFLAGS= -g 。
   命令展开就是:

arm-linux-gnueabihf-objcopy -g -O srec u-boot u-boot.srec

  最终生成S-record格式文件。objcopy参考链接.

2.1 u-boot(依赖在下级标题)

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

  $(OBJDUMP) = $(CROSS_COMPILE)objdump,$(CROSS_COMPILE)就是指定的编译器。objdump命令是反汇编目标文件或者可执行文件的命令,而objdump -x 的作用是显示头文件信息。 objdump参考链接.

  $(LIBS) =各种带路径的库。所以,$(OBJDUMP) -x $(LIBS) 就是将库中的头文件信息显示出来。

  sed -n -e 's/.*(_u_boot_cmd.*\)/-u\1/p 匹配 "*_u_boot_cmd.*"改为-u__u_boot_cmd_.*,/p参数用来将执行后的结果输出出来。
  所以,UNDEF_SYM变量的内容就是,通过编译器的objdump工具将LIBS中的库文件包含的头文件输出,匹配其中带有 "__u_boot_cmd_"的字符串,并将其前缀修改为 “-u__u_boot_cmd_.*”。
  
  $(LNDIR) = 顶层Makefile所在路径。

   $(LD) 为编译器的连接工具。

   $(LDSCRIPT) = board/smdk2410/u-boot.lds。

   $(TEXT_BASE) 在board/smdk2410/config.mk文件中有定义,为:0x33F80000

   $(PLATFORM_LDFLAGS) 为空

   $(LDFLAGS) = -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)=-Bstatic -T board/smdk2410/u-boot.lds -Ttext0x33F80000 -L xx -lgcc.

   $(__OBJS) = cpu/arm920t/start.o。

   $(__LIBS) 开发板相关的和通用的库文件。

   $(PLATFORM_LIBS) -L (编译器依赖的库路径) -lgcc

   u-boot目标的命令展开后如下:

UNDEF_SYM=`arm-linux-gnueabihf-objdump -x lib_generic/libgeneric.a board/smdk2410/libsmdk2410.a cpu/arm920t/libarm920t.a cpu/arm920t/s3c24x0/libs3c24x0.a lib_arm/libarm.a fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a net/libnet.a disk/libdisk.a rtc/librtc.a dtt/libdtt.a drivers/libdrivers.a drivers/nand/libnand.a drivers/nand_legacy/libnand_legacy.a drivers/sk98lin/libsk98lin.a post/libpost.a post/cpu/libcpu.a common/libcommon.a |sed  -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
	cd /home/xx/linux/u-boot-1.1.6 && arm-linux-gnueabihf-ld -Bstatic -T /home/xx/linux/u-boot-1.1.6/board/smdk2410/u-boot.lds -Ttext 0x33F80000  $UNDEF_SYM cpu/arm920t/start.o \
		--start-group lib_generic/libgeneric.a board/smdk2410/libsmdk2410.a cpu/arm920t/libarm920t.a cpu/arm920t/s3c24x0/libs3c24x0.a lib_arm/libarm.a fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a net/libnet.a disk/libdisk.a rtc/librtc.a dtt/libdtt.a drivers/libdrivers.a drivers/nand/libnand.a drivers/nand_legacy/libnand_legacy.a drivers/sk98lin/libsk98lin.a post/libpost.a post/cpu/libcpu.a common/libcommon.a --end-group -L /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/../lib/gcc/arm-linux-gnueabihf/4.9.4 -lgcc \
		-Map u-boot.map -o u-boot

   其中,最主要的命令为:
arm-linux-gnueabihf-ld
   -Bstatic
   -T u-boot-1.1.6/board/smdk2410/u-boot.lds
   -Ttext 0x33F80000 cpu/arm920t/start.o
   --start-group
      xxx.a
   --end-group
   -L /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/…/lib/gcc/arm-linux-gnueabihf/4.9.4 -lgcc
   -Map u-boot.map -o u-boot


  -Bstatic,默认情况下,编译器优先进行动态库链接,-Bstatic选项后面跟的-l xxx.a会进行静态链接。
  --Ttext ADDRESS 代码段链接地址;参考链接.
   --start-group xxx.a  --end-group:链接时在xxx.a循环搜索相关引用。参考链接.
  -Map u-boot.map,生成u-boot.map。这个文件包含了很多映射的内容。
  -o u-boot,链接生成u-boot文件,这个就是elf格式的了。

2.1.1 depend

depend dep:
        for dir in $(SUBDIRS) ; do $(MAKE) -C $$dir _depend ; done

  make中的for语法,其中"dir"为SHELL中的变量 (展开时使用$$符号),"SUBDIRS"为Makefile中的变量(展开时使用$符号),在这个例子中dir会在每次循环中获取SUBDIRS变量中的内容,循环的次数由SUBDIRS变量包含的字符串数量决定,循环的内容由do…done包括。
  for循环中执行的语句do $(MAKE) -C $$dir _depend,展开后等价于:

		cd dir
		make _depend

  而SUBDIRS变量中的内容在上一篇中有提到,为 “tools examples post post/cpu”。但是在tools的Makefile中没有找到_depend目标,所以_depend目标应该是tools的Makefile中用include包含了其他makefile文件。使用grep查找_depend:

./Makefile:270:		for dir in $(SUBDIRS) ; do $(MAKE) -C $$dir _depend ; done
./rules.mk:26:_depend:	$(obj).depend

  很明显,_depend存在于rules.mk文件中,那么tools目录下的Makefile肯定会包含了rules.mk文件。
  在这里插入图片描述
  果然,在212行找到了相关代码,我看了四个目录底下的Makefile,都使用了include包含rules.mk文件。接下里看一下rules.mk中_depend的代码:
  rules.mk文件中的代码只包含了_depend目标,代码如下:

_depend:    $(obj).depend

$(obj).depend:  $(src)Makefile $(TOPDIR)/config.mk $(SRCS)
        @rm -f $@
        @for f in $(SRCS); do \
            g=`basename $$f | sed -e 's/\(.*\)\.\w/\1.o/'`; \
            $(CC) -M $(HOST_CFLAGS) $(CPPFLAGS) -MQ $(obj)$$g $$f >> $@ ; \
        done

  _depend依赖于.depend,.depend依赖于$(src)Makefile $(TOPDIR)/config.mk $(SRCS),src变量的内容在上一篇有讲到,在make时没有指定目标文件路径的情况下,src为空; $(TOPDIR)/config.mk为顶层Makefile路径下的config.mk文件;SRCS在tools目录下的Makefile有定义:

OBJ_LINKS   = environment.o crc32.o
OBJ_FILES   = img2srec.o mkimage.o envcrc.o gen_eth_addr.o bmp_logo.o

SRCS    := $(addprefix $(obj),$(OBJ_LINKS:.o=.c)) $(OBJ_FILES:.o=.c)

  $(OBJ_LINKS:.o=.c)是变量的高级用法,作用是将OBJ_LINKS中以.o结尾的字符串变为.c结尾的。所以SRC的内容就为:environment.c crc32.c img2srec.c mkimage.c envcrc.c gen_eth_addr.c bmp_logo.c。
  接着分析.depnd的命令:

		@rm -f $@
        @for f in $(SRCS); do \
            g=`basename $$f | sed -e 's/\(.*\)\.\w/\1.o/'`; \
            $(CC) -M $(HOST_CFLAGS) $(CPPFLAGS) -MQ $(obj)$$g $$f >> $@ ; \
        done

  删除路径下的目标文件,也就是旧的.depend。
  随后在for循环中,使用basename命令提取文件名(这个basename命令是shell命令),通过管道输出到sed命令sed -e ‘s/(.*).\w/\1.o/’,sed命令中用正则表达式将后缀替换成.o后缀。表达式参考链接.
   .* 表示任意字符;
   \. 表示 . 字符;
   \w用于匹配字母,数字或下划线字符
   \1对应前面 .* 的匹配
  
  $(CC):编译器类型;-M:自动找寻源文件中包含的头文件并将其输出,输出一个用于make的规则,该规则描述了这个main源文件的依赖关系; $(HOST_CFLAGS) : 在tools目录的Makefile中有定义,为 -traditional-cpp -Wall,-traditional-cpp作用是强行编译,-Wall作用是编译后显示所有警告;$(CPPFLAGS) 在tools目录下的Makefile中定义,为:
  -idirafter $(SRCTREE)/include \
  -idirafter $(OBJTREE)/include2 \
  -idirafter $(OBJTREE)/include \
  -DTEXT_BASE=$(TEXT_BASE) -DUSE_HOSTCC

  -idirafter dir把目录dir添加到第二包含路径中.如果某个头文件在主包含路径(用`-I’添加的路径)中没有找到,预处理器就搜索第二包含路径。
  -DTEXT_BASE:在gcc中 -D选项是用来在使用gcc/g++编译的时候定义宏。
    -D 后面直接跟宏命,相当于定义这个宏,默认这个宏的内容是1。
    -D 后面跟 key=value 表示定义key这个宏,它的内容是value。

    所以-DTEXT_BASE=$(TEXT_BASE)就是定义TEXT_BASE宏,$(TEXT_BASE)是要copy到sdram中运行的地址,是链接时就确定的地址。
    -DUSE_HOSTCC:定义DUSE_HOSTCC宏
  -MQ :将目标设置为与您指定的字符串完全相同,这里指定的目标文件就是sed命令用正则表达式替换后的.o文件。
  最后将这次编译过程产生的所有信息都重定向到.depend中,其中主要就是编译environment.o crc32.o img2srec.o mkimage.o envcrc.o gen_eth_addr.o bmp_logo.o这些目标所依赖的头文件。

2.1.2 version

version:
        @echo -n "#define U_BOOT_VERSION \"U-Boot " > $(VERSION_FILE); \
        echo -n "$(U_BOOT_VERSION)" >> $(VERSION_FILE); \
        echo -n $(shell $(CONFIG_SHELL) $(TOPDIR)/tools/setlocalversion \
             $(TOPDIR)) >> $(VERSION_FILE); \
        echo "\"" >> $(VERSION_FILE)

   version中的命令就是将一些版本信息输出到指定的文件中。
   “echo -n” -n参数表示不换行输出。
   $(VERSION_FILE) = include/version_autogenerated.h。
   $(CONFIG_SHELL) = /bin/sh
  第三行使用tools/setlocalversion脚本工具,这是个本地版本号的检查工具。

2.1.3 $(SUBDIRS)

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

   $(SUBDIRS) 为tools examples post post/cpu。所以这里就是切换到相应的目录下执行make all。

2.1.4 $(OBJS)

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

   $(OBJS) = cpu/arm920t/start.o。
   $(CPU)=arm920t。
   这里切换到cpu/arm920t目录下执行 :make start.o,将相应的源文件进行编译。
   $(OBJS)其实就是代表u-boot所需要的.o文件。

2.1.5 $(LIBS)

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

   $(LIBS): = LIBS是很多依赖库。
   $(obj)为空。
   这里切换到库文件的路径下执行make操作,将相应的源文件进行编译。
   $(LIBS)其实就是代表u-boot所需要的.a文件。

2.1.6 $(LDSCRIPT)

   顶层Makefile中没有$(LDSCRIPT)定义,其定义在config.mk中,具体定义如下:

ifndef LDSCRIPT
#LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds.debug
ifeq ($(CONFIG_NAND_U_BOOT),y)
LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot-nand.lds
else
LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds
endif

在Makefile中添加$(LDSCRIPT)打印:

test:
    @echo "LDSCRIPT = $(LDSCRIPT)"

其值输出如下:在这里插入图片描述

3、u-boot.bin(依赖参考2.1u-boot.srec)

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

   命令展开就是:

arm-linux-gnueabihf-objcopy -O binary u-boot u-boot.bin

  最终生成二进制格式的文件。

4、System.map

$(obj)System.map:   $(obj)u-boot                                                                                                       
        @$(NM) $< | \                                                                                                                                                                                                                                                         
        grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | \                                               
        sort > $(obj)System.map

   nm命令被用于显示u-boot的符号表,使用grep -v进行过滤,使用sort命令进行排序,最后输出到System.map文件中。System.map包含了U-Boot的全局变量和函数的地址信息。

5、u-boot.lds

  先贴出用到的连接脚本代码:

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
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 = .;
}

  找到两篇很不错的参考文章
参考链接1.
参考链接2.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr_zhangsq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值