第一次写技术博客,还有些兴奋呢。我是CrazyCatJack,大家可以叫我CCJ或者疯猫。我即将成为一名嵌入式Linux的驱动工程师,现在还是一枚大四狗,呼呼~大学期间做了一些项目和比赛,都是基于32位的MCU(例如STM32、Freescale K60),这些呢都是根据网上的视频,PDF自学的。现在想更进一步,学习一下嵌入式Linux、UCOS-II等嵌入式系统。因为给板子加系统是一个必然趋势,控制会越来越复杂,内容也越来越多的。有一个系统统筹管理是非常棒的选择。好了,废话少说,今天开始我的第一篇技术博客:u-boot的配置、编译和链接^_^

  看到有的小伙伴好像不太了解u-boot,因此我简单介绍一下,u-boot是一种bootloader。在嵌入式开发过程中,bootloader用于配置平台/开发板,最主要的功能就是从flash中读出内核,然后启动内核。我们平时在PC上使用windows系统也是类似的。PC上是BIOS对硬件配置,从硬盘读出windows内核,然后启动windows内核,进入windows系统。相对的,嵌入式平台是bootloader对硬件配置,读出并启动嵌入式系统内核(例如Linux内核),进入你所用的嵌入式系统。

1.u-boot的配置

  首先,我们要想了解u-boot,最好是从Makefile开始看,就能知道u-boot要执行的操作了。就像C语言中的main函数一样。在Makefile文件里,和配置相关的语句如下:

OBJTREE        := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))
SRCTREE := $(CURDIR)
TOPDIR := $(SRCTREE)
LNDIR := $(OBJTREE)
export TOPDIR SRCTREE OBJTREE MKCONFIG := $(SRCTREE)/mkconfig
export MKCONFIG CCJ_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t CCJ NULL s3c24x0

  根据Makefile中ReadMe文件中的描述,我们要配置u-boot,就要执行"make <board name>_config"这条指令。所以从Makefile中要查找一下你所用的开发板型号的相关配置信息。一般厂家都会给你配置好u-boot,你也可以自己写。在这里就假设我们使用的开发板名字为“CCJ”。分析最后这条语句:

