前言

  这篇博文是 uboot makefile构建分析的续篇,继续分析uboot构建u-boot.bin的过程

构建u-boot.bin过程分析

  makefile一开始,就是确定链接脚本。在构建uboot和kernel的过程,链接脚本是非常重要的。它决定了你程序里面每个段的位置(加载位置和运行位置)。在编译应用程序时,我们一般不需要指定链接脚本,因为链接器这时候会采用默认的,通过命令ld --verbose可以查看默认的链接脚本。之所以uboot和kernel不采用默认的,是因为它们有特殊要求,比如要求哪些部分必须放在最前面,哪些部分放在哪个地址上等等。下面看uboot寻找链接脚本的代码(直接将说明插入到代码了):

LDSCRIPT_MAKEFILE_DIR = $(dir $(LDSCRIPT))                                      

ifndef LDSCRIPT  #如果没有指定LDSCRIPT
#LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds.debug
ifdef CONFIG_SYS_LDSCRIPT #如果指定了CONFIG_SYS_LDSCRIPT,那么LDSCRIPT就用CONFIG_SYS_LDSCRIPT指定的作为链接脚本
# need to strip off double quotes
LDSCRIPT := $(subst ",,$(CONFIG_SYS_LDSCRIPT))
endif
endif # If there is no specified link script, we look in a number of places for it
ifndef LDSCRIPT #如果还没指定LDSCRIPT(也就是板级没有指定CONFIG_SYS_LDSCRIPT,那么只好根据构建的方式自己采用默认的了
ifeq ($(CONFIG_NAND_U_BOOT),y) #如果是nand boot模式
LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot-nand.lds
ifeq ($(wildcard $(LDSCRIPT)),)#如果board/$(BOARDDIR)/u-boot-nand.lds不存在,用$(CPUDIR)/u-boot-nand.lds
LDSCRIPT := $(TOPDIR)/$(CPUDIR)/u-boot-nand.lds
endif
endif
ifeq ($(wildcard $(LDSCRIPT)),) #如果nand对应的lds文件不存在,用board/$(BOARDDIR)/u-boot.lds
LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds
endif
ifeq ($(wildcard $(LDSCRIPT)),) #如果对应的lds文件还不存在,用$(CPUDIR)/u-boot.lds
LDSCRIPT := $(TOPDIR)/$(CPUDIR)/u-boot.lds
endif
ifeq ($(wildcard $(LDSCRIPT)),) ##如果对应的文件还不存在,用arch/$(ARCH)/cpu/u-boot.lds
LDSCRIPT := $(TOPDIR)/arch/$(ARCH)/cpu/u-boot.lds
# We don't expect a Makefile here
LDSCRIPT_MAKEFILE_DIR =
endif
ifeq ($(wildcard $(LDSCRIPT)),)
$(error could not find linker script)
endif
endif

如果板子有自己对应的链接脚本,不需要通用的,那么可以通过配置LDSCRIPT或者CONFIG_SYS_LDSCRIPT来实现。从上一篇博文 uboot makefile构建分析可以知道,该头文件会在执行第二步配置的时候,会被包含到config.h里。下面是我这个uboot版本里有添加这些宏的板子:

$ grep -rns CONFIG_SYS_LDSCRIPT include/
include/configs/actux2.h:27:#define CONFIG_SYS_LDSCRIPT "board/actux2/u-boot.lds"
include/configs/MPC8569MDS.h:60:#define CONFIG_SYS_LDSCRIPT $(TOPDIR)/$(CPUDIR)/u-boot-nand.lds
include/configs/BSC9131RDB.h:45:#define CONFIG_SYS_LDSCRIPT "arch/powerpc/cpu/mpc85xx/u-boot-nand.lds"
include/configs/actux1.h:27:#define CONFIG_SYS_LDSCRIPT "board/actux1/u-boot.lds"
include/configs/mpq101.h:168:#define CONFIG_SYS_LDSCRIPT "board/mercury/mpq101/u-boot.lds"
include/configs/MVBLUE.h:31:#define CONFIG_SYS_LDSCRIPT "board/mvblue/u-boot.lds"
include/configs/p1_p2_rdb_pc.h:201:#define CONFIG_SYS_LDSCRIPT "arch/powerpc/cpu/mpc85xx/u-boot-nand.lds"
include/configs/MVSMR.h:22:#define CONFIG_SYS_LDSCRIPT "board/matrix_vision/mvsmr/u-boot.lds"
include/configs/sh7757lcr.h:21:#define CONFIG_SYS_LDSCRIPT "board/renesas/sh7757lcr/u-boot.lds"
include/configs/MPC8572DS.h:27:#define CONFIG_SYS_LDSCRIPT $(TOPDIR)/$(CPUDIR)/u-boot-nand.lds
include/configs/am335x_evm.h:29:#define CONFIG_SYS_LDSCRIPT "board/ti/am335x/u-boot.lds"
include/configs/Sandpoint8240.h:27:#define CONFIG_SYS_LDSCRIPT "board/sandpoint/u-boot.lds"
include/configs/P1023RDS.h:29:#define CONFIG_SYS_LDSCRIPT $(TOPDIR)/$(CPUDIR)/u-boot-nand.lds
include/configs/P1022DS.h:40:#define CONFIG_SYS_LDSCRIPT "arch/powerpc/cpu/mpc85xx/u-boot.lds"
include/configs/P1022DS.h:70:#define CONFIG_SYS_LDSCRIPT "arch/powerpc/cpu/mpc85xx/u-boot.lds"
include/configs/P1022DS.h:117:#define CONFIG_SYS_LDSCRIPT "arch/powerpc/cpu/mpc85xx/u-boot-nand.lds"
include/configs/P1010RDB.h:53:#define CONFIG_SYS_LDSCRIPT "arch/powerpc/cpu/mpc85xx/u-boot-nand.lds"
include/configs/rsdproto.h:27:#define CONFIG_SYS_LDSCRIPT "board/rsdproto/u-boot.lds"
include/configs/uc101.h:22:#define CONFIG_SYS_LDSCRIPT "arch/powerpc/cpu/mpc5xxx/u-boot-customlayout.lds"
include/configs/MOUSSE.h:37:#define CONFIG_SYS_LDSCRIPT "board/mousse/u-boot.lds"
include/configs/actux3.h:27:#define CONFIG_SYS_LDSCRIPT "board/actux3/u-boot.lds"
include/configs/dvlhost.h:28:#define CONFIG_SYS_LDSCRIPT "board/dvlhost/u-boot.lds"
include/configs/EVB64260.h:30:#define CONFIG_SYS_LDSCRIPT "board/evb64260/u-boot.lds"
include/configs/CATcenter.h:66:#define CONFIG_SYS_LDSCRIPT "board/dave/PPChameleonEVB/u-boot.lds"
include/configs/PPChameleonEVB.h:66:#define CONFIG_SYS_LDSCRIPT "board/dave/PPChameleonEVB/u-boot.lds"
include/configs/MPC8536DS.h:27:#define CONFIG_SYS_LDSCRIPT $(TOPDIR)/$(CPUDIR)/u-boot-nand.lds
include/configs/inka4x0.h:31:#define CONFIG_SYS_LDSCRIPT "arch/powerpc/cpu/mpc5xxx/u-boot-customlayout.lds"
include/configs/BSC9132QDS.h:54:#define CONFIG_SYS_LDSCRIPT "arch/powerpc/cpu/mpc85xx/u-boot-nand.lds"
include/configs/sh7752evb.h:20:#define CONFIG_SYS_LDSCRIPT "board/renesas/sh7752evb/u-boot.lds"
include/configs/P1_P2_RDB.h:41:#define CONFIG_SYS_LDSCRIPT $(TOPDIR)/$(CPUDIR)/u-boot-nand.lds
include/configs/Sandpoint8245.h:27:#define CONFIG_SYS_LDSCRIPT "board/sandpoint/u-boot.lds"

我这里假设板级没有指定LDSCRIPT或者CONFIG_SYS_LDSCRIPT,也不是nand boot模式,board/$(BOARDDIR)和$(CPUDIR)/u-boot.lds下也没有lds文件,那么就用arch/$(ARCH)/cpu/u-boot.lds了(即arch/arm/cpu/u-boot.lds),因为该文件默认就存在。

下面看all目标,因为该目标而导致最终的u-boot.bin被构建出来

# Always append ALL so that arch config.mk's can add custom ones
ALL-y += $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map all: $(ALL-y) $(SUBDIR_EXAMPLES)

从上面可知,首先构建u-boot.srec,然后是u-boot.bin,下面重点看u-boot.bin的构建过程,至于u-boot.srec(u-boot.srec是Motorola S-Record格式的image文件,我是从来没用过)和System.map(该文件按链接地址由小到大的顺序列出了所有符号,一般用于调试),就忽略吧,u-boot.bin的规则如下:

$(obj)u-boot.bin:   $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
$(BOARD_SIZE_CHECK)

依赖u-boot,于是看u-boot

$(obj)u-boot:   depend \
$(SUBDIR_TOOLS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT) $(obj)u-boot.lds
$(GEN_UBOOT)
ifeq ($(CONFIG_KALLSYMS),y)
smap=`$(call SYSTEM_MAP,$(obj)u-boot) | \
awk '$$2 ~ /[tTwW]/ {printf $$1 $$3 "\\\\000"}'` ; \
$(CC) $(CFLAGS) -DSYSTEM_MAP="\"$${smap}\"" \
-c common/system_map.c -o $(obj)common/system_map.o
$(GEN_UBOOT) $(obj)common/system_map.o
endif

u-boot依赖一堆,它也是uboot构建主要要做的事情。从这里我们可以知道uboot的构建就是先构建u-boot,然后再生成u-boot.bin。下面继续看u-boot的每一个依赖吧

第一个依赖depend

depend dep: $(TIMESTAMP_FILE) $(VERSION_FILE) \
$(obj)include/spl-autoconf.mk \
$(obj)include/tpl-autoconf.mk \
$(obj)include/autoconf.mk \
$(obj)include/generated/generic-asm-offsets.h \
$(obj)include/generated/asm-offsets.h
for dir in $(SUBDIRS) $(CPUDIR) $(LDSCRIPT_MAKEFILE_DIR) ; do \
$(MAKE) -C $$dir _depend ; done

忽略分析它的依赖吧,大部分在执行第二步配置的时候生成的。还一些文件是用于辅佐构建的。直接看命令部分,其中

ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(TOPDIR)/arch/$(ARCH)/cpu/u-boot.lds
# We don't expect a Makefile here
LDSCRIPT_MAKEFILE_DIR =
endif
LDSCRIPT_MAKEFILE_DIR = $(dir $(LDSCRIPT)) SUBDIRS = $(SUBDIR_TOOLS)
ifndef CONFIG_SANDBOX
SUBDIRS += $(SUBDIR_EXAMPLES)
endif CPUDIR=arch/$(ARCH)/cpu/$(CPU)

因此,depend会去这些目录(也就是目录tools [examples/standalone、examples/api] arch/arm/cpu/armv7/)执行_depend目标

第二个依赖SUBDIR_TOOLS

SUBDIR_TOOLS = tools
SUBDIR_EXAMPLES = examples/standalone examples/api
SUBDIRS = $(SUBDIR_TOOLS)
ifndef CONFIG_SANDBOX
SUBDIRS += $(SUBDIR_EXAMPLES)
endif
$(SUBDIRS): depend
$(MAKE) -C $@ all

去tools目录下执行all目标。之所以要这么早构建tools下的程序,是因为后面的构建过程会用到它们。

第三个依赖OBJS

$(OBJS):    depend
$(MAKE) -C $(CPUDIR) $(if $(REMOTE_BUILD),$@,$(notdir $@))

开始执行OBJS里面指定的文件了

OBJS  = $(CPUDIR)/start.o
ifeq ($(CPU),ppc4xx)
OBJS += $(CPUDIR)/resetvec.o
endif
ifeq ($(CPU),mpc85xx)
OBJS += $(CPUDIR)/resetvec.o
endif OBJS := $(addprefix $(obj),$(OBJS))

其实就是构建arch/arm/cpu/armv7/start.o(当然是根据arch/arm/cpu/armv7/start.S啦)

第四个依赖LIBBOARD

$(LIBBOARD):    depend $(LIBS)
$(MAKE) -C $(dir $(subst $(obj),,$@))

开始执行LIBBOARD里指定的文件了。注意,它依赖LIBS,也就是说第五个依赖会先执行。

BOARD  = zynq
VENDOR = xilinx ifdef VENDOR
BOARDDIR = $(VENDOR)/$(BOARD)
else
BOARDDIR = $(BOARD)
endif LIBBOARD = board/$(BOARDDIR)/lib$(BOARD).o
LIBBOARD := $(addprefix $(obj),$(LIBBOARD))

最终就是构建board/$(BOARDDIR)/下的默认目标,我这里就是board/xilinx/zynq/Makefile,对应的它里面的规则:

LIB = $(obj)lib$(BOARD).o                                                       

COBJS-y := board.o                                                              

# Added by MYIR for MYS-XC7Z010
COBJS-y += myir_init.o COBJS := $(sort $(COBJS-y)) SRCS := $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS)) $(LIB): $(obj).depend $(OBJS)
$(call cmd_link_o_target, $(OBJS))

