lx2160 LSDK21.08 firmware 笔记 - 0.基于fip.bin 编译流程展开的 makefile 分析

0.基于fip.bin 编译流程展开的 makefile 分析

前言

芯片所有的软硬件资源都能够在 NXP 官网找到,本文档也是对 NXP 开源 LSDK 代码工程的学习与分析。

官网链接如下:
LSDK 软件资料
LX2160 芯片资料

fip 编译命令

根据 LSDK 用户指导文档,编译命令(不带 optee)如下:

实际编译命令为:

shell 复制代码
make ARCH=aarch64 CROSS_COMPILE=aarch64-linux-gnu-  PLAT=lx2160ardb  BL33=<path_to_u-boot_binary>/u-boot.bin fip

分析编译命令:

  1. PLAT 变量:指定代码运行平台
  2. BL33变量:指定制作 fip.bin 的 BL33镜像,为 uboot.bin 的路径
  3. 指定了编译目标为: fip

fip 编译目标的 makefile 分析

分析顶层 Makefile( atf 工程根目录下的 Makefile), 查找 fip 目标:

fip 目标通过 1283 行的规则实现:

shell 复制代码
fip: ${BUILD_PLAT}/${FIP_NAME}

该目标依赖 ${BUILD_PLAT}/${FIP_NAME}, 继续查找 ${BUILD_PLAT}/${FIP_NAME} 目标:

${BUILD_PLAT}/${FIP_NAME} 通过如下规则实现:

shell 复制代码
${BUILD_PLAT}/${FIP_NAME}: ${FIP_DEPS} ${FIPTOOL}
	${Q}${FIPTOOL} create ${FIP_ARGS} $@
	${Q}${FIPTOOL} info $@
	@${ECHO_BLANK_LINE}
	@echo "Built $@ successfully"
	@${ECHO_BLANK_LINE}

分析上面的规则定义,发现目标依赖 ${FIP_DEPS}${FIPTOOL},命令是调用 ${FIPTOOL} 生成 fip.bin 文件。

根据 make log 分析,bl31.bin 的生成只可能通过 FIP_DEPS 中包含,因为 ${FIPTOOL}只是 FIP 文件生成工具。

FIP_DEPS 变量如何包含 bl31.bin

(该部分需要通读 atf 工程 makefile 框架)

上图中,使用下面的命令将 bl31.bin 包含在 fip 镜像的生成过程中(FIP_DEPS 变量):

shell 复制代码
ifeq (${NEED_BL31},yes)
BL31_SOURCES += ${SPD_SOURCES}
# Sort BL31 source files to remove duplicates
BL31_SOURCES := $(sort ${BL31_SOURCES})
ifneq (${DECRYPTION_SUPPORT},none)
$(if ${BL31}, $(eval $(call TOOL_ADD_IMG,bl31,--soc-fw,,$(ENCRYPT_BL31))),\
	$(eval $(call MAKE_BL,31,soc-fw,,$(ENCRYPT_BL31))))
else
# 下面的命令执行 $(eval $(call MAKE_BL,31,soc-fw),由于 第二个参数为 soc-fw, 会将 BL31 阶段镜像添加到 fip 的镜像生成中
$(if ${BL31}, $(eval $(call TOOL_ADD_IMG,bl31,--soc-fw)),\
	$(eval $(call MAKE_BL,31,soc-fw)))
endif
endif

通读 makefile,变量 NEED_BL31 总是会被设置为 yes(也可以通过命令选择不包含,哈哈,老老实实看makefile文件吧)。bl31.bin 通过 (eval (call MAKE_BL,31,soc-fw)) 命令包含在FIP_DEPS 变量中。

MAKE_BL makefile 宏函数实现分析(TODO:最重要的,请结合源码)

MAKE_BL 宏函数的作用是定义 blx(x可以为1,2,31)编译目标及其规则,并判断是否需要将指定 blx 目标对应的镜像包含在 fip 镜像中。

该宏函数定义在 atf\make_helpers\build_macros.mk 文件中:

具体实现命令如下:

