u-boot编译链接过程分析(ARM)

来源:互联网 发布:java判断文件编码格式 编辑:程序博客网 时间:2024/05/29 03:21

1.config.mk

通常配置文件生成之后,即可使用make all进行编译,在如下Makefile里面

ifeq ($(obj)include/config.mk,$(wildcard $(obj)include/config.mk))# load ARCH, BOARD, and CPU configurationinclude $(obj)include/config.mkexportARCH CPU BOARD VENDOR SOCifndef CROSS_COMPILEifeq ($(HOSTARCH),$(ARCH))CROSS_COMPILE =elseifeq ($(ARCH),ppc)CROSS_COMPILE = ppc_8xx-endififeq ($(ARCH),arm)CROSS_COMPILE = arm-linux-endif
......
# load other configurationinclude $(TOPDIR)/config.mk

包含了之前我们生成的include/config.mk,并且在以后还能使用ARCH、CPU、BOARD、SOC

然后确定编译平台和所使用的交叉编译工具,然后包含了顶层的config.mk,看顶层config.mk如下几行

## Include the make variables (CC, etc...)#AS= $(CROSS_COMPILE)asLD= $(CROSS_COMPILE)ldCC= $(CROSS_COMPILE)gccCPP= $(CC) -E

当我们编译的时候使用CROSS_COMPILE=arm-linux-,就会自动编译为相应工具,比如gcc就为arm-linux-gcc

2. LDFLAGS、AFLAGS、CFLAGS

LDFLAGS += -Bstatic -T $(LDSCRIPT) $(PLATFORM_LDFLAGS)ifneq ($(TEXT_BASE),)LDFLAGS += -Ttext $(TEXT_BASE)

这里LDSCRIPT如下

ifndef LDSCRIPT#LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds.debugifeq ($(CONFIG_NAND_U_BOOT),y)LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot-nand.ldselseLDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.ldsendif

这里使用LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds

这里TOPDIR就是当前目录,而BOARDDIR为下面所示

ifdefARCHsinclude $(TOPDIR)/$(ARCH)_config.mk# include architecture dependend rulesendififdefCPUsinclude $(TOPDIR)/cpu/$(CPU)/config.mk# include  CPUspecific rulesendififdefSOCsinclude $(TOPDIR)/cpu/$(CPU)/$(SOC)/config.mk# include  SoCspecific rulesendififdefVENDORBOARDDIR = $(VENDOR)/$(BOARD)elseBOARDDIR = $(BOARD)endififdefBOARDsinclude $(TOPDIR)/board/$(BOARDDIR)/config.mk# include board specific rulesendif

这里默认使用sinclude $(TOPDIR)/board/$(BOARDDIR)/config.mk

在/board/smdk2410/config.mk,有如下几句

## SMDK2410 has 1 bank of 64 MB DRAM## 3000'0000 to 3400'0000## Linux-Kernel is expected to be at 3000'8000, entry 3000'8000# optionally with a ramdisk at 3080'0000## we load ourself to 33F8'0000## download area is 3300'0000#TEXT_BASE = 0x33F80000

其实这个跟2440的SDRAM地址是一致的,这样LDFLAGS中就有 -T board/smdk2410/u-boot.lds -Ttext 0x33F80000

#########################################################################exportCONFIG_SHELL HPATH HOSTCC HOSTCFLAGS CROSS_COMPILE \AS LD CC CPP AR NM STRIP OBJCOPY OBJDUMP \MAKEexportTEXT_BASE PLATFORM_CPPFLAGS PLATFORM_RELFLAGS CPPFLAGS CFLAGS AFLAGS#########################################################################ifndef REMOTE_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

里面使用的AFLAGS

AFLAGS := $(AFLAGS_DEBUG) -D__ASSEMBLY__ $(CPPFLAGS)

这里AFLAGS_DEBUG、CPPFLAGS又是什么呢??

CPPFLAGS := $(DBGFLAGS) $(OPTFLAGS) $(RELFLAGS)\-D__KERNEL__ifneq ($(TEXT_BASE),)CPPFLAGS += -DTEXT_BASE=$(TEXT_BASE)endififneq ($(OBJTREE),$(SRCTREE))CPPFLAGS += -I$(OBJTREE)/include2 -I$(OBJTREE)/includeendifCPPFLAGS += -I$(TOPDIR)/includeCPPFLAGS += -fno-builtin -ffreestanding -nostdinc\-isystem $(gccincdir) -pipe $(PLATFORM_CPPFLAGS)ifdef BUILD_TAGCFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes \-DBUILD_TAG='"$(BUILD_TAG)"'elseCFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypesendifCFLAGS += $(call cc-option,-fno-stack-protector)# avoid trigraph warnings while parsing pci.h (produced by NIOS gcc-2.9)# this option have to be placed behind -Wall -- that's why it is hereifeq ($(ARCH),nios)ifeq ($(findstring 2.9,$(shell $(CC) --version)),2.9)CFLAGS := $(CPPFLAGS) -Wall -Wno-trigraphsendifendif# $(CPPFLAGS) sets -g, which causes gcc to pass a suitable -g<format># option to the assembler.AFLAGS_DEBUG :=

CPPFLAGES与DBGFLAGS、 OPTFLAGS、RELFLAGS有关

RELFLAGS= $(PLATFORM_RELFLAGS)DBGFLAGS= -g # -DDEBUGOPTFLAGS= -Os #-fomit-frame-pointer

这里PLATFORM _RELFLAGS

# clean the slate ...PLATFORM_RELFLAGS =PLATFORM_CPPFLAGS =PLATFORM_LDFLAGS =