第五个依赖LIBS

$(LIBS):    depend $(SUBDIR_TOOLS)
$(MAKE) -C $(dir $(subst $(obj),,$@))

开始执行LIBS里指定的文件了,到这一步,它的依赖在前面都执行完了,直接看LIBS有哪些吧!

LIBS-y += lib/libgeneric.o
LIBS-y += lib/rsa/librsa.o
LIBS-y += lib/lzma/liblzma.o
LIBS-y += lib/lzo/liblzo.o
LIBS-y += lib/zlib/libz.o
LIBS-$(CONFIG_TIZEN) += lib/tizen/libtizen.o
LIBS-$(HAVE_VENDOR_COMMON_LIB) += board/$(VENDOR)/common/lib$(VENDOR).o
LIBS-y += $(CPUDIR)/lib$(CPU).o
ifdef SOC
LIBS-y += $(CPUDIR)/$(SOC)/lib$(SOC).o
endif
ifeq ($(CPU),ixp)
LIBS-y += drivers/net/npe/libnpe.o
endif
LIBS-$(CONFIG_OF_EMBED) += dts/libdts.o
LIBS-y += arch/$(ARCH)/lib/lib$(ARCH).o
LIBS-y += fs/libfs.o \
fs/cbfs/libcbfs.o \
fs/cramfs/libcramfs.o \
fs/ext4/libext4fs.o \
fs/fat/libfat.o \
fs/fdos/libfdos.o \
fs/jffs2/libjffs2.o \
fs/reiserfs/libreiserfs.o \
fs/sandbox/libsandboxfs.o \
fs/ubifs/libubifs.o \
fs/yaffs2/libyaffs2.o \
fs/zfs/libzfs.o
LIBS-y += net/libnet.o
LIBS-y += disk/libdisk.o
LIBS-y += drivers/bios_emulator/libatibiosemu.o
LIBS-y += drivers/block/libblock.o
LIBS-$(CONFIG_BOOTCOUNT_LIMIT) += drivers/bootcount/libbootcount.o
LIBS-y += drivers/crypto/libcrypto.o
LIBS-y += drivers/dma/libdma.o
LIBS-y += drivers/fpga/libfpga.o
LIBS-y += drivers/gpio/libgpio.o
LIBS-y += drivers/hwmon/libhwmon.o
LIBS-y += drivers/i2c/libi2c.o
LIBS-y += drivers/input/libinput.o
LIBS-y += drivers/misc/libmisc.o
LIBS-y += drivers/mmc/libmmc.o
LIBS-y += drivers/mtd/libmtd.o
LIBS-y += drivers/mtd/nand/libnand.o
LIBS-y += drivers/mtd/onenand/libonenand.o
LIBS-y += drivers/mtd/ubi/libubi.o
LIBS-y += drivers/mtd/spi/libspi_flash.o
LIBS-y += drivers/net/libnet.o
LIBS-y += drivers/net/phy/libphy.o
LIBS-y += drivers/pci/libpci.o
LIBS-y += drivers/pcmcia/libpcmcia.o
LIBS-y += drivers/power/libpower.o \
drivers/power/fuel_gauge/libfuel_gauge.o \
drivers/power/mfd/libmfd.o \
drivers/power/pmic/libpmic.o \
drivers/power/battery/libbattery.o
LIBS-y += drivers/spi/libspi.o
LIBS-y += drivers/dfu/libdfu.o
ifeq ($(CPU),mpc83xx)
LIBS-y += drivers/qe/libqe.o
LIBS-y += arch/powerpc/cpu/mpc8xxx/ddr/libddr.o
LIBS-y += arch/powerpc/cpu/mpc8xxx/lib8xxx.o
endif
ifeq ($(CPU),mpc85xx)
LIBS-y += drivers/qe/libqe.o
LIBS-y += drivers/net/fm/libfm.o
LIBS-y += arch/powerpc/cpu/mpc8xxx/ddr/libddr.o
LIBS-y += arch/powerpc/cpu/mpc8xxx/lib8xxx.o
endif
ifeq ($(CPU),mpc86xx)
LIBS-y += arch/powerpc/cpu/mpc8xxx/ddr/libddr.o
LIBS-y += arch/powerpc/cpu/mpc8xxx/lib8xxx.o
endif
LIBS-y += drivers/rtc/librtc.o
LIBS-y += drivers/serial/libserial.o
LIBS-y += drivers/sound/libsound.o
LIBS-y += drivers/tpm/libtpm.o
LIBS-y += drivers/twserial/libtws.o
LIBS-y += drivers/usb/eth/libusb_eth.o
LIBS-y += drivers/usb/gadget/libusb_gadget.o
LIBS-y += drivers/usb/host/libusb_host.o
LIBS-y += drivers/usb/musb/libusb_musb.o
LIBS-y += drivers/usb/musb-new/libusb_musb-new.o
LIBS-y += drivers/usb/phy/libusb_phy.o
LIBS-y += drivers/usb/ulpi/libusb_ulpi.o
LIBS-y += drivers/video/libvideo.o
LIBS-y += drivers/watchdog/libwatchdog.o
LIBS-y += common/libcommon.o
LIBS-y += lib/libfdt/libfdt.o
LIBS-y += api/libapi.o
LIBS-y += post/libpost.o
LIBS-y += test/libtest.o ifneq ($(CONFIG_OMAP_COMMON),)
LIBS-y += $(CPUDIR)/omap-common/libomap-common.o
endif ifneq (,$(filter $(SOC), mx25 mx27 mx5 mx6 mx31 mx35 mxs vf610))
LIBS-y += arch/$(ARCH)/imx-common/libimx-common.o
endif ifeq ($(SOC),s5pc1xx)
LIBS-y += $(CPUDIR)/s5p-common/libs5p-common.o
endif
ifeq ($(SOC),exynos)
LIBS-y += $(CPUDIR)/s5p-common/libs5p-common.o
endif
ifneq ($(CONFIG_TEGRA),)
LIBS-y += arch/$(ARCH)/cpu/$(SOC)-common/lib$(SOC)-common.o
LIBS-y += arch/$(ARCH)/cpu/tegra-common/libcputegra-common.o
LIBS-y += $(CPUDIR)/tegra-common/libtegra-common.o
endif LIBS := $(addprefix $(obj),$(sort $(LIBS-y)))
.PHONY : $(LIBS)

