U-boot移植之编译控制
简述:在u-boot的readme文档中提到,u-boot的编译分为两步,即编译前的配置makeconfig和编译过程make。那么在这两步中都进行了怎样的一些操作呢!
一、公共代码
打开(u-boot)根目录下的Makefile,在第一个目标all之前的都是公共代码。这些代码都是设置变量、导出变量及包含文件的语句,只要在根目录执行make命令,不管有没有参数它们都会被执行。
-
变量BUILD_DIR
这个变量决定了编译过程产生的中间文件及输出文件存放的位置。如果不设置这个变量,那么这些中间文件及最终的生成文件将被放到源代码目录下的各目录中。
-
变量MKCONFIG
这个变量的值为根目录下的mkconfig,这是一个shell脚本文件。它通常在编译配置阶段被调用,其功能是根据输入变量或根目录下的board.cfg文件来产生板配置文件/include/config.h。这个config.h中包含着开发板的平台、芯片、开发板名、提供商等一些宏定义,它还包含了一些头文件。它对编译过程有着重要的影响,因为很多代码并不是为某单一平台写的。另外,这个config.h文件包含有smdk6400.h文件,这个文件是开发板的配置文件,里面放着几乎所有对于该开发板的u-boot的控制宏及一些重要板相关的变量的值。比如,内存起始地址,内存大小等。
-
包含文件include/config.mk
这个config.mk是在编译配置过程产生的,所以在编译配置阶段是没有这个文件的。这里面存放的是开发板相关Makefile变量,它将决定哪些板相关的文件会被编译。
二、编译配置
以官方源码自带的smdk6400为例(makesmdk6400_config)。执行完上面的公共代码,直接跳到目标smdk6400_config目标处并执行其也的命令。
-
@echo"#define CONFIG_NAND_U_BOOT" > $(obj)include/config.h
很多代码中都有用到这个宏,有没有定义这个宏会直接影响一部分代码是否需要被编译。由此也可知这个配置产生的u-boot是用于nandflash起动的。
-
@echo"CONFIG_NAND_U_BOOT = y" >> $(obj)include/config.mk
这个是Makefile的变量,和前面的宏定义不同,这个变量是作用于Makefile中,利用它可以控制一部分库或源代码是否需要被编译。
-
echo"RAM_TEXT = 0x57e00000" >>$(obj)board/samsung/smdk6400/config.tmp;
这里也是一个Makefile变量,这个变量的值在后面将会变成一个宏和另一个Makefile变量的值,这个值并非随意取的,它和u-boot在DRAM中的布局有关。具体如何关联,后面再说。
-
@$(MKCONFIG)smdk6400 arm arm1176 smdk6400 samsung s3c64xx
调用前面提到的mkconfigshell脚本处理开发板的信息,写入/include/config.h及/include/config.mk这两个文件。另外,它还会做一些工作,建议设置变量BUILD_DIR为一个新目录,然后运行makesmdk6400,再打开这个新目录,它所做的所有工作都就在你的眼前了。
这里有必要说明一下,这种配置是老版u-boot的配置方法。新版的配置统一到%_config这个目标下,该目标只有一条语句“@$(MKCONFIG)-A$(@:_config=)”,mkconfig借助根目录下的boards.cfg文件来创建/include/config.h及/include/config.mk这两个文件。所以,其他的额外信息就只能换个地方了。
三、编译过程
通常一个make就够了,因为makefile会把遇到的第一个目标作为最终目标。在根目录的makefile中的第一个目标是all,它没有任何依赖。原来,在这个目标all的下面还有个目标all是有依赖的,第二个all的会覆盖第一个all作为最终目标。这样做的目的在于,makefile中包含了很多其他的文件,如果某一文件中有一个目标,那么make就会把它作为最终目标,这不是我们想要的。所以第一个目标all存在的目的就是为了让make始终认为all为其最终目标。当然第一个目标all下的命令也会被执行。
-
包含文件include$(obj)include/config.mk
这个文件由mkconfig脚本生成,其注释已经很直白了
#load ARCH, BOARD, and CPU configuration
-
包含文件include$(TOPDIR)/config.mk
这个文件是根目录下的config.mk,这是u-boot的全局的makefile配置文件,源代码目标树中的每个makefile都有包含这个文件,可想而知其重要性。对于它的分析在后面再说。
-
第二个目标all: $(ALL-y)$(SUBDIR_EXAMPLES)
变量ALL-y的定义就是这个目标的前面,大家可以打开makefile开看看。根据smdk6410的配置其值应该为,$(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(obj)u-boot-nand.bin
变量SUBDIR_EXAMPLES的定义为 examples/standaloneexamples/api
前面说了,配置时配置的是nandflash起动的u-boot,所以我们重点关注u-boot-nand.bin目标
-
目标$(obj)u-boot-nand.bin: nand_spl $(obj)u-boot.bin
其下只有一条命令“cat$(obj)nand_spl/u-boot-spl-16k.bin $(obj)u-boot.bin >$(obj)u-boot-nand.bin”这条命令的作用是把u-boot.bin附加到u-boot-spl-16k.bin的尾部。这样做是和smdk6400的起动方式有关的,这里讨论编译过程就暂不解释了。
它有两个依赖nand_spl $(obj)u-boot.bin,这也是两个目标。一起去看看。
-
目标$(obj)u-boot.bin: $(obj)u-boot
这里实际上只是调用objdump将elf格式的u-boot压缩成bin模式的。它依赖于$(obj)u-boot
-
目标nand_spl: $(TIMESTAMP_FILE)$(VERSION_FILE) depend
命令$(MAKE)-C nand_spl/board/$(BOARDDIR) all
这条命令的意思是进入nand_spl/board/samsung/smdk6400目录并执行makeall。进入这个目录后,你会发现,这里仅有三个文件config.mkmakefileu-boot.lds,并没有任何源代码。事实上这个makefile将会生成u-boot的step_1的代码,它会将完整的u-boot拷贝到dram运行。这个的生成过程放在后面说。主要依赖于depend目标,另外两个依赖并不是十分的重要。
-
目标$(obj)u-boot: depend\
$(SUBDIR_TOOLS)$(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT) $(obj)u-boot.lds
$(GEN_UBOOT)
乍一看这个目标没有命令,有的只是一堆依赖。事实上,depend目标是将源代码树下的所有源文件编译成目标文件(.o),第二排的依赖是解决生成u-boot需要的库,链接脚本等,这些库也是靠depend目标生所的目标文件来生成的。第三排的GEN_UBOOT变量展开就是生成elf格式u-boot的命令。这里并没有用到编译器,只使用了objdump提取符号,链接器组合目标文件生成一个完整的u-boot。
-
目标dependdep: $(TIMESTAMP_FILE) $(VERSION_FILE) \
$(obj)include/autoconf.mk\
$(obj)include/generated/generic-asm-offsets.h\
$(obj)include/generated/asm-offsets.h
fordir in $(SUBDIRS) $(CPUDIR) $(LDSCRIPT_MAKEFILE_DIR) ; do \
$(MAKE)-C $$dir _depend ; done
这个目标是怎么工作的我也不解,但其作用却知道,就是遍历目录去执行make命令。通过这个目标,源代码树下所有组成该开发板u-boot需要的源文件都会被编译成目标文件(.o)。
四、全局编译配置文件
根目标下的config.mk
-
前面大部分内部都在处理编译器(gcc、as)的选项,并用变量代替具体的编译器命令,这样方便交叉编译与本机编译的切换。
-
接下来是加载各处的.mk配置文件,首先加载的便是编译配置过程中产生的配置文件。
-
sinclude$(OBJTREE)/include/autoconf.mk//几乎所有的 CONFIG_*宏,是从几个头文件中收集来由mkconfig脚本产生的
-
sinclude$(OBJTREE)/include/config.mk //存放了板相关的make变量,也是由mkconfig产生
-
sinclude$(TOPDIR)/arch/$(ARCH)/config.mk # 平台(arm)相关编译配置文件
通常在这里指定交叉编译器前缀,如定义变量“CROSS_COMPILE?= arm-none-eabi-”
另外特别提一下,LDFLAGS_u-boot+= -pie这里设置的链接选项-pie让我纠结了一天,这个选项的具体作用我并不了解,因为我的u-boot在RAM中并没有重定位,导至部分链接脚本中的符号没有值,从而让程序产生异常不能执行任何命令,在去除这个选项后正常。
-
sinclude$(TOPDIR)/$(CPUDIR)/config.mk # CPU(arm1176)相关配置文件
-
sinclude$(TOPDIR)/$(CPUDIR)/$(SOC)/config.mk # 具体芯片(s3c6400)相关配置文件
-
sinclude$(TOPDIR)/board/$(BOARDDIR)/config.mk # 开发板相关配置文件
这里用到了前面提到的RAM_TEXT变量,并根据变量决定重要变量CONFIG_SYS_TEXT_BASE的值,这和s3c6400芯片的起动流程相关的。
sinclude$(OBJTREE)/board/$(BOARDDIR)/config.tmp
ifndefCONFIG_NAND_SPL
CONFIG_SYS_TEXT_BASE= $(RAM_TEXT)
else
CONFIG_SYS_TEXT_BASE= 0
endif
-
配置全局宏定义
这编译选项-DXX=YY与在头文件中的#defineXX YY 是没有区别的。这里具两个重要的。
ifneq($(CONFIG_SYS_TEXT_BASE),)
CPPFLAGS+= -DCONFIG_SYS_TEXT_BASE=$(CONFIG_SYS_TEXT_BASE)
endif
ifeq($(CONFIG_SPL_BUILD),y)
CPPFLAGS+= -DCONFIG_SPL_BUILD
endif
-
配置链接标志,包括链接脚本,代码段重定位地址等
LDFLAGS_u-boot+= -T $(obj)u-boot.lds $(LDFLAGS_FINAL)
ifneq($(CONFIG_SYS_TEXT_BASE),)
LDFLAGS_u-boot+= -Ttext $(CONFIG_SYS_TEXT_BASE)
endif
-
设置make模式规则
$(obj)%.s: %.S
$(CPP)$(ALL_AFLAGS) -o $@ $<
$(obj)%.o: %.S
$(CC) $(ALL_AFLAGS) -o $@ $< -c
$(obj)%.o: %.c
$(CC) $(ALL_CFLAGS) -o $@ $< -c //对所有的.c文件执行此命令生成.o文件
$(obj)%.i: %.c
$(CPP)$(ALL_CFLAGS) -o $@ $< -c
$(obj)%.s: %.c
$(CC) $(ALL_CFLAGS) -o $@ $< -c -S
五、生成u-bootstep_1的makefile
-
make变量CONFIG_NAND_SPL =y
这个变量用于区别当前编译生成的是setp_1的代码还是完整u-boot。
-
包含配置文件
include$(TOPDIR)/config.mk
include$(TOPDIR)/nand_spl/board/$(BOARDDIR)/config.mk //主要
include$(TOPDIR)/board/$(BOARDDIR)/config.mk //由上一条config.mk引入
根据前面的分析可知,最后一条config.mk的引入目的是将CONFIG_SYS_TEXT_BASE设置为0。
-
给as及gcc添加编译选项-DCONFIG_NAND_SPL
AFLAGS +=-DCONFIG_NAND_SPL
CFLAGS +=-DCONFIG_NAND_SPL -ffunction-sections
这样在编译所有代码时就相当于有#defineCONFIG_NAND_SPL这个宏
-
指定需要的文件
SOBJS =start.o cpu_init.o lowlevel_init.o
COBJS =nand_boot.o nand_ecc.o s3c64xx.o smdk6400_nand_spl.o nand_base.o
__OBJS :=$(SOBJS) $(COBJS)
生成u-boot step_1代码的目标
$(nandobj)u-boot-spl: $(OBJS)$(nandobj)u-boot.lds
cd$(LNDIR) && $(LD) $(LDFLAGS) $(__OBJS) \
-Map$(nandobj)u-boot-spl.map \
-o$(nandobj)u-boot-spl
-
创建源代码文件的符号链接
上面指定需要的文件时只给出了文件名,那么源代码呢?原来,生成这些目标文件的源代码都使用ln命令在当前文件夹下创建了一个对应的符号链接并利用下面make的模式规则编译成了相应的.o文件。
-
make模式规则
$(obj)%.o: $(obj)%.S
$(CC)$(AFLAGS) -c -o $@ $<
$(obj)%.o: $(obj)%.c
$(CC)$(CFLAGS) -c -o $@ $<
六、总结
本文详细介绍了U-boot的编译控制过程,包括公共代码、编译配置和编译过程。在编译配置阶段,`makeconfig`用于生成配置文件,影响编译过程。在编译过程中,`all`目标确保所有相关文件正确编译,生成最终的u-boot镜像。文章还探讨了`config.mk`等全局配置文件的作用,以及`CONFIG_NAND_U_BOOT`等宏如何影响代码编译。
6158

被折叠的 条评论
为什么被折叠?