shell 复制代码
# 定义 BL2 目标与现象, 通过 OBJS 变量指定编译的源文件与目标文件 BL2_SOURCES or BL31 SOURCES, BL_COMMON_SOURCES,PLAT_BL_COMMON_SOURCES
# MAKE_BL macro defines the targets and options to build each BL image.
# Arguments:
#   $(1) = BL stage
#   $(2) = FIP command line option (if empty, image will not be included in the FIP)
#   $(3) = FIP prefix (optional) (if FWU_, target is fwu_fip instead of fip)
#   $(4) = BL encryption flag (optional) (0, 1)
define MAKE_BL
        $(eval BUILD_DIR  := ${BUILD_PLAT}/$(1))
        $(eval BL_SOURCES := $($(call uppercase,$(1))_SOURCES))
        $(eval SOURCES    := $(sort $(BL_SOURCES) $(BL_COMMON_SOURCES) $(PLAT_BL_COMMON_SOURCES)))
        $(eval OBJS       := $(addprefix $(BUILD_DIR)/,$(call SOURCES_TO_OBJS,$(SOURCES))))
        $(eval MAPFILE    := $(call IMG_MAPFILE,$(1)))
        $(eval ELF        := $(call IMG_ELF,$(1)))
        $(eval DUMP       := $(call IMG_DUMP,$(1)))
        $(eval BIN        := $(call IMG_BIN,$(1)))
        $(eval ENC_BIN    := $(call IMG_ENC_BIN,$(1)))
        $(eval BL_LIBS    := $($(call uppercase,$(1))_LIBS))

        $(eval DEFAULT_LINKER_SCRIPT_SOURCE := $($(call uppercase,$(1))_DEFAULT_LINKER_SCRIPT_SOURCE))
        $(eval DEFAULT_LINKER_SCRIPT := $(call linker_script_path,$(DEFAULT_LINKER_SCRIPT_SOURCE)))

        # BL2 与 BL31 只使用了默认链接脚本,下面的变量为自定义变量,指向自定义连接脚本
        $(eval LINKER_SCRIPT_SOURCES := $($(call uppercase,$(1))_LINKER_SCRIPT_SOURCES))
        $(eval LINKER_SCRIPTS := $(call linker_script_path,$(LINKER_SCRIPT_SOURCES)))

        # We use sort only to get a list of unique object directory names.
        # ordering is not relevant but sort removes duplicates.
        $(eval TEMP_OBJ_DIRS := $(sort $(dir ${OBJS} ${DEFAULT_LINKER_SCRIPT} ${LINKER_SCRIPTS})))
        # The $(dir ) function leaves a trailing / on the directory names
        # Rip off the / to match directory names with make rule targets.
        $(eval OBJ_DIRS   := $(patsubst %/,%,$(TEMP_OBJ_DIRS)))

# Create generators for object directory structure
# 创建 build 目录
$(eval $(call MAKE_PREREQ_DIR,${BUILD_DIR},${BUILD_PLAT}))

$(eval $(foreach objd,${OBJ_DIRS},
        $(call MAKE_PREREQ_DIR,${objd},${BUILD_DIR})))

.PHONY : ${1}_dirs

# We use order-only prerequisites to ensure that directories are created,
# but do not cause re-builds every time a file is written.
${1}_dirs: | ${OBJ_DIRS}

# 将源文件编译生成目标文件
$(eval $(call MAKE_OBJS,$(BUILD_DIR),$(SOURCES),$(1)))

# Generate targets to preprocess each required linker script
# 生成链接脚本 ld 文件
$(eval $(foreach source,$(DEFAULT_LINKER_SCRIPT_SOURCE) $(LINKER_SCRIPT_SOURCES), \
        $(call MAKE_LD,$(call linker_script_path,$(source)),$(source),$(1))))

#获得连接标志位
$(eval BL_LDFLAGS := $($(call uppercase,$(1))_LDFLAGS))

ifeq ($(USE_ROMLIB),1)
$(ELF): romlib.bin
endif

# MODULE_OBJS can be assigned by vendors with different compiled
# object file path, and prebuilt object file path.
$(eval OBJS += $(MODULE_OBJS))

# ELF 编译规则
$(ELF): $(OBJS) $(DEFAULT_LINKER_SCRIPT) $(LINKER_SCRIPTS) | $(1)_dirs libraries $(BL_LIBS)
	$$(ECHO) "  LD      $$@"
ifdef MAKE_BUILD_STRINGS
	$(call MAKE_BUILD_STRINGS,$(BUILD_DIR)/build_message.o)
else
	@echo 'const char build_message[] = "Built : "$(BUILD_MESSAGE_TIMESTAMP); \
	       const char version_string[] = "${VERSION_STRING}"; \
	       const char version[] = "${VERSION}";' | \
		$$(CC) $$(TF_CFLAGS) $$(CFLAGS) -xc -c - -o $(BUILD_DIR)/build_message.o