看到它,大概也猜到了,大部分文件的编译都在这。

第六个依赖LDSCRIPT

$(LDSCRIPT):    depend
$(MAKE) -C $(dir $@) $(notdir $@)

LDSCRIPT在寻找链接脚本时,我这里的LDSCRIPT已经假设为arch/$(ARCH)/cpu/u-boot.lds了(即arch/arm/cpu/u-boot.lds)。它的命令就是去arch/arm/cpu下执行u-boot.lds目标啦,但是我发现那目录下根本没有makefile!

第七个依赖u-boot.lds

$(obj)u-boot.lds: $(LDSCRIPT)
$(CPP) $(CPPFLAGS) $(LDPPFLAGS) -ansi -D__ASSEMBLY__ -P - <$< >$@

对应我这里的命令:

arm-xilinx-linux-gnueabi-gcc -E -g  -Os   -ffunction-sections -fdata-sections -fno-common -ffixed-r8 -msoft-float  -fno-strict-aliasing -mno-unaligned-access -D__KERNEL__ -DCONFIG_SYS_TEXT_BASE=0x04000000 -I/home/rongp/company/zynq/Bootloader/u-boot-xlnx/include -fno-builtin -ffreestanding -nostdinc -isystem /home/rongp/company/zynq/Toolchain/CodeSourcery/Sourcery_CodeBench_Lite_for_Xilinx_GNU_Linux/bin/../lib/gcc/arm-xilinx-linux-gnueabi/4.6.1/include -pipe  -DCONFIG_ARM -D__ARM__ -marm -mno-thumb-interwork -mabi=aapcs-linux -mword-relocations -march=armv7-a -include /home/rongp/company/zynq/Bootloader/u-boot-xlnx/include/u-boot/u-boot.lds.h -DCPUDIR=arch/arm/cpu/armv7  -ansi -D__ASSEMBLY__ -P - </home/rongp/company/zynq/Bootloader/u-boot-xlnx/arch/arm/cpu/u-boot.lds >u-boot.lds