@$(MKCONFIG) $(@:_config=) arm arm920t CCJ NULL s3c24x0

  第一个MKCONFIG在Makefile文件中已有定义,可以看到MKCONFIG:=$(SRCTREE)/mkconfig,也就是说我们要用源代码树下的mkconfig替换这条语句中的MKCONFIG。

  第二个$(@的含义是用前面板子的名字替换掉后面的内容,而且不要“_config”,也就是说替换之后为CCJ。

  现在经过替换,这条语句变成了:

./mkconfig CCJ arm arm920t CCJ NULL s3c24x0

  后面的5个参数其实分别代表着你现在用到的硬件平台的架构、CPU、开发板型号名称、供应商、SOC名称。这个我们后面还会有讲解。所以能够看出这里我用到的是ARM架构,CPU为ARM920T,开发板名称为CCJ,SOC为S3C24X0。

  那现在我们已经分析了这条配置语句所代表的含义,但它具体是怎么工作的呢?是怎样将我们写好的这些硬件相关的信息进行具体配置赋值的呢?其实这里我们调用了根目录下的./mkconfig这一文件,就像C语言中的函数调用一样,调用mkconfig这个文件,并传入参数(arch,CPU,boardname,vendor,soc)为(arm arm920t CCJ NULL s3c24x0),来执行相应的操作。这么说大家应该就理解了吧。所以接下来我们看mkconfig文件中是怎样使用这些参数进行配置的。

  现在我们打开mkconfig文件,先分析第一段代码:

APPEND=no    # Default: Create new config file
BOARD_NAME="" # Name to print in make output while [ $# -gt ] ; do
case "$1" in
--) shift ; break ;;
-a) shift ; APPEND=yes ;;
-n) shift ; BOARD_NAME="${1%%_config}" ; shift ;;
*) break ;;
esac
done [ "${BOARD_NAME}" ] || BOARD_NAME="$1" [ $# -lt ] && exit
[ $# -gt ] && exit echo "Configuring for ${BOARD_NAME} board..."

  首先我们看到两个参数的赋值,APPEND为“no”,这个参数我们后面会用到,BOARD_NAME为“”,非空。这里大家会看到很多“$1,$2,$3...”等等,大家不要晕,其实很简单的,他们都分别对应我们刚刚所说传入的参数:

Makefile命令:             ./mkconfig CCJ arm arm920t CCJ NULL s3c24x0
mkconfig对应符号表示: $ $ $ $ $ $ $

  所以这里用到的$1其实就是"CCJ",$2就是"arm",以此类推。进入while语句,由于这句Makefile命令中没有“--”,“-a”,“-n”。所以这个while其实什么都没干。如果大家在写Makefile命令的时候,用到了“--”,“-a”,“-n”,则这里会改变APPEND和BOARD_NAME两个参数的值。我们这里没有改变。

  接下来看下一句话 [ "${BOARD_NAME}" ] || BOARD_NAME="$1" 这句话是说如果BOARD_NAME不为空,则BOARD_NAME等于$1,即“CCJ”。然后是 echo "Configuring for ${BOARD_NAME} board..." 这句话的含义就是打印“Configuring for CCJ board”,所以在我们执行“make CCJ_config”命令时,一定会打印出这句话来。其中CCJ是你开发板的型号。

  

if [ "$SRCTREE" != "$OBJTREE" ] ; then
mkdir -p ${OBJTREE}/include
mkdir -p ${OBJTREE}/include2
cd ${OBJTREE}/include2
rm -f asm
ln -s ${SRCTREE}/include/asm-$ asm
LNPREFIX="../../include2/asm/"
cd ../include
rm -rf asm-$
rm -f asm
mkdir asm-$
ln -s asm-$ asm
else
cd ./include
rm -f asm
ln -s asm-$ asm
fi rm -f asm-$/arch

  接着往下看,上面这段代码主要完成链接工作。首先if判断 $SRCTREE 是否等于 $OBJTREE,这里要注意了,这两个参数是我们在Makefile文件中定义的。看我发的第一个代码片中就有对它们的赋值。

OBJTREE        := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))
SRCTREE := $(CURDIR)

  第一条语句的意思是如果我们定义了BUILD_DIR,则OBJTREE等于 $BUILD_DIR,否则等于 $CURDIR。那么这里我没有定义BUILD_DIR,所以OBJTREE= $CURDIR。所以现在OBJTREE=SRCTREE,则不执行if语句中的内容,而转去执行else分支的内容。

  首先进入./include目录下,删除原来生成的asm文件,重新建立asm文件,并链接到asm-$2文件。经过前面的讲解,相信你已经知道asm-$2经过转换,得到asm-arm。也就是说现在asm->asm-arm。最后删除asm-arm/arch文件。同理,为后面重新建立连接做准备。

if [ -z "$6" -o "$6" = "NULL" ] ; then
ln -s ${LNPREFIX}arch-$ asm-$/arch
else
ln -s ${LNPREFIX}arch-$ asm-$/arch
fi if [ "$2" = "arm" ] ; then
rm -f asm-$/proc
ln -s ${LNPREFIX}proc-armv asm-$/proc
fi

  接着往下分析,如果$6不为空,或$6等于NULL,则执行if内的语句,我们$6=s3c24x0。所以执行分支语句,建立文件asm-am/arch,并指向arch-sac24x0。

  第二个if中,因为$2等于arm,所以执行语句,删除asm-arm/proc,建立文件asm-arm/proc,并指向proc-armv。

#
# Create include file for Make
#
echo "ARCH = $2" > config.mk
echo "CPU = $3" >> config.mk
echo "BOARD = $4" >> config.mk [ "$5" ] && [ "$5" != "NULL" ] && echo "VENDOR = $5" >> config.mk [ "$6" ] && [ "$6" != "NULL" ] && echo "SOC = $6" >> config.mk #
# Create board specific header file
#
if [ "$APPEND" = "yes" ] # Append to existing config file
then
echo >> config.h
else
> config.h # Create new config file
fi
echo "/* Automatically generated - do not edit */" >>config.h
echo "#include <configs/$1.h>" >>config.h exit

  现在是不是有点累了?我们开始分析mkconfig的最后一大段^_^。坚持就是胜利!!!

  根据注释,第一段代码的作用是创建包含的文件,为配置做准备。这里我们主要是创建config.mk文件。这里说明一下">"表示创建某文件,">>"表示将内容添加到某文件。所以接下来几行的含义就分别是:创建config.mk文件,并添加"ARCH  = arm",添加"CPU  = arm920t",添加"BOARD  = CCJ"语句。$5为NULL,不执行语句。$6为s3c24x0,添加"SOC  =s3c24x0"所以,如果你尝试着执行config.mk文件一定会出现如下信息:

ARCH  = arm
CPU = arm920t
BOARD = CCJ
SOC = s3c24x0

  接下来看第二段代码,根据作者提供的注释我们知道要创建开发板指定头文件了。根绝最开始mkconfig中我们定义的APPEND值,执行语句,由于这里我定义的是APPEND=no,所以执行else分支,创建config.h头文件,并添加  /* Automatically generated - do not edit */    #include <configs/$1.h>  语句到config.h头文件中。 最后执行 exit 0 退出mkconfig文件。

2.u-boot的编译

  接下来我们要做的就是编译了,即make。我们回到Makefile文件,看与你所使用的硬件平台相关的代码,这里我使用的是ARM9。根据注释,可以得知这段代码用于载入架构,开发板信息,CPU的配置。

# load ARCH, BOARD, and CPU configuration
include $(OBJTREE)/include/config.mk
export ARCH CPU BOARD VENDOR SOC ifndef CROSS_COMPILE
ifeq ($(HOSTARCH),ppc)
CROSS_COMPILE =
else
ifeq ($(ARCH),ppc)
CROSS_COMPILE = powerpc-linux-
endif
ifeq ($(ARCH),arm)
CROSS_COMPILE = arm-linux-
endif
ifeq ($(ARCH),i386)
ifeq ($(HOSTARCH),i386)
CROSS_COMPILE =
else
CROSS_COMPILE = i386-linux-
endif
endif

  在包含了我们之前所配置的config.mk文件后,Makefile代码就会根据我们配置的具体架构信息选择合适的交叉编译,很明显,因为我的是ARM架构,这里CROSS_COMPILE=arm-linux-。

export    CROSS_COMPILE

# load other configuration
include $(TOPDIR)/config.mk

  接着往下看,最下面我们包含了顶层目录下的config.mk文件。其实这里我发现有一个问题,就是之前我们不是也生成了一个config.mk吗?这其实是两个文件。之前那个完完全全是我们自己创建的config.mk,它的作用就是为了给接下来包含的这个原作者书写的config.mk传值,传递CPU、SOC、ARCH、BOARD、VENDOR的参数值。如果你手头上有下载好的u-boot源文件,打开u-boot文件夹,你就会看到一个config.mk。打开它就明白我刚刚所说的,传递的参数进入这个config,mk配置交叉编译选项、选择结构依赖规则。如下图:

ifeq ($(ARCH),arm)
ifeq ($(CROSS_COMPILE),powerpc-netbsd-)
PLATFORM_CPPFLAGS+= -D__ARM__
endif
ifeq ($(CROSS_COMPILE),powerpc-openbsd-)
PLATFORM_CPPFLAGS+= -D__ARM__
endif
endif ifeq ($(ARCH),blackfin)
PLATFORM_CPPFLAGS+= -D__BLACKFIN__ -mno-underscore
endif ifdef ARCH
sinclude $(TOPDIR)/$(ARCH)_config.mk # include architecture dependend rules
endif
ifdef CPU
sinclude $(TOPDIR)/cpu/$(CPU)/config.mk # include CPU specific rules
endif
ifdef SOC
sinclude $(TOPDIR)/cpu/$(CPU)/$(SOC)/config.mk # include SoC specific rules
endif

  在这个文件中,也有调用结构依赖规则,就是你所用的硬件架构对应的结构规则config文件,这里不再详述。可以自己打开看看。

  

  

  接下来接着看Makefile的命令:

  

# U-Boot objects....order is important (i.e. start must be first)

OBJS  = cpu/$(CPU)/start.o
OBJS := $(addprefix $(obj),$(OBJS)) LIBS = lib_generic/libgeneric.a
LIBS += board/$(BOARDDIR)/lib$(BOARD).a
LIBS += cpu/$(CPU)/lib$(CPU).a
ifdef SOC
LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a
endif
LIBS += lib_$(ARCH)/lib$(ARCH).a
LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a

  首先是目标文件start.o,然后是给LIBS变量指定平台/开发板相关的库。这些库是由各个模块自身编译生成的。(.a文件)