endif
ifneq ($(findstring armlink,$(notdir $(LD))),)
	$$(Q)$$(LD) -o $$@ $$(TF_LDFLAGS) $$(LDFLAGS) $(BL_LDFLAGS) --entry=${1}_entrypoint \
		--predefine="-D__LINKER__=$(__LINKER__)" \
		--predefine="-DTF_CFLAGS=$(TF_CFLAGS)" \
		--map --list="$(MAPFILE)" --scatter=${PLAT_DIR}/scat/${1}.scat \
		$(LDPATHS) $(LIBWRAPPER) $(LDLIBS) $(BL_LIBS) \
		$(BUILD_DIR)/build_message.o $(OBJS)
else ifneq ($(findstring gcc,$(notdir $(LD))),)
	$$(Q)$$(LD) -o $$@ $$(TF_LDFLAGS) $$(LDFLAGS) -Wl,-Map=$(MAPFILE) \
		$(addprefix -Wl$(comma)--script$(comma),$(LINKER_SCRIPTS)) -Wl,--script,$(DEFAULT_LINKER_SCRIPT) \
		$(BUILD_DIR)/build_message.o \
		$(OBJS) $(LDPATHS) $(LIBWRAPPER) $(LDLIBS) $(BL_LIBS)
else
	$$(Q)$$(LD) -o $$@ $$(TF_LDFLAGS) $$(LDFLAGS) $(BL_LDFLAGS) -Map=$(MAPFILE) \
		$(addprefix -T ,$(LINKER_SCRIPTS)) --script $(DEFAULT_LINKER_SCRIPT) \
		$(BUILD_DIR)/build_message.o \
		$(OBJS) $(LDPATHS) $(LIBWRAPPER) $(LDLIBS) $(BL_LIBS)
endif

ifeq ($(DISABLE_BIN_GENERATION),1)
	@${ECHO_BLANK_LINE}
	@echo "Built $$@ successfully"
	@${ECHO_BLANK_LINE}
endif

$(DUMP): $(ELF)
	$${ECHO} "  OD      $$@"
	$${Q}$${OD} -dx $$< > $$@

$(BIN): $(ELF)
	$${ECHO} "  BIN     $$@"
	$$(Q)$$(OC) -O binary $$< $$@
	@${ECHO_BLANK_LINE}
	@echo "Built $$@ successfully"
	@${ECHO_BLANK_LINE}

#将 bl2 与 bl31 添加到全局目标中
.PHONY: $(1)
ifeq ($(DISABLE_BIN_GENERATION),1)
$(1): $(ELF) $(DUMP)
else
$(1): $(BIN) $(DUMP)
endif

all: $(1)

ifeq ($(4),1)
$(call ENCRYPT_FW,$(BIN),$(ENC_BIN))
$(if $(2),$(call TOOL_ADD_IMG_PAYLOAD,$(1),$(BIN),--$(2),$(ENC_BIN),$(3), \
		$(ENC_BIN)))
else
#bl31 规则生成时,会将 BIN(bl31.bin) 添加到 FIP_DEPS 变量中, 这样,在编译 fip.bin  时会编译 BIN 目标
#BIN 目标对应的规则在上面 $(BIN): $(ELF)
$(if $(2),$(call TOOL_ADD_IMG_PAYLOAD,$(1),$(BIN),--$(2),$(BIN),$(3)))
endif

endef

在上述的命令实现中:通过 .PHONY: bl$(1) 定义了 blx 目标,包括 bl2 与 bl31。

而 blx 目标的编译规则为:

shell 复制代码
bl$(1): $(BIN) $(DUMP)

$(BIN) 的编译规则为:

shell 复制代码
$(BIN): $(ELF)
	$${ECHO} "  BIN     $$@"
	$$(Q)$$(OC) -O binary $$< $$@
	@${ECHO_BLANK_LINE}
	@echo "Built $$@ successfully"
	@${ECHO_BLANK_LINE}

$(ELF) 的编译规则为(简化):

shell 复制代码
$(ELF): $(OBJS) $(LINKERFILE) | bl$(1)_dirs libraries $(BL_LIBS)
	$$(ECHO) "  LD      $$@"
	$$(Q)$$(LD) -o $$@ $$(TF_LDFLAGS) $$(LDFLAGS) $(BL_LDFLAGS) -Map=$(MAPFILE) \
	--script $(LINKERFILE) $(BUILD_DIR)/build_message.o \
	$(OBJS) $(LDPATHS) $(LIBWRAPPER) $(LDLIBS) $(BL_LIBS)