分析完依赖

$(obj)u-boot:   depend \
$(SUBDIR_TOOLS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT) $(obj)u-boot.lds
$(GEN_UBOOT)

,下面继续看u-boot对应的命令部分

        $(GEN_UBOOT)
ifeq ($(CONFIG_KALLSYMS),y) #这部分部分析,忽略
smap=`$(call SYSTEM_MAP,$(obj)u-boot) | \
awk '$$2 ~ /[tTwW]/ {printf $$1 $$3 "\\\\000"}'` ; \
$(CC) $(CFLAGS) -DSYSTEM_MAP="\"$${smap}\"" \
-c common/system_map.c -o $(obj)common/system_map.o
$(GEN_UBOOT) $(obj)common/system_map.o
endif

其中GEN_UBOOT定义如下:

ifeq ($(CONFIG_SANDBOX),y)
GEN_UBOOT = \
cd $(LNDIR) && $(CC) $(SYMS) -T $(obj)u-boot.lds \
-Wl,--start-group $(__LIBS) -Wl,--end-group \
$(PLATFORM_LIBS) -Wl,-Map -Wl,u-boot.map -o u-boot
else
GEN_UBOOT = \
cd $(LNDIR) && $(LD) $(LDFLAGS) $(LDFLAGS_$(@F)) \
$(__OBJS) \
--start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
-Map u-boot.map -o u-boot
endif