可以看到为空,以start.S为例做说明

$(CPP) $(AFLAGS) -o $@ $< //$@规则的目标文件名,$<第一个依赖的名字,这里是start.S

 =>>arm-linu-gcc -E -D__ASSEMBLY__ -g # -DDEBUG -Os #-fomit-frame-pointer -D__KERNEL__ -o start.s start.S

$(obj)%.o: %.S
 $(CC) $(AFLAGS) -c -o $@ $<

等价于:arm-linu-gcc  -D__ASSEMBLY__ -g # -DDEBUG -Os #-fomit-frame-pointer -D__KERNEL__ -o start.o start.S

3.连接编译(包括*.o, *.a, )

# 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.aLIBS += lib_generic/lzma/liblzma.aLIBS += $(shell if [ -f board/$(VENDOR)/common/Makefile ]; then echo \"board/$(VENDOR)/common/lib$(VENDOR).a"; fi)LIBS += cpu/$(CPU)/lib$(CPU).a

......

LIBS += lib_$(ARCH)/lib$(ARCH).aLIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a \fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a fs/yaffs2/libyaffs2.aLIBS += net/libnet.aLIBS += disk/libdisk.aLIBS += drivers/bios_emulator/libatibiosemu.aLIBS += drivers/block/libblock.aLIBS += drivers/dma/libdma.aLIBS += drivers/hwmon/libhwmon.aLIBS += drivers/i2c/libi2c.aLIBS += drivers/input/libinput.aLIBS += drivers/misc/libmisc.aLIBS += drivers/mmc/libmmc.aLIBS += drivers/mtd/libmtd.aLIBS += drivers/mtd/nand/libnand.aLIBS += drivers/mtd/nand_legacy/libnand_legacy.a

......

上面第一个编译的就是start.o,下面就是平台/开发板相关的各个目录,通用目录下的相应的库,OBJS、LIBS所代表的.o、.a文件就是uboot的构成,这两个文件的编译形成

$(OBJS):depend $(obj)include/autoconf.mk$(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))$(LIBS):depend $(obj)include/autoconf.mk$(MAKE) -C $(dir $(subst $(obj),,$@))

上面的depend一般是自己设置和定义的宏,比如使用mini2440生成的autoconf.mk

CONFIG_SYS_CLK_FREQ=12000000CONFIG_RTC_S3C24X0=yCONFIG_STACKSIZE="(128*1024)"CONFIG_BOOTDELAY=3CONFIG_NR_DRAM_BANKS=yCONFIG_ETHADDR="08:00:3e:26:0a:5b"CONFIG_CMD_CONSOLE=yCONFIG_ENV_OVERWRITE=yCONFIG_CMD_NET=yCONFIG_CMD_NFS=yCONFIG_ENV_SIZE="0x10000"CONFIG_CMD_PING=yCONFIG_CMD_FLASH=yCONFIG_AMD_LV800=yCONFIG_ENV_ADDR="(CFG_FLASH_BASE + 0x0F0000)"CONFIG_S3C2440=yCONFIG_IPADDR="192.168.1.70"CONFIG_BOOTP_HOSTNAME=yCONFIG_MINI2440=yCONFIG_CMD_ECHO=yCONFIG_BOOTP_GATEWAY=yCONFIG_BOOTFILE="elinos-lart"CONFIG_BAUDRATE=115200CONFIG_NETMASK="255.255.255.0"CONFIG_ARM920T=yCONFIG_CMD_ELF=yCONFIG_CMD_DATE=yCONFIG_CMD_ENV=yCONFIG_SERVERIP="192.168.1.100"

上面只是列取部分,其他的请自行参考文档

$(notdir names):抽取names每一个文件名中除路径部分外一切字符 如(notdir src/foo.c hi) 结果为foo.c hi

$(if con ,then part[,else-part]): 首先去掉con前导空格、结尾空格去掉,然后扩展,扩展字符串为非空,con为真,计算then部分,否则计算else部分

$(subst from,to,text): 在文本text中使用to替换每一处from,比如$(subst ee,EE, feet on the street) ,结果为:fEET on the strEEt

$(dir names):  抽取names中的路径,比如$(dir src/foo.c hi) 结果为src/ ./

$(OBJS): depend $(obj)include/autoconf.mk
  $(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))

对于OBJS中的每个成员,都进入cpu/$(CPU)目录编译他们

$(LIBS): depend $(obj)include/autoconf.mk
  $(MAKE) -C $(dir $(subst $(obj),,$@))

对于LIBS中的每个成员,都进入相应的子目录执行make命令,他们将制定的文件编译链接成一个库文件.a

当所有的OBJS,LIBS所表示的.o、.a文件都生成后,就可以进行连接了

$(obj)u-boot.bin:$(obj)u-boot$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
$(obj)u-boot:depend $(SUBDIRS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT)UNDEF_SYM=`$(OBJDUMP) -x $(LIBBOARD) $(LIBS) | \sed  -n -e 's/.*\($(SYM_PREFIX)__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

u-boot指定生成了ELF格式的uboot文件,最后转换为二进制文件u-boot.bin,LDFLAGS确定了连接方式

4.总结uboot编译过程

(1)首先编译cpu/$(CPU)/start.S,还可能编译cpu/$(CPU)其他文件

(2)编译平台/开发板相关的目录,每个通用目录下的Makefile,生成相应的库

(3)将.o,.a文件按照board/$(BOARDADDIR)/config.mk、u-boot.lds中指定的代码段起始地址,连接脚本进行连接

(4)将ELF格式的uboot转换为其他格式,比如二进制格式u-boot.bin

原创粉丝点击