$(OBJS):
echo $(OBJS)
$(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@)) $(LIBS):
$(MAKE) -C $(dir $(subst $(obj),,$@)) usb:
$(MAKE) -C drivers/usb $(SUBDIRS):
$(MAKE) -C $@ all

  这些.o文件和.a文件就是由上面的语句编译生成的。其中库文件是由每个模块子目录自己make后生成的。编译过程就此结束。

3.u-boot的链接

  通过连接,我们可以得到最终的u-boot的hex文件,srec文件,二进制文件,img文件。

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

all:        $(ALL)

$(obj)u-boot.hex:    $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@ $(obj)u-boot.srec: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O srec $< $@ $(obj)u-boot.bin: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@ $(obj)u-boot.img: $(obj)u-boot.bin
./tools/mkimage -A $(ARCH) -T firmware -C none \
-a $(TEXT_BASE) -e \
-n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' $(VERSION_FILE) | \
sed -e 's/"[ ]*$$/ for $(BOARD) board"/') \
-d $< $@ $(obj)u-boot.dis: $(obj)u-boot
$(OBJDUMP) -d $< > $@ $(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

  

  LDFLAGS确定了程序地址和代码段、数据段的排列位置。下图是board/CCJ/u-boot.lds文件

SECTIONS
{
. = 0x00000000; . = ALIGN();
.text :
{
cpu/arm920t/start.o (.text)
board/CCJ/boot_init.o (.text)
*(.text)
} . = ALIGN();
.rodata : { *(.rodata) } . = ALIGN();
.data : { *(.data) } . = ALIGN();
.got : { *(.got) } . = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .; . = ALIGN();
__bss_start = .;
.bss : { *(.bss) }
_end = .;
}

  首先定义了基地址0x00000000,这个地址并不是我们实际存储程序的地址,而是在board目录下我们还要定义一个config.mk,包含内容TEXT_BASE = 0x33F80000。这个地址是我们自己定义的,基地址+偏移地址,才是u-boot程序最终存储的地址(0+0x33F80000=0x33F80000)。首先存储start.o的代码段,初始化代码段,然后是只读数据段,数据段。就此,u-boot链接完毕。

  

  第一次写博客,感觉真挺累的,但是一写就停不下来。还是很高兴的,将自己学到的知识分享给大家,其实嵌入式的学习路程真的很长,需要不断地努力,还要有兴趣。我很喜欢乔布斯说的那句话:“人类追求极致,并分享给同类,然后才能共同进步。”在实际的编程、比赛、项目中,我们都需要合作和互相学习,尤其是不同领域的人们合作更会创造出令人惊叹的事物,会让我们感叹人类创造力的同时让生活更美好。这大概也是GPL协议的初衷吧,开源、但又尊重人们的自由和所属权。

  今天写的博客都是基于这段时间的学习。希望能帮到大家。有写的不对的地方也希望大家给我指正,我会非常高兴,因为我喜欢交流^_^。

  最后谢谢u-boot的作者,能够将u-boot开源,并提供下载以供全世界的人们学习使用。我尊重u-boot的作者的版权:Wolfgang Denk, DENX Software Engineering, wd@denx.de.

                                    

                                    CCJ

                               2016-11-12 23:43:13

  

  

  

  

u-boot的配置、编译及链接的更多相关文章

  1. 使用pybind11为Python编写C++扩展(一)配置篇:Build(编译和链接)

    目录 Setuptools CMake 最后决定选用pybind11,理由如下: 比python原生的C API看起来人性多了 我的C++代码不是现成的,需要一定的C++开发工作量,所以感觉cytho ...

  2. 【嵌入式开发】 Linux Kernel 下载 配置 编译 安装 及 驱动简介

    作者 : 韩曙亮 转载请出名出处 : http://blog.csdn.net/shulianghan/article/details/38636827 一. Linux 内核简介 1. 内核功能简介 ...

  3. AOSP ON MAKO(在NEXUS 4上刷ANDROID 4.4 源代码包-下载/配置/编译/刷机)

    AOSP ON MAKO(在NEXUS 4上刷ANDROID 4.4 源代码包-下载/配置/编译/刷机) 特别感谢google官方文档及AOSP源代码开放 參考链接: https://source.a ...

  4. 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明

    目录 联盛德 HLK-W806 (一): Ubuntu20.04下的开发环境配置, 编译和烧录说明 联盛德 HLK-W806 (二): Win10下的开发环境配置, 编译和烧录说明 联盛德 HLK-W ...

  5. 原创 C++应用程序在Windows下的编译、链接:第三部分 静态链接(二)

    3.5.2动态链接库的创建 3.5.2.1动态链接库的创建流程 动态链接库的创建流程如下图所示: 在系统设计阶段,主要的设计内容包括:类结构的设计以及功能类之间的关系,动态链接库的接口.在动态链接库中 ...

  6. GADL配置编译

    GADL配置编译 文章1:Win7(32/64)VS2010配置编译GDAL环境(图文教程+亲测可用!) 转载:http://malagis.com/win7-vs2010-gdal.html 近的一 ...

  7. App开发流程之数据持久化和编译静态链接库

    先记录数据持久化. iOS客户端提供的常用数据持久化方案:NSUserDefaults代表的用户设置,NSKeydArchiver代表的归档,plist文件存储,SQLite数据库(包括上层使用的Co ...

  8. Spring Boot 属性配置和使用(转)

    Spring Boot 属性配置和使用 Spring Boot 允许通过外部配置让你在不同的环境使用同一应用程序的代码,简单说就是可以通过配置文件来注入属性或者修改默认的配置. Spring Boot ...

  9. Linux内核分析(一)---linux体系简介|内核源码简介|内核配置编译安装

    原文:Linux内核分析(一)---linux体系简介|内核源码简介|内核配置编译安装 Linux内核分析(一) 从本篇博文开始我将对linux内核进行学习和分析,整个过程必将十分艰辛,但我会坚持到底 ...

随机推荐

  1. 本人提供微软系.NET技术顾问服务,欢迎企业咨询!

    背景: 1:目前微软系.NET技术高端人才缺少. 2:企业很难直接招到高端技术人才. 3:本人提供.NET技术顾问,保障你的产品或项目在正确的技术方向. 技术顾问服务 硬服务项: 1:提供技术.决策. ...

  2. django server之间通过remote user 相互调用

    首先,场景是这样的:存在两个django web应用,并且两个应用存在一定的联系.某些情况下彼此需要获取对方的数据. 但是我们的应用肯经都会有对应的鉴权机制.不会让人家随随便便就访问的对吧.好比上车要 ...

  3. Spring中Bean的实例化

                                    Spring中Bean的实例化 在介绍Bean的三种实例化的方式之前,我们首先需要介绍一下什么是Bean,以及Bean的配置方式. 如果 ...

  4. 设计模式之行为类模式大PK

                                        行为类模式大PK 行为类模式包括责任链模式.命令模式.解释器模式.迭代器模式.中介者模式.备忘录模式.观察者模式.状态模式.策略 ...

  5. [原]Cachedb 网络模块文档

    Cachedb 网络模块文档 整体结构 多路复用 (epoll 模块) 事件驱动 (事件封装) 缓冲管理 (上层buffer管理) 设计思想 层次化的设计,每一个模块只调用上一个模块的接口,并将耦合聚 ...

  6. PhpStorm和WAMP配置调试参数,问题描述Error. Interpreter is not specified or invalid. Press “Fix” to edit your project configuration.

    PhpStorm和WAMP配置调试参数 问题描述: Error. Interpreter is not specified or invalid. Press “Fix” to edit your p ...

  7. psoc学习

    第一是:项目的路径需要放在Documents and Settings\,也就是默认的文件夹的地方,不然会报错错误范例为:Question:CY8CKIT-023 kit example projec ...

  8. 【JS基础】对象

    delete 可以删除对象属性及变量 function fun(){ this.name = 'mm'; } var obj = new fun(); console.log(obj.name);// ...

  9. 【JavaScript】javascript中伪协议(javascript:)使用探讨

    javascript:这个特殊的协议类型声明了URL的主体是任意的javascript代码,它由javascript的解释器运行. 比如下面这个死链接: <a href="javasc ...

  10. 敏捷转型历程 - Sprint3 Grooming

    我: Tech Leader 团队:团队成员分布在两个城市,我所在的城市包括我有4个成员,另外一个城市包括SM有7个成员.另外由于我们的BA离职了,我暂代IT 的PO 职位.PM和我在一个城市,但他不 ...