对应我这里的命令:

cd /home/rongp/company/zynq/Bootloader/u-boot-xlnx && arm-xilinx-linux-gnueabi-ld  -pie -T u-boot.lds --gc-sections -Bstatic -Ttext 0x04000000 arch/arm/cpu/armv7/start.o --start-group api/libapi.o arch/arm/cpu/armv7/libarmv7.o arch/arm/cpu/armv7/zynq/libzynq.o arch/arm/lib/libarm.o common/libcommon.o disk/libdisk.o drivers/bios_emulator/libatibiosemu.o drivers/block/libblock.o drivers/crypto/libcrypto.o drivers/dfu/libdfu.o drivers/dma/libdma.o drivers/fpga/libfpga.o drivers/gpio/libgpio.o drivers/hwmon/libhwmon.o drivers/i2c/libi2c.o drivers/input/libinput.o drivers/misc/libmisc.o drivers/mmc/libmmc.o drivers/mtd/libmtd.o drivers/mtd/nand/libnand.o drivers/mtd/onenand/libonenand.o drivers/mtd/spi/libspi_flash.o drivers/mtd/ubi/libubi.o drivers/net/libnet.o drivers/net/phy/libphy.o drivers/pci/libpci.o drivers/pcmcia/libpcmcia.o drivers/power/battery/libbattery.o drivers/power/fuel_gauge/libfuel_gauge.o drivers/power/libpower.o drivers/power/mfd/libmfd.o drivers/power/pmic/libpmic.o drivers/rtc/librtc.o drivers/serial/libserial.o drivers/sound/libsound.o drivers/spi/libspi.o drivers/tpm/libtpm.o drivers/twserial/libtws.o drivers/usb/eth/libusb_eth.o drivers/usb/gadget/libusb_gadget.o drivers/usb/host/libusb_host.o drivers/usb/musb-new/libusb_musb-new.o drivers/usb/musb/libusb_musb.o drivers/usb/phy/libusb_phy.o drivers/usb/ulpi/libusb_ulpi.o drivers/video/libvideo.o drivers/watchdog/libwatchdog.o fs/cbfs/libcbfs.o fs/cramfs/libcramfs.o fs/ext4/libext4fs.o fs/fat/libfat.o fs/fdos/libfdos.o fs/jffs2/libjffs2.o fs/libfs.o fs/reiserfs/libreiserfs.o fs/sandbox/libsandboxfs.o fs/ubifs/libubifs.o fs/yaffs2/libyaffs2.o fs/zfs/libzfs.o lib/libfdt/libfdt.o lib/libgeneric.o lib/lzma/liblzma.o lib/lzo/liblzo.o lib/rsa/librsa.o lib/zlib/libz.o net/libnet.o post/libpost.o test/libtest.o board/xilinx/zynq/libzynq.o --end-group /home/rongp/company/zynq/Bootloader/u-boot-xlnx/arch/arm/lib/eabi_compat.o  -L /home/rongp/company/zynq/Toolchain/CodeSourcery/Sourcery_CodeBench_Lite_for_Xilinx_GNU_Linux/bin/../lib/gcc/arm-xilinx-linux-gnueabi/4.6.1 -lgcc -Map u-boot.map -o u-boot

