tiny210——uboot移植之Makefile剖析篇

这篇东东早就写好了,一直没时间发出来,现在终于有时间发一下了记录总结uboot的学习历程,好像够详细了,以后忘了也可以再温习回来嘛有些特殊字符显示得乱掉了

Makefile追踪技巧:

技巧1:可以先从编译目标开始顺藤摸瓜地分析,先不要关注具体细节,着重关注主要的代码结构和编译过程

技巧2:追踪分析时要通过文本或者其他途径暂时记录重要的线索

技巧3:将主要的Makefile文件中export出来的变量以及include的文件提取出来,看看include的文件大致是些什么文件,当看到一些来历不明的变量或者操作时可以从这些记录中查找是否有相关记录(将Make涉及的配置文件找出来,相当于形成了一个闭合的环境)

为什么要去研究uboot的Makefile?因为要研究一个工程代码,最好的方法就是阅读其Makefile,Makefile会告诉我们工程的模块结构以及目标的具体编译和连接过程,读懂了Makefile无疑就是对工程结构和代码结构了如指掌,相当于一张地图,是移植以及学习其技术的基础。

在这里,是以smart210的uboot_smart210作为剖析对象,其原生uboot版本是u-boot-2011.06。uboot的所有Makefile有2000行左右的代码,其中的关系也比较复杂,要想读懂其Makefile,必须要对Make工具的的语法规则和shell脚本比较熟悉,这是阅读的基础,里面很多非常关键的地方用到了grep、sed和awk等文本处理工具,当然可以边看边学。此外,还要善于使用搜索,最好可以自己写一些简单的自动搜索脚本,因为Makefile里面有很多变量并不是在同一个文件里面定义或赋值,善用搜索,必定可以节约非常多的精力。

如何编译smart10的uboot?

编译smart210的uboot命令是:

make samrt210

这是相对于以前的老版本进行的改进,老版本通常是现用make xx_config进行配置,然后再用make进行编译,当然后来的版本也依然可以使用这样的方式进行编译。后来版本的Makefile结构有了较大的变动。

Make主要过程

在uboot的整个编译过程中,源码根目录下的主Makefile被make读取执行了两次,第一次是完成编译环境的配置以及相关编译参数的配置;第二次是开始真正的编译过程。

u-bootMakefile主要流程

第一次读入执行Makefile:

1.产生编译目标和相应规则。

从编译命令入手,但是我们在Makefile中根本就无法搜索到smart210的字眼,更不要说smart210这个目标。在代码根目录搜索会发现,smart210的字眼出现在boards.cfg。原来是uboot为了易于拓展对平台和各种开发板,将各种开发板的相关信息集中放在这个文件中,如下图:

smart210                        arm   armv7      smart210    samsung   s5pc1xx

那么这个文件是怎么被引入到Makefile中的呢?

在这里:

sinclude $(obj).boards.depend

$(obj).boards.depend:     boards.cfg

