uboot之Makefile编译过程详解

1.Makefile分析:

ubootversion(版本信息):

 

VERSION = 1

PATCHLEVEL = 3

SUBLEVEL = 4

EXTRAVERSION =

U_BOOT_VERSION =$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)

 

VERSION

1级版本号: 1

PATCHLEVEL

2级版本号: 3

SUBLEVEL

3级版本号: 4

EXTRAVERSION

额外信息:自己的信息

U_BOOT_VERSION

最终版本号:1.3.4xxx

 

HOSTARCH :=$(shell uname -m | \

sed -es/i.86/i386/ \

    -e s/sun4u/sparc64/ \

    -e s/arm.*/arm/ \

    -e s/sa110/arm/ \

    -e s/powerpc/ppc/ \

    -e s/ppc64/ppc/ \

    -e s/macppc/ppc/)

 

HOSTOS := $(shelluname -s | tr '[:upper:]' '[:lower:]' | \

    sed -e 's/\(cygwin\).*/cygwin/')

 

HOSTARCH

主机CPU架构

HOSTOS

主机的系统信息

uname -m

这个命令可以得到主机的CPU信息

uname -s

这个命令可以得到主机的操作系统信息

|

(管道),参考shell内容

tr '[:upper:]' '[:lower:]'

将得到的信息转换成小写的

 

HOSTARCH HOSTOS :这里的两个环境变量主要是得出了一个我们用于编译这个Makefile时的主机的一些信息,在后边的操作中会用到这些信息做一些处理。

 

静默编译:

 

# Allow forsilent builds

ifeq (,$(findstrings,$(MAKEFLAGS))) 

XECHO = echo

else

XECHO = :

endif

 

静默编译原因:

·平时的默认编译的时候会打印出很多的编译信息。但是有时候我们并不希望看到这些编译信息,因此,可以用静默编译的方式来解决这个问题。

·静默编译的使用方法:

make -s