u-boot是ELF格式二进制的image文件,u-boot.bin是原始的二进制image文件。构建完u-boot,接着就是构建uboot.bin了:

$(obj)u-boot.bin:   $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
$(BOARD_SIZE_CHECK)

对应我这里的命令:

arm-xilinx-linux-gnueabi-objcopy --gap-fill=0xff -O binary u-boot u-boot.bin

all目标还会构建SUBDIR_EXAMPLES,它对应的规则:

ifndef CONFIG_SANDBOX
SUBDIRS += $(SUBDIR_EXAMPLES)
endif $(SUBDIRS): depend
$(MAKE) -C $@ all

链接脚本分析

最后再分析下链接脚本吧!先贴出我这里使用到的arch/arm/cpu/u-boot.lds(说明直接嵌入到脚本中):

/*
* Copyright (c) 2004-2008 Texas Instruments
*
* (C) Copyright 2002
* Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
*
* SPDX-License-Identifier: GPL-2.0+
*/ OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")//三个分别指定在缺省、大端、小端情况下的输出可执行文件格式,这里都指定输出格式是elf32,小端和arm体系结构
OUTPUT_ARCH(arm)//输出可执行文件指定为arm体系结构
ENTRY(_start)//指定_start函数为程序的入口。_start在每个CPU目录下的start.S中定义,真正的启动运行地址段由CONFIG_SYS_TEXT_BASE宏定义在编译时由config.mk中定义
SECTIONS //每个链接脚本都会有一个SECTIONS,它里面会有很多section用于描述每个段最终存放到输出文件的位置。The SECTIONS command tells the linker how to map input sections into output sections, and how to place the output sections in memory.
{
. = 0x00000000;//指定定位器符号为0,定系统启动从偏移地址零处开始。
//注意这只是个代码地址偏移值,真正的起始地址是由编译时指定的LDFLAGS_XXX指定的 . = ALIGN(4);//4字节对齐
.text ://这里指定所有文件的.__image_copy_start以及CPUDIR/start.o的.text*以及所有其他文件的.text*都放到.text段
{
*(.__image_copy_start)//u-boot将自己copy到RAM,此为需要copy的程序的start,参考arch/arm/lib/sections.c和arch/arm/lib/relocate.S
CPUDIR/start.o (.text*)//对应到我这里,就是arch/arm/cpu/armv7/start.o文件啦
*(.text*)//其他所有文件的.text*
} . = ALIGN(4);//修改当前定位寄存器,保持4字节对齐
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }//指定所有文件的.rodata*放到.rodata段,
//放置前先用SORT_BY_NAME排序,然后用SORT_BY_ALIGNMENT做排序,SORT_BY_ALIGNMENT会根据对齐的
//特性进行降序排序,The difference is SORT_BY_ALIGNMENT will sort sections into descending order by alignment
//before placing them in the output file. Larger alignments are placed before
//smaller alignments in order to reduce the amount of padding necessary.
//在代码段之后,存放read only数据段 . = ALIGN(4);//修改当前定位寄存器,保持4字节对齐
.data : {//所有文件的.data*放到.data段
*(.data*)
} . = ALIGN(4);//修改当前定位寄存器,保持4字节对齐 . = .;//??? . = ALIGN(4);//修改当前定位寄存器,保持4字节对齐
.u_boot_list : {//所有文件的.u_boot_list*放到.u_boot_list段,且先通过SORT对
//所有.u_boot_list*排序然后通过KEEP告诉连接器,这个段很重要,不要优化掉了哈。
//.data段结束后,紧接着存放u-boot自有的一些function,例如u-boot command等
KEEP(*(SORT(.u_boot_list*)));
} . = ALIGN(4);//修改当前定位寄存器,保持4字节对齐 .image_copy_end ://同__image_copy_start,参考arch/arm/lib/sections.c和
//arch/arm/lib/relocate.S,里面有注解。
//至此,u-boot需要自拷贝的内容结束,总结一下,包括代码段,数据段,以及u_boot_list
{
*(.__image_copy_end)
} .rel_dyn_start ://用于用于动态连接的重定位信息的start,参考arch/arm/lib/sections.c ,里面有注解。
//在老的uboot中,如果我们想要uboot启动后把自己拷贝到内存中的某个地方,只要把要拷贝的地址写给TEXT_BASE即可,
//然后boot启动后就会把自己拷贝到TEXT_BASE内的地址处运行,在拷贝之前的代码都是相对的,不能出现绝对的跳转,否则会跑飞。
//在新版的uboot里(2013.07),TEXT_BASE的含义改变了。它表示用户要把这段代码加载到哪里,通常是通过串口等工具。
//然后搬移的时候由uboot自己计算一个地址来进行搬移。
//新版的uboot采用了动态链接技术,在lds文件中有__rel_dyn_start和__rel_dyn_end,这两个符号之间
//的区域存放着动态链接符号,只要给这里面的符号加上一定的偏移,拷贝到内存中代码的后面相应的
//位置处,就可以在绝对跳转中找到正确的函数。*/ {
*(.__rel_dyn_start)
} .rel.dyn : {//动态链接符存放在的段
*(.rel*)
} .rel_dyn_end ://动态链接符段结束,参考arch/arm/lib/sections.c ,里面有注解
{
*(.__rel_dyn_end)
} _end = .;//设置_end的地址为当前定位器,board_f.c里会使用它来计算 /*
* Deprecated: this MMU section is used by pxa at present but
* should not be used by new boards/CPUs.
*/
. = ALIGN(4096);
.mmutable : {
*(.mmutable)
} /*
* Compiler-generated __bss_start and __bss_end, see arch/arm/lib/bss.c
* __bss_base and __bss_limit are for linker only (overlay ordering)
*/ .bss_start __rel_dyn_start (OVERLAY) : {//参考arch/arm/lib/sections.c ,里面有注解
KEEP(*(.__bss_start));//
__bss_base = .;
} .bss __bss_base (OVERLAY) : {//未初始化段,存放程序中未初始化的全局变量和静态变量的一块内存区域
*(.bss*)
. = ALIGN(4);
__bss_limit = .;
} .bss_end __bss_limit (OVERLAY) : {//参考arch/arm/lib/sections.c ,里面有注解
KEEP(*(.__bss_end));
} /DISCARD/ : { *(.dynsym) }
/DISCARD/ : { *(.dynstr*) }
/DISCARD/ : { *(.dynamic*) }
/DISCARD/ : { *(.plt*) }
/DISCARD/ : { *(.interp*) }
/DISCARD/ : { *(.gnu*) }
/DISCARD/ : { *(.ARM.exidx*) }
/DISCARD/ : { *(.gnu.linkonce.armexidx.*) }
}