awk‘(NF && $$1 !~ /^#/) { print $$1 ": " $$1 "_config;$$(MAKE)" }‘ $< > [email protected]

要理解3句语句,必须要知道make的工作过程。

执行make smart210指令时,make会把所有引用的文件展开到Makefile中,如果发现有文件无法找到时便会查找使用相应规则来尝试创建相应的文件。

sinclude$(obj).boards.depend这句语句执行时,代码目录下是没有这个文件的,那么make便执行

$(obj).boards.depend:     boards.cfg

awk‘(NF && $$1 !~ /^#/) { print $$1 ": " $$1 "_config;$$(MAKE)" }‘ $< > [email protected]

规则来生成.boards.depend文件,.boards.depend是依赖boards.cfg来生成的,其中的内容类似为:

……….

smart210: smart210_config; $(MAKE)

smdkc100: smdkc100_config; $(MAKE)

………..

可见,各种开发板的编译目标和规则现都已产生,并且被引入到Makefile中,因此此时Makefile中便已存在各种开发板的编译目标和规则。

2.配置编译环境。

在(1)中生成了编译目标和相应的规则:

smart210: smart210_config; $(MAKE)

这句规则的依赖的规则是smart210_config,故先执行规则生成依赖。

smart210_config的生成规则:

%_config::        unconfig

@$(MKCONFIG)-A $(@:_config=)

其执行的命令就是:./mkconfig –A smart210,uboot的编译环境绝大部分就是在这个脚本中进行配置的,接下来看看这个脚本究竟主要做了些什么事情。

if [ \( $# -eq 2 \) -a \( "$1" ="-A" \) ] ; then

#Automatic mode

##从boards.cfg抽取出smart210那一行信息

line=`egrep-i "^[[:space:]]*${2}[[:space:]]" boards.cfg` || {

echo"make: *** No rule to make target \`$2_config‘.  Stop." >&2

exit1

}

set${line}         ##将line中的字符串分别赋值给位置参数$1,$2,$3.....

#add default board name if needed

[$# = 3 ] && set ${line} ${1}

fi

语句line=`egrep -i"^[[:space:]]*${2}[[:space:]]" boards.cfg`从boards.cfg抽取出smart210那一行信息,并保存到变量line中,set ${line}将line中的字符串分别赋值给位置参数$1,$2,$3.....,由boards.cfg中说保存的信息类型可知,这里相当于是将Target、ARCH、CPU、Board name 、Vendor、SoC等平台相关或者板级相关的将用于配置编译环境的必要信息提取了出来。

echo "ARCH   = ${arch}"  > config.mk

echo "CPU    = ${cpu}"   >> config.mk

echo "BOARD  = ${board}" >> config.mk

[ "${vendor}" ] && echo"VENDOR = ${vendor}" >> config.mk

[ "${soc}"    ] && echo "SOC    = ${soc}"    >> config.mk

上面的语句将提取的ARCH、CPU、BOARD、VENDOR、SOC保存到config.mk文件中,这个文件将被Makefile引用以获取平台相关或者板级相关的信息来进行编译。

……

else

cd./include

rm-f asm

ln-s ../arch/${arch}/include/asm asm

echo"ln -s ../arch/${arch}/include/asm asm"

fi

上面先删除以前建立的连接,然后ln -s../arch/${arch}/include/asm asm在include目录下创建一个平台相关的头文件夹连接。

rm -f asm/arch

……

else

ln-s ${LNPREFIX}arch-${soc} asm/arch

echo"ln -s ${LNPREFIX}arch-${soc} asm/arch"

fi

上面先include/asm/目录下的旧的连接, 然后ln -s${LNPREFIX}arch-${soc} asm/arch在include/asm/中创建一个片上系统(也就是同一平台下的一类芯片)相关的连接。

…….

cat << EOF>> config.h

#defineCONFIG_BOARDDIR board/$BOARDDIR

#include<config_cmd_defaults.h>

#include<config_defaults.h>

#include<configs/${CONFIG_NAME}.h>  #即#include<configs/smart210.h>

#include<asm/config.h>

EOF

最后,生成include/config.h,将配置相关的头文件集中在config.h中引用。

编译环境配置小结:

(1)      提取ARCH、CPU、BOARD、VENDOR、SOC等平台相关或者板级相关的信息保存到config.mk文件中备用。

(2)      在include目录下创建一个平台相关的头文件夹连接。

(3)      在include/asm/中创建一个片上系统相关的头文件连接。

(4)      将配置相关的头文件集中在config.h中引用。

3.生成Makefile编译时所依赖的配置文件及其依赖关系

           Makefile编译时依赖的配置文件是include/autoconf.mk;其头文件依赖关系存放在include/autoconf.mk.dep文件中,那么这两个文件是怎么被引入到Makefile中的呢?

sinclude$(obj)include/autoconf.mk.dep

sinclude$(obj)include/autoconf.mk

make展开Makefile中上面这两句语句时,没有发现相应文件,便使用规则生成它们。

工程中的文件的数量的是非常庞大的数字,而每个文件依赖的文件也是非常多,如要要将每个文件的依赖手工一一列出,那工作量是相当繁重的,也不利于代码的移植,因此uboot利用make工具和gcc编译器的特性自动生成以及维护文件依赖关系。Make支持多规则目标,但是只能在一个规则中定义命令,换句话来说就是说同一目标的依赖可以不止出现在一个规则中,而gcc的选项-M可生成源文件和头文件的make可用的依赖关系,自动生成依赖关系就是利用了这个make特性和gcc的特性。

autoconf.mk.dep的生成规则如下:

$(obj)include/autoconf.mk.dep:$(obj)include/config.h include/common.h

@$(XECHO)Generating [email protected] ; \

set-e ; \

:Generate the dependancies ; \

$(CC)-x c -DDO_DEPS_ONLY -M $(HOSTCFLAGS) $(CPPFLAGS) \

-MQ$(obj)include/autoconf.mk include/common.h > [email protected]

gcc的选项-M可用于生成文件的make可用的依赖关系,-MQ用于指定目标名,-x指定语言类型。生成的autoconf.mk.dep的内容如下:

include/autoconf.mk:include/common.h /home/zqb/uboot/uboot_smart210/include/config.h\

……..

显然,这就是autoconf.mk的依赖关系,autoconf.mk.dep中的规则只有依赖关系而没有命令。

Uboot的配置主要是通过修改include/configs/中的头文件中宏来实现,有很多的编译选项也是在其中修改,那么问题就来了,make工具可不认识头文件中的宏语法,所以Makefile中通过一些规则调用sed文本处理工具来生成我们想要的配置格式。

$(obj)include/autoconf.mk:$(obj)include/config.h

@$(XECHO) Generating [email protected] ; \

set -e ; \

: Extract the config macros ; \

$(CPP) $(CFLAGS) -DDO_DEPS_ONLY -dMinclude/common.h | \

sed -n -ftools/scripts/define2mk.sed > [email protected] && \

mv [email protected] [email protected]

上面的规则是利用sed工具来进行的,sed的工作方式由tools/scripts/define2mk.sed来指定,头文件中的配置方式是宏,如#define CONFIG_xx xxx,而成的make可用的配置的autoconf.mk文件格式如下:

……..

CONFIG_CMD_ITEST=y

CONFIG_CMD_BDI=y

CONFIG_SYS_MAX_NAND_DEVICE=y

……….

其实就是将头文件中的宏定义转化为了make的变量作为控制开关。例如:

Ifeq($(CONFIG_CMD_ITEST),y)

#do_something

Endif

用上述的方式来生成Makefile编译时所依赖的配置文件及其依赖关系,便可实现自动生成相关依赖关系以及因相关的配置文件的改动而重新编译工程,不再需要人工去生成及维护其依赖关系。

第二次读入执行Makefile:

规则smart210: smart210_config;$(MAKE)完成了依赖smart210_config更新后再次调用make,开始了第二次读入执行Makefile。

1.展开所有的配置文件的配置

主要展开的是:

sinclude $(obj)include/autoconf.mk.dep

sinclude $(obj)include/autoconf.mk         #主要的编译配置都在这个文件中,故其非常重要

…….

include $(obj)include/config.mk                #平台相关或板级相关的信息

……

include $(TOPDIR)/config.mk

源码根目录下的config.mk中语句主要完成了根据配置项进行的编译选项的配置,如使用什么编译器、连接器,这些编译器、连接器的编译选项是什么:

AS     = $(CROSS_COMPILE)as  #汇编器

LD    = $(CROSS_COMPILE)ld           #连接器

CC    = $(CROSS_COMPILE)gcc        #编译器

CPP  = $(CC) -E                  #预处理

AR    = $(CROSS_COMPILE)ar          #归档器

………

此外,config.mk还包含了一些通用的源文件编译规则,如下:

$(obj)%.s:         %.S

$(CPP) $(ALL_AFLAGS) -o [email protected] $<

$(obj)%.o:         %.S

$(CC) $(ALL_AFLAGS) -o [email protected] $< -c

$(obj)%.o:         %.c

$(CC) $(ALL_CFLAGS) -o [email protected] $< -c

$(obj)%.i: %.c

$(CPP) $(ALL_CFLAGS) -o [email protected] $< -c

$(obj)%.s:         %.c

$(CC)  $(ALL_CFLAGS) -o [email protected] $< -c –S

2.生成最终目标

第二次读入并执行Makefile并没有指定终极目标,故其终极目标默认为第一个目标,也即是:

all:

sinclude $(obj)include/autoconf.mk.dep

……..

all:             $(ALL-y)

你会发现,第一个目标all在Makefile中出现了两次,这是为什么呢?第一个目标规则只有一个目标名,而没有依赖和命令,其出现是为了防止autoconf.mk.dep中的目标成为了终极目标,而后面出现的all:  $(ALL-y)才是真正的终极目标,这里也利用了make支持多规则目标的特性。all:  $(ALL-y)展开后:

all:    u-boot.srec u-boot.binSystem.map spl/u-boot-spl.bin

那么便可知道,最终生成的目标有u-boot.srec u-boot.bin System.map spl/u-boot-spl.bin。

先看看其中两个目标的规则:

…….

$(obj)u-boot.srec:    $(obj)u-boot

$(OBJCOPY) -O srec $< [email protected]

$(obj)u-boot.bin:      $(obj)u-boot

$(OBJCOPY) ${OBJCFLAGS} -Obinary $< [email protected]

$(BOARD_SIZE_CHECK)

……

你会发现最终的那几个目标都依赖u-boot目标,u-boot的规则如下:

$(obj)u-boot:    depend \

$(SUBDIRS) $(OBJS)$(LIBBOARD) $(LIBS) $(LDSCRIPT) $(obj)u-boot.lds

$(GEN_UBOOT)

在这之后里,逐个调用依赖的生成规则继续进行编译,如depend、$(OBJS) $(LIBBOARD)等的生成。源文件的编译看后面的“3.各个子目录下需要编译生成的目标文件被分门别类地编译”。

各源文件编译完成后,连接生成目标u-boot:

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)$(LDFLAGS_$(@F)) $$UNDEF_SYM $(__OBJS) \

--start-group $(__LIBS) --end-group$(PLATFORM_LIBS) \

-Map u-boot.map -o u-boot

elf格式的u-boot被连接生成后,u-boot.srec u-boot.bin System.map spl/u-boot-spl.bin就接着被生成了,其过程并不复杂。

3.各个子目录下需要编译生成的目标文件被分门别类地编译

uboot的开始执行的代码目标:

OBJS  =$(CPUDIR)/start.o

uboot板级密切相关的代码编译打包为:

LIBBOARD = board/$(BOARDDIR)/lib$(BOARD).o

其他的同一处理器通用的源文件打包为:

LIBS  = lib/libgeneric.o

LIBS +=lib/lzma/liblzma.o

LIBS += lib/lzo/liblzo.o

…….

接下来看看如何进行编译:

$(OBJS):   depend

$(MAKE) -C $(CPUDIR) $(if$(REMOTE_BUILD),[email protected],$(notdir [email protected]))

$(LIBS):    depend $(SUBDIRS)

$(MAKE) -C $(dir $(subst$(obj),,[email protected]))

$(LIBBOARD):  depend $(LIBS)

$(MAKE)-C $(dir $(subst $(obj),,[email protected]))

make的dir函数可将文件的路径提取出来,$(MAKE) -C $(dir $(subst $(obj),,[email protected]))的意思是到目标所在目录下去读取该目录下的Makefile进行编译,这样所有的目标和库都可以生成了。

4.子目录下的Makefile

make会像前面所说的那样调用子目录中的Makefile进行编译。在这里drivers/bios_emulator/目录下的Makefile进行剖析,其他目录的Makefile都是以类似的方式工作。

include$(TOPDIR)/config.mk

makefile通过引用源码顶层目录下的config.mk来配置编译选项,并引入了一些通用的源文件编译规则,如:

AS     =$(CROSS_COMPILE)as  #汇编器

LD    =$(CROSS_COMPILE)ld           #连接器

CC    =$(CROSS_COMPILE)gcc        #编译器

………

$(obj)%.o:         %.S

$(CC)  $(ALL_AFLAGS) -o [email protected] $< -c

$(obj)%.o:         %.c

Makefile中通过include$(SRCTREE)/rules.mk引入了源码顶层目录下的rules.mk文件,该文件中的规则完成了头文件依赖关系的自动生成,这也是如前面提到的一样,利用了make和gcc的特性。此外,rules.mk文件中还包含了编译宿主机下的目标的规则,那些在宿主机中使用的工具会用到这些规则来编译。

Makefile的all:         $(LIB)便是最终目标,根据依赖关系,便能完成子目录的编译。

5.生成S5PV210可启动的smart210-uboot.bin程序

前面生成的u-boot.bin还不能直接在S5PV210了上运行,因为其没有加上S5PV210所要求的特定的校验格式。S5PV210在启动的时候会自动从所选择的启动方式对应的设备中读取其前16kb的内容到内存中,S5PV210要求前面的16字节存放校验和之类的信息,具体内容请参阅S5PV210的datasheet。

在顶层主Makefile的最终目标中有一个目标spl/u-boot-spl.bin,spl也就是第二阶段程序加载程序(Secondary Program Loader),S5PV210内置的ROM中固化的代码是第一阶段加载程序,加载前24Kb(也就是第二阶段程序加载城程序u-boot-spl.bin)到内存中,并跳转到其中执行;而第二阶段加载程序则将完整的u-boot.bin加载到内存中,然后跳转到u-boot.bin中运行,这个时候,uboot就算是启动起来了。

在顶层主Makefile中,目标spl/u-boot-spl.bin的规则是:

$(obj)spl/u-boot-spl.bin:           depend

$(MAKE)-C spl all

可见,make会加载spl目录下的Makefile进行编译,接下来便看看这个子Makefile的工作方式。

由于硬件的限制,u-boot-spl.bin必须不超过(24*1024-16)字节,所以编译时只需要一些加载完整uboot所需要的模块,其他统统不编译进来。其他的方面,与Makefile的做法几乎一样,这里不再赘述,而值得一的是smart210-uboot.bin的最终生成。

在u-boot-spl.bin生成后,如下的规则便运行了:

ifdef CONFIG_SAMSUNG

$(obj)$(BOARD)-spl.bin:$(obj)u-boot-spl.bin

$(TOPDIR)/board/$(BOARDDIR)/tools/mk$(BOARD)spl.exe\

$(obj)u-boot-spl.bin$(obj)$(BOARD)-spl.bin

cat$(obj)$(BOARD)-spl.bin $(TOPDIR)/u-boot.bin > $(TOPDIR)/$(BOARD)-uboot.bin

endif

mk$(BOARD)spl.exe是在board/Samsung/smart210/tools/中已经被编译好了的工具,就是用来给第二阶段程序加载程序添加S5PV210所要求的头部信息的,添加了头部信息的u-boot-spl.bin就可以被加载并执行了,加了头部信息的u-boot-spl.bin命名为smart210-spl.bin。cat $(obj)$(BOARD)-spl.bin $(TOPDIR)/u-boot.bin >$(TOPDIR)/$(BOARD)-uboot.bin语句将第二阶段程序加载程序smart210-spl.bin和完整的u-boot.bin打包成为一个整体,生成的可执行文件为smart210-uboot.bin,那么smart210-uboot.bin烧进去以后便可以完成一个完整uboot的启动。

时间: 2024-12-29 06:51:31

tiny210——uboot移植之Makefile剖析篇的相关文章

uboot移植之makefile分析

/* Name:uboot之makefile分析 Data:2015-3-3 Author:suj_un */ Uboot之makefile分析 编译uboot,内核或者其他软件只需要执行make命令就可以生成可执行文件.执行命令后是怎么工作的?要知道这个就要看makefile了.现在就来揭开这玩意神秘的面纱.其中会粘一些代码段来分析... VERSION = 2010 PATCHLEVEL = 03 SUBLEVEL = EXTRAVERSION = ifneq "$(SUBLEVEL)&qu

uboot移植总结

1.uboot的介绍及体系结构 1.1 uboot的介绍 Uboot是德国DENX小组的开发用于多种嵌入式CPU的bootloader程序, UBoot不仅仅支持嵌入式Linux系统的引导,当前,它还支持NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS嵌入式操作系统.UBoot除了支持PowerPC系列的处理器外,还能支持MIPS. x86.ARM.NIOS.XScale等诸多常用系列的处理器. 1.2 uboot的体系结构 目录树 |--board |--c

U-Boot移植之前期分析(上)

老是看别人移植uboot,用别人移植好的uboot,今天终于下定决心自己移植一个uboot来玩玩,好歹我也是个软件开发人员啊. 第一步:去ftp://ftp.denx.de/pub/u-boot/网站下载个uboot工程源码,为了防止环境出问题,我决定用个老一点的,于是就下了:u-boot-1.1.6.tar.bz2. 第二步:解压源码:tar  jxvf  u-boot-1.1.6.tar.bz2. 第三步:建立source insight工程 好了完成以上三步之后,我们需要的前提条件都准备好

I.MX6Q(TQIMX6Q/TQE9)学习笔记——新版BSP之u-boot移植

前段时间就开始学习I.MX6Q了,但是最近工作实在是忙,间断了一些时间了.为了提高移植效率,还是考虑移植Freescale维护的3.10版本的内核. 源码获取 Freescale维护的3.10的内核是使用git管理的,但是直接使用git下载代码会比较慢,下面是我下载好的uboot和kernel: I.MX6Q BSP源码(Freescale官方维护) 代码下载好后,先将u-boot解压到工作目录,然后在终端下切换到uboot根目录.由于这个版本的bsp是使用git管理的,因此,需要切换到指定分支

uboot移植——uboot源码目录分析

uboot移植(一)--uboot源码目录分析 本文分析的uboot是九鼎官方提供的,是对应s5pv210开发板x210bv3的uboot 一:uboot的概念及移植的原理. uboot就是在内核运行前的一段小程序,用来初始化硬件设备,建立内存空间映射图.从而将系统的软硬件带到合适的状态,主要功能就是为了启动内核,它将内核从flash中拷贝到ddr中,然后跳转到内核入口中,交由内核控制权,uboot严重依赖硬件,因此一个通用的uboot不太可能. 移植原理:uboot中有很多平行代码,各自属于各

uboot移植(二)——uboot mkconfig脚本分析

uboot移植(二)--uboot  mkconfig 脚本分析 一:mkconfig脚本的作用 mkconfig是通过传入的参数来脚本用于某个开发板配置uboot,主要是通过判断其输入的参数来创建符号链接文件,使它们指向该开发板对应的配置文件来进行配置. (1)配置CPU架构相关的文件:在include目录下创建asm文件,指向include/asm-arm (2)配置SOC类型相关的文件:include目录下创建regs.h文件,指向include/s5pc110.h include/asm

U-Boot移植_DDR3移植

疯雨-版权所有,转载请注明[http://blog.csdn.net/u010346967] U-Boot移植_DDR3移植:首先在这里感谢网友fengtian的整理,没有你就没有这篇文章 在系统上电后,CPU并不知道外部的RAM是什么类型的存储器,因此U-Boot需要对CPU进行RAM初始化设置,然后将程序拷贝到RAM中运行. 本系统采用的RAM是DDR3类型存储芯片,容量是4GB,频率是1066KHz:系统从eMMC中启动.采用的是U-Boot-2009版,linux3.0.35系统内核,U

嵌入式linux开发uboot移植(七)——三星官方uboot的移植

嵌入式linux开发uboot移植(七)--三星官方uboot的移植 友善之臂Smart210开发板是基于三星SMDKV210评估板裁剪.调整而来的.因此三星官方发布的基于SMDKV210评估板的uboot是移植uboot到Smart210开发板的最合适uboot版本.本文将SMDKV210的uboot移植到Smart210开发板.Smart210开发板的配置如下: SoC:Samsung S5PV210 SDRAM:512MB DDR2 RAM FLASH存储:2G MLC NAND Flas

mini6410移植--uboot移植(1)

u-boot移植 (1)移植环境 u-boot版本:u-boot-2011-03Linux平台:XP下虚拟机Ubuntu12.04交叉编译工具:arm-linux-gcc-4.5.1arm开发板:mini6410        CPU:S3C6410        DDR:256M        Nand Flash:256M        网卡:DM9000EP (2)移植目标 支持Nand启动支持Nand读写支持yaffs写入支持tftp下载 下载UBoot把它解压,然后得到u-boot-2