-s 会作为 MAKEFLAGS 传给Makefile,在上面的代码中作用下,XECHO 变量就会被清空(默认等于echo,于是就实现了静默编译

 

两种编译方式:

 

ifdef O

ifeq("$(origin O)", "command line")

BUILD_DIR := $(O)

endif

endif

 

ifneq($(BUILD_DIR),)

saved-output :=$(BUILD_DIR)

 

# Attempt tocreate a output directory.

$(shell [ -d${BUILD_DIR} ] || mkdir -p ${BUILD_DIR})

 

# Verify if itwas successful.

BUILD_DIR :=$(shell cd $(BUILD_DIR) && /bin/pwd)

$(if$(BUILD_DIR),,$(error output directory "$(saved-output)" does notexist))

endif # ifneq($(BUILD_DIR),)

 

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

SRCTREE                :=$(CURDIR)

TOPDIR                :=$(SRCTREE)

LNDIR                :=$(OBJTREE)

export        TOPDIRSRCTREE OBJTREE

 

MKCONFIG        :=$(SRCTREE)/mkconfig

export MKCONFIG

 

ifneq($(OBJTREE),$(SRCTREE))

REMOTE_BUILD        :=1

exportREMOTE_BUILD

endif

 

# $(obj) and(src) are defined in config.mk but here in main Makefile

# we also needthem before config.mk is included which is the case for

# some targetslike unconfig, clean, clobber, distclean, etc.

ifneq($(OBJTREE),$(SRCTREE))

obj :=$(OBJTREE)/

src :=$(SRCTREE)/

else

obj :=

src :=

endif

export obj src

 

# Make sureCDPATH settings don't interfere

unexport CDPATH

 

上面的代码其实主要就是对编译方式进行了一下选择。因为目前的编译方式可以有两种,一种是原地编译,一种是文件夹输出编译。

·原地编译:

这是默认的一种编译方式,在这种编译方式下,编译出来的文件都会被默认存放在当前文件夹下面。这种编译方式比较简单,但是,他也有很多的坏处:第一,他会污染原有的文件目录。第二,一套源代码只能按照一种配置和编译方式进行处理,无法同时维护两个以上的编译和配置方式。

·文件夹输出编译:

这里uboot的这种编译方式主要是从linux kernel学习过来的。在这种编译方式中,你可以指定一个文件夹,然后将编译好的输出文件全部放到这个文件夹下面,这样子就可以实现同一套源代码,可以实现多次配置和编译,并且他们之间不会相互影响。

使用方法:

方法一:make O=输出目录

方法二:export BULLD_DIR=输出目录, 然后在make

其中,方法一的优先级高于方法二的。

 

环境变量:

变量名

变量作用

OBJTREE

编译出的.o文件存放的目录,默认情况下是当前目录,在文件夹输出编译模式下,是我们指定的目录

SRCTREE

源码的根目录,就是源代码的当前目录。

TOPDIR

等于 SRCTREE,也是主目录

LNDIR

等于 OBJTREE

MKCONFIG

它的值就是我们源码根目录下面的mkconfig。这个mkconfig是一个脚本,这个脚本是在uboot配置阶段的配置脚本。

 

Makefile

 

include$(obj)include/config.mk

 

上面的config.mk并不是源码自带,是在配置过程中(make x210_sd_config)中生成的文件。因此,这个文件的值是和我们的配过程相关的。

当我们编译完成后,在include目录中的config.mk文件中有:

 

ARCH   = arm                                                                   

CPU    = s5pc11x

BOARD  = x210

VENDOR = samsung

SOC    = s5pc110

 

这里我们得出了我们的的当前硬件的一些信息。

 

Makefile

export        ARCHCPU BOARD VENDOR SOC

 

这里将上面的五个环境变量导出到makefile中来了,实际上就是在makefile中定义了这五个环境变量。

 

ifndefCROSS_COMPILE

ifeq($(HOSTARCH),$(ARCH))

CROSS_COMPILE =

else

ifeq($(ARCH),ppc)

CROSS_COMPILE =ppc_8xx-

endif

ifeq($(ARCH),arm)

#CROSS_COMPILE =arm-linux-

#CROSS_COMPILE =/usr/local/arm/4.4.1-eabi-cortex-a8/usr/bin/arm-linux-

#CROSS_COMPILE =/usr/local/arm/4.2.2-eabi/usr/bin/arm-linux-

CROSS_COMPILE =/usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-

endif

ifeq($(ARCH),i386)

CROSS_COMPILE =i386-linux-

endif

 

这里,我们主要是通过CROSS_COMPILE 这个变量来定义交叉编译工具链的前缀,因为定义前缀的目的主要就是确定使用的交叉编译工具,而后缀的作用主要是确定用这个工具的那些功能。因为大部分的交叉编译工具链的功能都是一样的,但是这些交叉编译工具因为不同的CPU架构的不同而不同。所以,我们可以通过这个来选择一个合适的交叉扁你工具。

可以看到,ifeq ($(ARCH),arm)ifeq($(ARCH),i386)   这两句就是根据ARCH的值来确定CROSS_COMPILE的值的,而这里,我们根据自己的交叉编译工具链的位置,而将CROSS_COMPILE的值定义为我们所选的位置,等到将来要编译代码的时候,他就会自动使用我们的编译工具了。

在实际的应用中,我们也会通过:make CROSS_COMPILE=xxx这种方式来选择我们的交叉编译工具。

 

# load otherconfiguration

include$(TOPDIR)/config.mk

 

上面的一行代码将主目录下面的config.mk文件导入了主Makefile中,因此在这里要对config.mk进行一下分析。

 

config.mk中:

 

AS        =$(CROSS_COMPILE)as

LD        =$(CROSS_COMPILE)ld

CC        =$(CROSS_COMPILE)gcc

CPP        =$(CC) -E

AR        =$(CROSS_COMPILE)ar

NM        =$(CROSS_COMPILE)nm

LDR        =$(CROSS_COMPILE)ldr

STRIP        =$(CROSS_COMPILE)strip

OBJCOPY =$(CROSS_COMPILE)objcopy

OBJDUMP =$(CROSS_COMPILE)objdump

RANLIB        =$(CROSS_COMPILE)RANLIB

 

前面将CROSS_COMPILE替换我们的编译工具前缀。

 

# Load generatedboard configuration

sinclude$(OBJTREE)/include/autoconf.mk

 

·这里我们有在config.mk中调用的autoconf.mk文件,autoconfig.mk并不是我们源码自带的,而是我们在编译的过程中自动产生的。

·这个文件的作用就是知道整个uboot的编译过程。这个文件其实是很多CONFIG_开头的宏(可以理解为变量),这些宏会影响我们的uboot编译的过程走向(根据条件编译)。在uboot中的很多地方都使用了条件编译,因此,uboot才具有广泛的可移植性。

·这个文件是根据配置过程中的其他文件产生的,他的原材料在源码目录的include/configs/xxx.h头文件中(x210开发板为include/configs/x210_sd.h)。这个头文件里面全都是宏定义。这些宏定义就是我们当前对开发板的移植。每一个开发板的移植都对应着这个目录下的一个头文件。这些配置就是我们移植uboot的关键所在。

 

链接脚本:

 

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

endif

 

从上面的代码可知,对于如果CONFIG_NAND_U_BOOT等于y,那么我们就会LDSCRIPT就会i等于u-boot-nand.lds,否则就等于u-boot.lds。因为我们的开发板使用的是iNand,因此,我们就会的LDSCRIPT就等于u-boot.lds。我们在我们的文件夹board/samsung/x210下面确实可以看到u-boot.lds这个链接脚本。我们在分析uboot的编译链接过程时就要考虑这个链接脚本。

 

config.mk

ifneq($(TEXT_BASE),)

CPPFLAGS +=-DTEXT_BASE=$(TEXT_BASE)

endif

 

在这里,我们可以看到出现了一个TEXT_BASE,而这个变量其实是在board/samsung/x210中的config.mk中对它进行了赋值的。这里我们将它赋值成0xc3e00000,然后把它当作了基地址。这里的0xc3e00000其实是一个地址映射而已,将它映射到了23e00000中。

 

ifndefREMOTE_BUILD

 

%.s:        %.S

$(CPP) $(AFLAGS)-o $@ $<

%.o:        %.S

$(CC) $(AFLAGS)-c -o $@ $<

%.o:        %.c

$(CC) $(CFLAGS)-c -o $@ $<

 

else

 

$(obj)%.s:        %.S

$(CPP) $(AFLAGS)-o $@ $<

$(obj)%.o:        %.S

$(CC) $(AFLAGS)-c -o $@ $<

$(obj)%.o:        %.c

$(CC) $(CFLAGS)-c -o $@ $<

endif

 

这里是一个自动推导规则。至此,config.mk 分析结束。

 

回到主Makefile中继续分析。

 

前面主要分析了Makefile中的一些和编译环境相关的代码,他们主要是在编译之前先把其中要做的必要工作提前做了。还没有涉及到具体代码的编译中。

 

下面是主要的编译代码:

 

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.ldr:        $(obj)u-boot

$(LDR) -T$(CONFIG_BFIN_CPU) -f -c $@ $< $(LDR_FLAGS)

 

$(obj)u-boot.ldr.hex:        $(obj)u-boot.ldr

$(OBJCOPY)${OBJCFLAGS} -O ihex $< $@ -I binary

 

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

$(OBJCOPY)${OBJCFLAGS} -O srec $< $@ -I binary

……

 

 

这些以及之后的一些代码才开始真正的编译,从这里开始,因为之前已经将一些编译的准备工作做了一些了。因此,在这里我们就可以直接对所要编译的代码进行编译和链接。然后就会真正的生成一些我们需要的文件。

 

配置过程:

 

当我们配置我们的开发板时会使用make x210_sd_config,于是,我们就能会来执行这个目标代码:

 

x210_sd_config:        unconfig

@$(MKCONFIG)$(@:_config=) arm s5pc11x x210 samsung s5pc110

@echo"TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/x210/config.mk

 

这来两行代码具体做了两件事情:

1@$(MKCONFIG)$(@:_config=) arm s5pc11x x210 samsung s5pc110

首先它先将后面的几个参数传递进了mkconfig则会个文件中。

一共传递进去了六个参数:

$(@:_config=)

 arm

s5pc11x

 x210

 samsung

 s5pc110

 

2@echo"TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/x210/config.mk

然后它又再创建了一个文件 $(obj)board/samsung/x210/config.mk,将TEXT_BASE = 0xc3e00000写到了config.mk这个文件中去。

 

具体就是通过这两步完成了对整个文件的配置过程。

其中,第一步的的传参过程我们将详细分析一下。

 

配置过程参考下节。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

猜你喜欢

转载自blog.csdn.net/qq_25827755/article/details/53672073