在config.mk中:

ifneq ($(CONFIG_SYS_TEXT_BASE),)
LDFLAGS_u-boot += -Ttext $(CONFIG_SYS_TEXT_BASE)
endif

在生成elf可执行文件u-boot的命令中就指定了链接标志LDFLAGS

ifeq ($(CONFIG_SANDBOX),y)
GEN_UBOOT = \
cd $(LNDIR) && $(CC) $(SYMS) -T $(obj)u-boot.lds \
-Wl,--start-group $(__LIBS) -Wl,--end-group \
$(PLATFORM_LIBS) -Wl,-Map -Wl,u-boot.map -o u-boot
else
GEN_UBOOT = \
cd $(LNDIR) && $(LD) $(LDFLAGS) $(LDFLAGS_$(@F)) \
$(__OBJS) \
--start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
-Map u-boot.map -o u-boot
endif

这说明只需要定义CONFIG_SYS_TEXT_BASE,编译的时候就会去设置text段的base地址。至于在哪里设置,相信大家可以猜到了,当然是板级相关的头文件啦,并且在执行第二步配置的时候会被处理,放到config.mk里去。另外需要说明一下,$(@F)表示"$@"的文件部分,如果"$@"值是"dir/foo.o",那么"$(@F)"就是"foo.o","$(@F)"相当于函数"$(notdir $@)",对应到这里,

$(LDFLAGS_$(@F))就是LDFLAGS_u-boot啦,因为我们的目标为$(obj)u-boot

另外,关于OVERLAY。每一个输出节可以有一个类型。类型是一个放在括号中的关键字,已定义的类型如下所示:

NOLOAD' 这个节应当被标式讵不可载入,所以当程序运行时,它不会被载入到内存中。 DSECT'

COPY' INFO'

