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

    技术2024-05-30  114

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

    1、执行make all2、u-boot.srec(依赖在下级标题)2.1 u-boot(依赖在下级标题)2.1.1 depend2.1.2 version2.1.3 $(SUBDIRS)2.1.4 $(OBJS)2.1.5 $(LIBS)2.1.6 $(LDSCRIPT) 3、u-boot.bin(依赖参考2.1u-boot.srec)4、System.map5、u-boot.lds

    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.

    Processed: 0.019, SQL: 9