$(ELF) 的编译规则依赖下述的目标:

  • OBJS:所有指定的源文件.c 与.S
  • LINKERFILE: 链接脚本 ld 文件
  • bl$(1)_dirs:各目标文件和中间文件的输出目录
  • libraries、$(BL_LIBS):库文件

各变量具体的定义可查看 makefile 实现,通过上述的依赖目标与编译命令,blx.elf 可正常编译生成

MAKE_BL 将 BL31 镜像包含在 fip 镜像编译流程中

在 MAKE_BL 宏函数的实现中,存在下列命令:

shell 复制代码
ifeq ($(4),1)
$(call ENCRYPT_FW,$(BIN),$(ENC_BIN))
$(if $(2),$(call TOOL_ADD_IMG_PAYLOAD,bl$(1),$(BIN),--$(2),$(ENC_BIN),$(3), \
		$(ENC_BIN)))
else
# 实例:(eval $(call TOOL_ADD_IMG_PAYLOAD,bl31,$(BIN), --soc-fw, $(BIN),,)
$(if $(2),$(call TOOL_ADD_IMG_PAYLOAD,bl$(1),$(BIN),--$(2),$(BIN),$(3)))
endif

当执行$(eval $(call MAKE_BL,31,soc-fw))) 时, 上述命令会运行:

shell 复制代码
$(if $(2),$(call TOOL_ADD_IMG_PAYLOAD,bl$(1),$(BIN),--$(2),$(BIN),$(3)))
# 展开为 $(call TOOL_ADD_IMG_PAYLOAD,bl31,build/<platform>/release/bl31.bin, --soc-fw, build/<platform>/release/bl31.bin,,)

通过 TOOL_ADD_IMG_PAYLOAD 宏函数,可以实现将 bl31.bin 添加到 fip.bin 镜像的依赖中。

TOOL_ADD_IMG_PAYLOAD 宏函数实现

宏函数如下:

shell 复制代码
# TOOL_ADD_IMG_PAYLOAD works like TOOL_ADD_PAYLOAD, but applies image filters
# before passing them to host tools if BL*_PRE_TOOL_FILTER is defined.
#   $(1) = image_type (scp_bl2, bl33, etc.)
#   $(2) = payload filepath (ex. build/fvp/release/bl31.bin)
#   $(3) = command line option for the specified payload (ex. --soc-fw)
#   $(4) = tool target dependency (optional) (ex. build/fvp/release/bl31.bin)
#   $(5) = FIP prefix (optional) (if FWU_, target is fwu_fip instead of fip)
#   $(6) = encrypted payload (optional) (ex. build/fvp/release/bl31_enc.bin)

define TOOL_ADD_IMG_PAYLOAD

$(eval PRE_TOOL_FILTER := $($(call uppercase,$(1))_PRE_TOOL_FILTER))

ifneq ($(PRE_TOOL_FILTER),)

$(eval PROCESSED_PATH := $(BUILD_PLAT)/$(1).bin$($(PRE_TOOL_FILTER)_SUFFIX))

$(call $(PRE_TOOL_FILTER)_RULE,$(PROCESSED_PATH),$(2))

$(PROCESSED_PATH): $(4)

$(call TOOL_ADD_PAYLOAD,$(PROCESSED_PATH),$(3),$(PROCESSED_PATH),$(5),$(6))

else
#$(call TOOL_ADD_PAYLOAD,build/<platform>/release/bl31.bin,--soc-fw, build/<platform>/release/bl31.bin,,)
$(call TOOL_ADD_PAYLOAD,$(2),$(3),$(4),$(5),$(6))
endif
endef

简化后,会调用:

shell 复制代码
$(call TOOL_ADD_PAYLOAD,$(2),$(3),$(4),$(5),$(6))

根据 MAKE_EL 传入的参数,展开为:

shell 复制代码
$(call TOOL_ADD_PAYLOAD, build/<platform>/release/bl31.bin, --soc-fw, build/<platform>/release/bl31.bin,,,)

TOOL_ADD_PAYLOAD 宏函数实现

具体命令如下:

shell 复制代码
# TOOL_ADD_PAYLOAD appends the command line arguments required by fiptool to
# package a new payload and/or by cert_create to generate certificate.
# Optionally, it adds the dependency on this payload
#   $(1) = payload filename (i.e. bl31.bin)
#   $(2) = command line option for the specified payload (i.e. --soc-fw)
#   $(3) = tool target dependency (optional) (ex. build/fvp/release/bl31.bin)
#   $(4) = FIP prefix (optional) (if FWU_, target is fwu_fip instead of fip)
#   $(5) = encrypted payload (optional) (ex. build/fvp/release/bl31_enc.bin)
define TOOL_ADD_PAYLOAD
ifneq ($(5),)
    $(4)FIP_ARGS += $(2) $(5)
    $(if $(3),$(4)CRT_DEPS += $(1))
else
    $(4)FIP_ARGS += $(2) $(1)
    $(if $(3),$(4)CRT_DEPS += $(3))
endif
    # 添加 fip 的依赖
    $(if $(3),$(4)FIP_DEPS += $(3))
    $(4)CRT_ARGS += $(2) $(1)
endef

根据 TOOL_ADD_IMG_PAYLOAD 传入的参数:

shell 复制代码
$(call TOOL_ADD_PAYLOAD, build/<platform>/release/bl31.bin, --soc-fw, build/<platform>/release/bl31.bin,,,)

此处命令运行后:

  • FIP_ARGS += --soc-fw build//release/bl31.bin
  • FIP_DEPS += build//release/bl31.bin

到此,我们已将 bl31.bin 添加到 fip 目标的依赖 FIP_DEPS中, 而 bl31.bin 的生成规则在 MAKE_BL 宏函数中已定义(前文已分析)。

build_message 字符串与 version_string 字符串

这两个字符串变量的定义在 MAKE_BL 宏函数中:

shell 复制代码
ifdef MAKE_BUILD_STRINGS
	$(call MAKE_BUILD_STRINGS,$(BUILD_DIR)/build_message.o)
else
	@echo 'const char build_message[] = "Built : "$(BUILD_MESSAGE_TIMESTAMP); \
	       const char version_string[] = "${VERSION_STRING}"; \
	       const char version[] = "${VERSION}";' | \
		$$(CC) $$(TF_CFLAGS) $$(CFLAGS) -xc -c - -o $(BUILD_DIR)/build_message.o
endif

在定义指定 BL 的 ELF 规则时,会运行:

shell 复制代码
	@echo 'const char build_message[] = "Built : "$(BUILD_MESSAGE_TIMESTAMP); \
	       const char version_string[] = "${VERSION_STRING}"; \
	       const char version[] = "${VERSION}";' | \
		$$(CC) $$(TF_CFLAGS) $$(CFLAGS) -xc -c - -o $(BUILD_DIR)/build_message.o

命令使用 gcc -c 选项但并未指定源文件生成 $(BUILD_DIR)/build_message.o

根据 gcc 规范,gcc -c - 表示从标准输入中读取源文件内容。

重新解析上述命令,echo 命令会将其后的字符串显示到标准输入、输出上,所以

那么 $(BUILD_DIR)/build_message.o 对应的源文件内容即为:

shell 复制代码
'const char build_message[] = "Built : "$(BUILD_MESSAGE_TIMESTAMP); \
 const char version_string[] = "${VERSION_STRING}";'

所以:build_messageversion_string 在elf镜像文件编译时定义。

相关推荐
Dovis(誓平步青云)3 分钟前
解构C++高级命名空间:构建空间作用域·控制兼容
开发语言·c++·经验分享·笔记·学习方法
DoorToZen12 分钟前
理解 `.sln` 和 `.csproj`:从项目结构到构建发布的一次梳理
经验分享·笔记·其他·前端框架·c#·.net
Arenaschi16 分钟前
运用fmpeg写一个背英文单词的demo带翻译
java·笔记·tcp/ip·其他·eclipse·maven
RLG_星辰5 小时前
第六章-哥斯拉4.0流量分析与CVE-2017-12615的复现
笔记·安全·网络安全·tomcat·应急响应·玄机
敦普水性工业漆10 小时前
汽车紧固件防腐3.0时代:敦普水性漆用无铬锌铝涂层定义「零氢脆」标准
笔记·汽车
TUTO_TUTO11 小时前
【AWS+Wordpress】将本地 WordPress 网站部署到AWS
笔记·学习·云计算·aws
大溪地C12 小时前
CSS详细学习笔记
css·笔记·学习
chennalC#c.h.JA Ptho13 小时前
Centos系统详解架构详解
linux·经验分享·笔记·系统架构·系统安全
饕餮争锋13 小时前
Spring普通配置类 vs 自动配置类-笔记
java·笔记·spring
Aimyon_3613 小时前
Java复习笔记-基础
java·开发语言·笔记