`OVERLAY'

支持这些类型名只是为了向下兼容,它们很少使用。它们都具有相同的效果:这个节应当被标式讵不可分配,所以当程序运行时,没有内存为这个节分配。

补充1:

标准应用程序包括 3 类标准段空间:.text 运行代段;.data 全局变量等具有初始值的数据空间;.bss暂态变量,堆栈等数据空间;

补充2:

.rodata,.u_boot_list.image_copy_end 等段空间由程序员设计需要而自行定义的段空间;

总的来说,uboot的链接脚本还是很简单的。

总结

  构建u-boot.bin,会先在tools [examples/standalone、examples/api] arch/arm/cpu/armv7/目录下构建_depend目标,然后去tools下构建all目标,再然后构建arch/arm/cpu/armv7/start.o,然后执行LIBS构建(LIBS由lib/ fs/ drivers/ common/ api/ post/ test/ arch/arm/cpu/armv7/ arch/arm/cpu/armv7/zynq/ arch/arm/lib/ disk/ net/下的部分文件构成),然后构建board/xilinx/zynq/Makefile下的默认目标,以及$(obj)u-boot.lds,最后基于以上构建的来构建u-boot以及u-boot.bin。当然,不同的板子,构建过程多少会有些差异,不过大体思想是一样的。本文如果有错误的地方,欢迎大家指出!

完!

2016年7月

uboot makefile构建分析-续的更多相关文章

  1. uboot makefile构建分析

    前言 几年前分析过uboot的构建及启动过程,做了笔记,但最终没有转为文章.这次又有机会开发嵌入式产品了(之前一年多都是在搞x86 linux),看了下uboot的构建过程,觉得有必要写下整个分析过程 ...

  2. uboot 顶层makefile细节分析

    uboot的源文件众多,学习庞然大物首先找到脊椎--顶层的makfile,逐一破解.但是,uboot的makefile同样是一个庞然大物,所以也要找到它的主线.倘若过分专注部分细节,很难做到把握全局, ...

  3. U-Boot Makefile分析(5)主控Makefile分析

    这次分析源码根目录下的Makefile,它负责读入配置过的信息,通过OBJS.LIBS等变量设置能够参与镜像链接的目标文件,设定编译的目标等等. HOSTARCH := $(shell uname - ...

  4. linux kernel make构建分析

    前言 之前对uboot的构建进行了分析,现在再对linux kernel的构建进行分析.几年前的确也分析过,但是只是停留在笔记层面,没有转为文章,这次下定决定来完善它. 环境 同样,采用的还是zynq ...

  5. openwrt: Makefile 框架分析

    openwrt: Makefile 框架分析 原文链接:blog.chinaunix.net/uid-26675482-id-4704952.html 本篇的主要目的是想通过分析Makefile,了解 ...

  6. u-boot 之配置分析 (2)

    Makefile简要分析所有这些目录的编译连接都是由顶层目录的makefile来确定的. 1.在makefile中有: unconfig: @rm -f $(obj)include/config.h ...

  7. u-boot启动流程分析(2)_板级(board)部分

    转自:http://www.wowotech.net/u-boot/boot_flow_2.html 目录: 1. 前言 2. Generic Board 3. _main 4. global dat ...

  8. u-boot Makefile整体解析

    一.概述   1.理解u-boot的makefile需要的准备 linux常用命令.shell脚本基础知识.makefile脚本基础知识 2.Makefile的元素 万变不离其宗,无论工程多么复杂,文 ...

  9. [国嵌笔记][030][U-Boot工作流程分析]

    uboot工作流程分析 程序入口 1.打开顶层目录的Makefile,找到目标smdk2440_config的命令中的第三项(smdk2440) 2.进入目录board/samsung/smdk244 ...

随机推荐

  1. 一个Vue实例-添加、显示列表、删除

    <link href="~/Content/css/bootstrap-theme.min.css" rel="stylesheet" /> < ...

  2. EM算法【转】

    混合高斯模型和EM算法 这篇讨论使用期望最大化算法(Expectation-Maximization)来进行密度估计(density estimation). 与K-means一样,给定的训练样本是, ...

  3. ssh-keygen的使用方法及配置authorized_keys两台linux机器相互认证

    一.概述 1.就是为了让两个linux机器之间使用ssh不需要用户名和密码.采用了数字签名RSA或者DSA来完成这个操作 2.模型分析 假设 A (192.168.20.59)为客户机器,B(192. ...

  4. P2610 [ZJOI2012]旅游

    题目描述 到了难得的暑假,为了庆祝小白在数学考试中取得的优异成绩,小蓝决定带小白出去旅游~~ 经过一番抉择,两人决定将T国作为他们的目的地.T国的国土可以用一个凸N边形来表示,N个顶点表示N个入境/出 ...

  5. 测试开发面试的Linux面试题总结之二:常用命令

    (1)Linux的文件系统目录配置要遵循FHS规范,规范定义的两级目录规范如下:        /home  每个账号在该目录下都有一个文件夹,进行数据的管理        /usr 有点像windo ...

  6. POJ.2528 Mayor's posters (线段树 区间更新 区间查询 离散化)

    POJ.2528 Mayor's posters (线段树 区间更新 区间查询 离散化) 题意分析 贴海报,新的海报能覆盖在旧的海报上面,最后贴完了,求问能看见几张海报. 最多有10000张海报,海报 ...

  7. Python精要参考(第二版)

    ython 精要参考(第二版) 是Python语言初学者不错的参考学习用书,本系列译自Python Essential Reference, Second Edition 希望本系列可以给python ...

  8. Linux(六)shell操作实用技巧

    一.shell操作日期时间 linux 系统为我们提供了一个命令 date,专门用来显示或者设置系统日期时间的.      语法格式为:      date [OPTION]... [+FORMAT] ...

  9. 解题:POI 2015 Kinoman

    题面 发现每种电影只在两场之间产生贡献(只有$pos$的一场的就在$[pos,n]$产生贡献).那么我们针对每个位置$i$求出这场电影下一次出现的位置$nxt[i]$,然后每次更新一下,求整个区间的最 ...

  10. HDU 4372 Count the Buildings——第一类斯特林数

    题目大意:n幢楼,从左边能看见f幢楼,右边能看见b幢楼 楼高是1~n的排列. 问楼的可能情况 把握看到楼的本质! 最高的一定能看见! 计数问题要向组合数学或者dp靠拢.但是这个题询问又很多,难以dp ...