OP-TEE 编译流程

本文深入解析了OPTEE中编译的核心组件——optee_core、SDK(针对32/64位用户态TA)和DTA(动态加载的TA),探讨了它们的编译流程、环境设置、SMP模式以及源码目录结构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

版权声明:本文为CSDN博主「ashimida@」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lidan113lidan/article/details/119879565

更多内容可关注微信公众号  

一、概述:

1. OPTEE中的编译目标:

  OPTEE中需要被编译的目标分为3类:

  1. optee core: 这里是optee的内核代码(运行在Secure EL1,对应目标为core)
  2. optee SDK: 分为32/64位, 用来编译用户态TA的开发环境(对应目标为ta_arm32/ta_arm64)
  3. optee DTA: DTA(动态TA)也就是optee的用户态TA,其是借助optee SDK编译的,与前两者编译是分开的(DTA运行在Secure EL0)

2. DTA与PTA:

  optee中存在两种TA,正常的用户态TA是运行时动态加载的,故通常也称为DTA,还有一种是直接随core编译到optee内核中的,称为PTA(pseudo TA)

  • DTA:
    • DTA在刚编译出来时是一个pie的可执行文件,但其在链接脚本中discard了interpreter,所以看起来像一个dll文件(关于dll和so参考[1]), DTA必须通过optee core来加载
    • DTA是一个运行在secure EL0的独立的进程,拥有独立的地址空间,SMP时也互不影响。
  • PTA
    • PTA实际上是在core中内嵌的一段代码,随optee core共同编译,其与optee core一样运行在secure EL1,地址空间不隔离,只是在smc处理的时候根据uuid来判断下如果是pta内的uuid,则core直接调用pta的代码执行
    • 这也是其为什么叫pta的原因,其优点就是快,不用做用户态的地址空间隔离,dta加载等,但缺点就是权限较高,不同pta之间地址空间不隔离,所以一般编译成pta是要慎重的。
    • 在PTA中可以直接使用optee core的接口,这和DTA还是有一定区别的

3. OPTEE的SMP:

    OPTEE中是不支持调度的,不论是SMP模式还是非SMP模式。在一个core上smc进入optee后都只能一调到底然后返回,optee所谓的SMP指的是每个core上都可以同时分别运行一个ta,但一个core还是最多只能同时运行一个ta。


二、optee源码目录

    optee的源码目录中的文件都只与core(包括pta)和SKD的编译有关,而DTA的编译则与生成后的SDK目录有关,目录的主要内容如下:

PS D:\SourceCode\optee_os-3.2.0> ls
Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----         2020/8/6     11:32                core        ## optee core的代码全在里面,所有的pta默认都放在./core/arch/arm/pta/ 目录下
d-----         2020/8/6     11:32                documentation
d-----         2020/8/6     11:32                keys
d-----         2020/8/6     11:32                lib         ## lib目录,这里面包含的都是库文件,同一份代码在optee core中会编译一次给core用
                                                             ## 在编译SDK时又会编译一次(选项不同),并导出给DTA用
d-----         2020/8/6     11:33                mk          ## core/SDK编译的所有.mk文件都在这个目录,部分.mk文件在编译DTA时是会被共用的
d-----         2020/8/6     11:33                scripts
d-----         2020/8/6     11:33                ta          ## 编译DTA用到的.mk,链接脚本,头文件等都在此目录,最终编译SDK时会被输出到SDK目录
-a----        2020/8/25     16:01           3199 Makefile    ## optee的Makefile,同样也就是core和SDK的makefile
......

三、optee的整体编译流程

  optee的编译实际上就是直接一个make,其编译流程就是直接走的根目录的Makefile,分为:

  1. optee初始化
  2. optee core编译
  3. optee SDK编译

  具体如下:

1. optee初始化

    optee初始化中只是简单配置了几个目标和变量,而大部分环境的初始化是在core的编译时做的,SDK在core之后编译并继承了部分core的环境,也单独设置了一些自己的环境:

## ./Makefile (省略部分代码)
SHELL = /bin/bash

## 这里面引入了几个宏定义
include mk/checkconf.mk

## 默认目标是all, 默认是tee.elf 等tee*一系列,大部分的依赖都在link.mk中定义
.PHONY: all
all:

## optee可以编译为32或64位的,其可以运行在armv7/v8上的,armv8中secure EL1也可以配置为32/64位的
## 在optee的make中的ARCH是不区分32/64的,统一称为arm
# Make these default for now
$(call force,ARCH,arm)

## 输出目录与echo设置
ifeq ($O,)
O               := out
out-dir         := $(O)/$(ARCH)-plat-$(PLATFORM)
else
out-dir         := $(O)
endif

arch_$(ARCH)    := y

ifneq ($V,1)
q := @
cmd-echo := true
cmd-echo-silent := echo
else
q :=
cmd-echo := echo
cmd-echo-silent := true
endif

## makefile版本相关配置
ifneq ($(filter 4.%,$(MAKE_VERSION)),)  # make-4
ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)
cmd-echo-silent := true
endif
else                                    # make-3.8x
ifneq ($(findstring s, $(MAKEFLAGS)),)
cmd-echo-silent := true
endif
endif

2. optee core编译

./Makefile
## 在顶层makefile中,core的编译就此一句
include core/core.mk

2.1 core编译环境初始化

## ./core/core.mk
## 清除了srcs/objs/libfiles/libdirs/libnames/libdeps/sm-$(sm)/incdirs变量,这些变量都是全局作用域用到的
include mk/cleanvars.mk

## sm是submodule的意思,当前编译core就设置为core,后面编译SDK会分别设置为ta_arm32/ta_arm64
## 这个sm很关键,不同编译的编译选项就取决于这个sm和sm-$(sm)的设置
sm := core
sm-$(sm) := y

arch-dir    := core/arch/$(ARCH)
platform-dir    := $(arch-dir)/plat-$(PLATFORM)

## 这里包含平台相关的配置项(OPTEE中的配置项都是CFG_XXX开头的),此配置项中一般也决定了后面是否编译SDK(ta-targets = ta_arm32 ta_arm64)
## 此文件如core/arch/arm/plat-mediatek/conf.mk
include $(platform-dir)/conf.mk

## 这里包含非平台的配置项,里面也是各种CFG_XXX
include mk/config.mk

## 这里目前只能是 core/arch/arm/arm.mk,其中包含了core, SDK(ta_arm32/ta_arm64)三种平台的编译flags的定义
## 根据$(sm)的不同,include此文件后会得到不同的编译flags配置
include core/arch/$(ARCH)/$(ARCH).mk

PLATFORM_$(PLATFORM) := y
PLATFORM_FLAVOR_$(PLATFORM_FLAVOR) := y

$(call cfg-depends-all,CFG_PAGED_USER_TA,CFG_WITH_PAGER CFG_WITH_USER_TA)
## 这里也是config,但都是算法相关的CFG_XXX
include core/crypto.mk

# Setup compiler for this sub module
COMPILER_$(sm)        ?= $(COMPILER)
## 这里是编译器相关配置,GCC,LD,等都是在这里配置的,通常是mk/gcc.mk
include mk/$(COMPILER_$(sm)).mk

## 这里如cppflagcore,是core编译的通用flag
cppflags$(sm)    += -D__KERNEL__
cppflags$(sm)    += -Icore/include
cppflags$(sm)    += -include $(conf-file)
cppflags$(sm)    += -I$(out-dir)/core/include
cppflags$(sm)    += $(core-platform-cppflags)
cflags$(sm)    += $(core-platform-cflags)
ifeq ($(CFG_CORE_SANITIZE_UNDEFINED),y)
cflags$(sm)    += -fsanitize=undefined
endif
ifeq ($(CFG_CORE_SANITIZE_KADDRESS),y)
ifeq ($(CFG_ASAN_SHADOW_OFFSET),)
$(error error: CFG_CORE_SANITIZE_KADDRESS not supported by platform (flavor))
endif
cflags_kasan    += -fsanitize=kernel-address \
           -fasan-shadow-offset=$(CFG_ASAN_SHADOW_OFFSET)\
           --param asan-stack=1 --param asan-globals=1 \
           --param asan-instrumentation-with-call-threshold=0
cflags$(sm)    += $(cflags_kasan)
endif
aflags$(sm)    += $(core-platform-aflags)

cppflags$(sm) += -DTRACE_LEVEL=$(CFG_TEE_CORE_LOG_LEVEL)
ifeq ($(CFG_TEE_CORE_MALLOC_DEBUG),y)
cppflags$(sm) += -DENABLE_MDBG=1
endif
ifneq ($(CFG_TEE_CORE_DEBUG),y)
cppflags$(sm)  += -DNDEBUG
endif

cppflags$(sm)    += -Ilib/libutee/include

## 这里是core的配置文件的生成
conf-file := $(out-dir)/include/generated/conf.h
conf-mk-file := $(out-dir)/conf.mk
conf-cmake-file := $(out-dir)/conf.cmake
$(conf-file): $(conf-mk-file)

cleanfiles += $(conf-file)
cleanfiles += $(conf-mk-file)

$(conf-file): FORCE
    $(call check-conf-h)

$(conf-mk-file):  FORCE
    $(call check-conf-mk)

$(conf-cmake-file):  FORCE
    $(call check-conf-cmake)

2.2 编译core使用的静态库

    在编译optee core之前,会先编译optee core中可能别buildin的静态库,代码如下:

## ./core/core.mk
## base-prefix是库的前缀,如core-lib/libutils/libutils.a 中的core-
base-prefix := $(sm)-

## 编译出core-lib/libutils/libutils.a
libname = utils
libdir = lib/libutils
include mk/lib.mk

## 编译出core-lib/libmpa/libmpa.a
libname = mpa
libdir = lib/libmpa
include mk/lib.mk
base-prefix :=

CFG_CRYPTOLIB_NAME ?= tomcrypt
CFG_CRYPTOLIB_DIR ?= core/lib/libtomcrypt
## 编译core/lib/libtomcrypt.a
libname = $(CFG_CRYPTOLIB_NAME)
libdir = $(CFG_CRYPTOLIB_DIR)
include mk/lib.mk

......

  其中mk/lib.mk负责具体的编译,其代码如下:

## 如 libname = utils libdir = lib/libutils,指定当前lib代码所在的目录
subdirs = $(libdir)
## 遍历subdirs, 确认所有要编译的c/.S文件(源码分析见后)
include mk/subdir.mk
## 编译subdir.mk确认的所有的c/.S文件(源码分析见后)
include mk/compile.mk

## 确认要输出的lib文件名
lib-libfile     = $(out-dir)/$(base-prefix)$(libdir)/lib$(libname).a
cleanfiles    := $(cleanfiles) $(lib-libfile)
libfiles    := $(lib-libfile) $(libfiles)
libdirs     := $(out-dir)/$(base-prefix)$(libdir) $(libdirs)
libnames    := $(libname) $(libnames)
libdeps        := $(lib-libfile) $(libdeps)

define process-lib
## 这个一般是没有定义,通常不会直接输出为一个relocatable文件,而是会生成一个归档文件,最终链接的时候按需连接
ifeq ($(lib-use-ld), y)
$(lib-libfile): $(objs)
    @echo '  LD      $$@'
    @mkdir -p $$(dir $$@)
    $$(q)$$(LD$(sm)) $(lib-ldflags) -o $$@ $$^
else
## 正常后是直接打包成.a文件
$(lib-libfile): $(objs)
    @$(cmd-echo-silent) '  AR      $$@'
    @mkdir -p $$(dir $$@)
    $$(q)rm -f $$@ && $$(AR$(sm)) rcs $$@ $$^
endif
endef #process-lib

## 负责将所有编译出的.o文件打包成库
$(eval $(call process-lib))

## 这里的conf-file如 $(out-dir)/include/generated/conf.h
$(objs): $(conf-file)

......

2.3 确定所有需要编译的源码文件

    subdirs.mk脚本是用来确认需要编译的源码文件的,optee core/SDK/DTA都使用此脚本来确认哪些源码文件需要参与编译。subdirs.mk需要一个输入变量subdirs,用来指定要从哪些目录中遍历子目录,此脚本会递归遍历subdirs中sub.mk通过sub-subdirs指定的子目录中的所有子目录,并在每个目录和子目录中查找sub.mk文件,sub.mk文件是一个需要用户指定的文件,其中指定了哪些源码文件需要参与编译。

    用户在sub.mk中可设置的变量有三个,分别是:

  • gensrcs-y: 代表需动态生成的源码文件(包括.c和.S文件,若配置此文件,sub.mk中同样要有对应的recipe-开头的编译命令)
  • srcs-y: 正常需要编译的源码文件(包括.c和.S文件)
  • asm-defines-y: 记录要由.c生成的.S文件,目前看到的只有一个asm-defines.c => asm-defines.S

    最终subdirs.mk返回后,会将三者中要编译的文件分别记录到gen-srcs,srcs和asm-defines-files中:

##./core/core.mk
## optee core要编译的目录包括: core/arch/arm/kernel core/arch/arm/crypto core/arch/arm/mm core/arch/arm/tee core/arch/arm/pta core
## 这个是在 core\arch\arm\arm.mk中设置的; 递归遍历时遍历sub.mk中sub-subdirs记录的子目录,而不是全部子目录,所以这里指定了好多个core的子目录
subdirs = $(core-platform-subdirs) core

## subdir.mk负责确定所有要编译的目标,此脚本内部递归每个子目录的sub.mk中记录的gensrcs-y srcs-y asm-defines-y subdirs-y配置,其中
## 1) gensrcs-y srcs-y asm-defines-y指定的目标分别记录到变量gen-srcs,srcs和asm-defines-files中
## 2) subdirs-y 指定的目标,会被递归处理同样加到1)中的三个中
include mk/subdir.mk

其中subdir.mk:

## ./mk/subdir.mk (省略部分代码)
srcs :=
gen-srcs :=
asm-defines-files :=

## 这里是srcs-y中目标的展开
define process-subdir-srcs-y
##设置当前文件编译后的输出路径
ifeq ($$(sub-dir),.)
srcs                 += $1
oname                := $(out-dir)/$(base-prefix)$(basename $1).o
else
# $1 is an absolute path - start with "/"
srcs                 += $1
oname                := $(out-dir)/$(base-prefix)$(basename $1).o
endif

## 设置当前文件的编译flags
cflags-$$(oname)         := $$(cflags-y) $$(cflags-$(1)-y)
cflags-remove-$$(oname)     := $$(cflags-remove-y) $$(cflags-remove-$(1)-y)
cppflags-$$(oname)         := $$(cppflags-y) $$(cppflags-$(1)-y)
cppflags-remove-$$(oname)     := $$(cppflags-remove-y) $$(cppflags-remove-$(1)-y)
aflags-$$(oname)         := $$(aflags-y) $$(aflags-$(1)-y)
aflags-remove-$$(oname)     := $$(aflags-remove-y) $$(aflags-remove-$(1)-y)
incdirs-$$(oname)        := $$(thissubdir-incdirs) $$(addprefix $(sub-dir)/,$$(incdirs-$(1)-y))

## 清除已有falgs
cflags-$(1)-y             :=
cflags-remove-$(1)-y        :=
cflags-lib-y            :=
cppflags-$(1)-y            :=
cppflags-remove-$(1)-y        :=
cppflags-lib-y            :=
aflags-$(1)-y             :=
aflags-remove-$(1)-y        :=
incdirs-$(1)-y            :=
fname                :=
oname                :=
endef #process-subdir-srcs-y

## 这里是gensrcs-y中目标的展开
define process-subdir-gensrcs-helper
gen-srcs            += $2
oname                := $3
FORCE-GENSRC: $2
$$(addprefix $4,$$(produce-additional-$1)): $2

subdir-$2 := $$(sub-dir)
recipe-$2 := $$(recipe-$1)
## gensrcs需要提供recipe-开头的编译命令
$2: $$(depends-$1)
    @$(cmd-echo-silent) '  GEN     $2'
    $(q)mkdir -p $4
    ## 这里目前就一生成了一个ta_pub_key.c,见recipe-ta_pub_key
    $(q)$$(recipe-$2)

cflags-$$(oname)         := $$(cflags-y) $$(cflags-$(1)-y)
......    ## 这里和前面相同,略去
oname                :=
endef #process-subdir-gensrcs-helper

## 这里是gensrcs-y中目标的展开
define process-subdir-gensrcs-y
$$(eval $$(call process-subdir-gensrcs-helper,$1,$(sub-dir-out)/$$(produce-$1),$(sub-dir-out)/$(basename $(produce-$1)).o,$(sub-dir-out)))
endef #process-subdir-gensrcs-y

## 这里是asm-defines-y中目标的展开
define process-subdir-asm-defines-y
asm-defines-files += $(sub-dir)/$1
endef #process-subdir-asm-defines-y

## 这里是subdirs-y中目标的处理,代表要处理的子目录
define process-subdir
## 要处理的子目录
sub-dir := $1
ifeq ($1,.)
## 输出目录
sub-dir-out := $(out-dir)/$(base-prefix)
else
sub-dir-out := $(out-dir)/$(base-prefix)$1
endif

## include当前处理的子目录中的sub.mk,这里面记录所有子目录中需要make的内容
include $1/sub.mk
## sub.mk中的subdirs-y是下一级需要make的子目录
sub-subdirs := $$(addprefix $1/,$$(subdirs-y))
## 这里是增加include目录,此目录会输出给SDK
incdirs$(sm) := $(incdirs$(sm)) $$(addprefix $1/,$$(global-incdirs-y))
thissubdir-incdirs := $(out-dir)/$(base-prefix)$1 $$(addprefix $1/,$$(incdirs-y))
ifneq ($$(libname),)
incdirs-lib$$(libname)-$$(sm) := $$(incdirs-lib$$(libname)-$$(sm)) $$(addprefix $1/,$$(incdirs-lib-y))
cflags-lib$$(libname)-$$(sm) := $$(cflags-lib$$(libname)-$$(sm)) $$(cflags-lib-y)
cppflags-lib$$(libname)-$$(sm) := $$(cppflags-lib$$(libname)-$$(sm)) $$(cppflags-lib-y)
endif

# Process files in current directory
## 处理sub.mk中的gensrcs-y,srcs-y,asm-defines-y定义的目标, gensrcs-y目前只有两个目标,分别是ta_pub_key 和 early-ta-core,
## gensrcs-y中的目标是要在out目录生成源码的目标,这里生成完源码后,将源码目标放到gen-srcs中
$$(foreach g, $$(gensrcs-y), $$(eval $$(call process-subdir-gensrcs-y,$$(g))))
## 这里只是将目标放到srcs中,然后设置此目标单独的flags
$$(foreach s, $$(srcs-y), $$(eval $$(call process-subdir-srcs-y,$$(s))))
## 这里将最终目标放到asm-defines-files中
$$(foreach a, $$(asm-defines-y), $$(eval $$(call process-subdir-asm-defines-y,$$(a))))

# Clear flags used when processing current directory
srcs-y :=
......
asm-defines-y :=

# Process subdirectories in current directory
$$(foreach sd, $$(sub-subdirs), $$(eval $$(call process-subdir,$$(sd))))
endef #process-subdir

# Top subdirectories
## 这里处理顶层subdir,最后会处理core目录,这里只是将sub.mk中
## 1) gensrcs-y srcs-y asm-defines-y指定的目标分别记录到gen-srcs,srcs和asm-defines-files中
## 2) subdirs-y 指定的目标,会被递归处理同样加到1)中的三个中
$(foreach sd, $(subdirs), $(eval $(call process-subdir,$(sd))))

2.4 源码编译

    源码编译就直接使用mk/compile.mk来完成的,在optee core/SDK/DTA中都是使用此脚本来完成源码编译的,此脚本的参数来自于前面subdirs.mk返回的gen-srcs,srcs和asm-defines-file, 最终编译后的结果会保存到全局变量objs中供后续链接使用:

##./core/core.mk
include mk/compile.mk

  其中compile.mk:

## ./mk/compile.mk (忽略部分代码)
## 这里真正编译gen-srcs,srcs和asm-defines-files中的内容

## 这里保存最终编译出的.o文件
objs        :=

## 这里设置通用的flags
comp-cflags$(sm) = -std=gnu99
comp-aflags$(sm) =
comp-cppflags$(sm) =

ifeq ($(CFG_WERROR),y)
comp-cflags$(sm)    += -Werror
endif
comp-cflags$(sm)      += -fdiagnostics-show-option

comp-cflags-warns-high = -Wall -Wcast-align -Werror-implicit-function-declaration ......
comp-cflags-warns-medium = -Waggregate-return -Wredundant-decls
comp-cflags-warns-low = -Wold-style-definition -Wstrict-aliasing=2 .......
comp-cflags-warns-1:= $(comp-cflags-warns-high)
comp-cflags-warns-2:= $(comp-cflags-warns-1) $(comp-cflags-warns-medium)
comp-cflags-warns-3:= $(comp-cflags-warns-2) $(comp-cflags-warns-low)
WARNS        ?= 3

comp-cflags$(sm)    += $(comp-cflags-warns-$(WARNS))

CHECK ?= sparse
.PHONY: FORCE
.PHONY: FORCE-GENSRC
FORCE:
FORCE-GENSRC:

## srcs和gen-srcs中的目标最终都是通过此宏展开后编译的
## 参数 $1是源码文件 $2是编译后的.o文件
define process_srcs
## objs中增加要编译出的.o文件
objs        += $2
comp-dep-$2    := $$(dir $2).$$(notdir $2).d
comp-cmd-file-$2:= $$(dir $2).$$(notdir $2).cmd

## 不同模块的编译选项的区别都是通过这个来区分的,主要的区分flag是   cppflags$$(comp-sm-$2),如:
## core.mk (D:\SourceCode\optee_os-3.2.0\core) line 32 : cppflags$(sm)    += -D__KERNEL__
## ta.mk (D:\SourceCode\optee_os-3.2.0\ta) line 36 : cppflags$(sm)    := $(platform-cppflags) $($(sm)-platform-cppflags)
comp-sm-$2    := $(sm)
comp-lib-$2    := $(libname)-$(sm)

cleanfiles := $$(cleanfiles) $$(comp-dep-$2) $$(comp-cmd-file-$2) $2

## 如果当前srcs或gen-srcs中的目标是一个.c文件
ifeq ($$(filter %.c,$1),$1)
comp-q-$2 := CC
comp-flags-$2 = $$(filter-out $$(CFLAGS_REMOVE) $$(cflags-remove) \
                  $$(cflags-remove-$$(comp-sm-$2)) \
                  $$(cflags-remove-$2), \
           $$(CFLAGS$$(arch-bits-$$(comp-sm-$2))) $$(CFLAGS_WARNS) \
           $$(comp-cflags$$(comp-sm-$2)) $$(cflags$$(comp-sm-$2)) \
           $$(cflags-lib$$(comp-lib-$2)) $$(cflags-$2))
## 如果当前srcs或gen-srcs中的目标是一个.S文件
else ifeq ($$(filter %.S,$1),$1)
## 这里只是输出AS,不是用AS编译
comp-q-$2 := AS
comp-flags-$2 = -DASM=1 $$(filter-out $$(AFLAGS_REMOVE) $$(aflags-remove) \
                      $$(aflags-remove-$$(comp-sm-$2)) \
                      $$(aflags-remove-$2), \
               $$(AFLAGS) $$(comp-aflags$$(comp-sm-$2)) \
               $$(aflags$$(comp-sm-$2)) $$(aflags-$2))
else
$$(error "Don't know what to do with $1")
endif

## 整理flags,core ta_arm32 ta_arm64独有的flags也会加入到这里
comp-cppflags-$2 = $$(filter-out $$(CPPFLAGS_REMOVE) $$(cppflags-remove) \
                 $$(cppflags-remove-$$(comp-sm-$2)) \
                 $$(cppflags-remove-$2), \
              $$(nostdinc$$(comp-sm-$2)) $$(CPPFLAGS) \
              $$(addprefix -I,$$(incdirs$$(comp-sm-$2))) \
              $$(addprefix -I,$$(incdirs-lib$$(comp-lib-$2))) \
              $$(addprefix -I,$$(incdirs-$2)) \
              ## 这里的comp-sm-$2 如 core ta_arm32 ta_arm64
              $$(cppflags$$(comp-sm-$2)) \
              $$(cppflags-lib$$(comp-lib-$2)) $$(cppflags-$2))

## 这个是最终编译使用的flags
comp-flags-$2 += -MD -MF $$(comp-dep-$2) -MT $$@
comp-flags-$2 += $$(comp-cppflags-$2)

-include $$(comp-cmd-file-$2)
-include $$(comp-dep-$2)

## 这里是最终要展开的.c => .o的规则
$2: $1 FORCE-GENSRC
    ## 这个是if_changed 然后build
    $$(if $$(strip $$(filter-out FORCE-GENSRC, $$?) \
        $$(filter-out $$(comp-cmd-$2), $$(old-cmd-$2)) \
        $$(filter-out $$(old-cmd-$2), $$(comp-cmd-$2))), \
        @set -e ;\
        mkdir -p $$(dir $2) ;\
        $$(echo-check-$2) '  CHECK   $$<' ;\
        $$(echo-check-cmd-$2) ;\
        $$(check-cmd-$2) ;\
        ## 这里输出 CC/AS /xxx/xxx/xxx.o
        $(cmd-echo-silent) '  $$(comp-q-$2)      $$@' ;\
        ## 这里输出的是详细的编译命令,这里将"替换为\"
        $(cmd-echo) $$(subst \",\\\",$$(comp-cmd-$2)) ;\
        ## 真正的编译命令
        $$(comp-cmd-$2) ;\
        $(cmd-echo) $$(comp-objcpy-cmd-$2) ;\
        ## 编译后通过objcopy将输出文件中的.rodata两个section重命名了
        $$(comp-objcpy-cmd-$2) ;\
        echo "old-cmd-$2 := $$(subst \",\\\",$$(comp-cmd-$2))" > \
            $$(comp-cmd-file-$2) ;\
    )
endef  ##process_srcs

## 遍历srcs中所有目标,并展开其编译规则
$(foreach f, $(srcs), $(eval $(call \
    process_srcs,$(f),$(out-dir)/$(base-prefix)$$(basename $f).o)))

## 遍历gen-srcs中所有目标,并展开其编译规则
$(foreach f, $(gen-srcs), $(eval $(call process_srcs,$(f),$$(basename $f).o)))

$(objs): $(conf-file)

## 这里是对asm-defines-files中目标的处理,和src类似
define _gen-asm-defines-file
FORCE-GENSRC: $(2)
comp-dep-$3    := $$(dir $3)$$(notdir $3).d
comp-cmd-file-$3:= $$(dir $3)$$(notdir $3).cmd
comp-sm-$3    := $(sm)
cleanfiles := $$(cleanfiles) $$(comp-dep-$3) $$(comp-cmd-file-$3) $3 $2

comp-flags-$3 = $$(filter-out $$(CFLAGS_REMOVE) $$(cflags-remove) \
                  $$(cflags-remove-$$(comp-sm-$3)) \
                  $$(cflags-remove-$3), \
           $$(CFLAGS) $$(CFLAGS_WARNS) \
           $$(comp-cflags$$(comp-sm-$3)) $$(cflags$$(comp-sm-$3)) \
           $$(cflags-lib$$(comp-lib-$3)) $$(cflags-$3))

comp-cppflags-$3 = $$(filter-out $$(CPPFLAGS_REMOVE) $$(cppflags-remove) \
                 $$(cppflags-remove-$$(comp-sm-$3)) \
                 $$(cppflags-remove-$3), \
              $$(nostdinc$$(comp-sm-$3)) $$(CPPFLAGS) \
              $$(addprefix -I,$$(incdirs$$(comp-sm-$3))) \
              $$(addprefix -I,$$(incdirs-lib$$(comp-lib-$3))) \
              $$(addprefix -I,$$(incdirs-$3)) \
              $$(cppflags$$(comp-sm-$3)) \
              $$(cppflags-lib$$(comp-lib-$3)) $$(cppflags-$3))

comp-flags-$3 += -MD -MF $$(comp-dep-$3) -MT $$@
comp-flags-$3 += $$(comp-cppflags-$3)

comp-cmd-$3 = $$(CC$(sm)) $$(comp-flags-$3) -fverbose-asm -S $$< -o $$@

-include $$(comp-cmd-file-$3)
-include $$(comp-dep-$3)

## 这里默认只有 asm-defines.S:asm-defines.c 一个目标
$3: $1 $(conf-file) FORCE
# Check if any prerequisites are newer than the target and
# check if command line has changed
    $$(if $$(strip $$(filter-out FORCE, $$?) \
        $$(filter-out $$(comp-cmd-$3), $$(old-cmd-$3)) \
        $$(filter-out $$(old-cmd-$3), $$(comp-cmd-$3))), \
        @set -e ;\
        mkdir -p $$(dir $2) $$(dir $3) ;\
        $(cmd-echo) $$(subst \",\\\",$$(comp-cmd-$3)) ;\
        $$(comp-cmd-$3) ;\
        echo "old-cmd-$3 := $$(subst \",\\\",$$(comp-cmd-$3))" > \
            $$(comp-cmd-file-$3) ;\
    )
guard-$2 := $$(subst -,_,$$(subst .,_,$$(subst /,_,$2)))

## 具体规则
$(2): $(3)
    $(q)set -e;                            \
    $(cmd-echo-silent) '  CHK     $$@';            \
    mkdir -p $$(dir $$@);                    \
    echo "#ifndef $$(guard-$2)" >$$@.tmp;            \
    echo "#define $$(guard-$2)" >>$$@.tmp;            \
    sed -ne 's|^==>\([^ ]*\) [\$$$$#]*\([-0-9]*\) \([^@/]*\).*|#define \1\t\2\t/* \3*/|p' \
    < $$< >>$$@.tmp;                    \
    echo "#endif" >>$$@.tmp;                \
    $$(call mv-if-changed,$$@.tmp,$$@)
endef

## 这里是对asm-defines-files中目标的处理,和src类似
define gen-asm-defines-file
$(call _gen-asm-defines-file,$1,$2,$(dir $2).$(notdir $(2:.h=.s)))
endef

## 这里正常只处理asm-defines.s文件的编译
$(foreach f,$(asm-defines-files),$(eval $(call gen-asm-defines-file,$(f),$(out-dir)/$(sm)/include/generated/$(basename $(notdir $(f))).h)))

2.5 链接tee.elf

    前面目录遍历和编译的脚本是在多个目标共用的,但最终的链接脚本都是各自的,optee core最终的链接脚本正常是./core/arch/arm/kernel/link.mk,但也可以是由平台指定的:

## ./core/core.mk
##  通常是core/arch/arm/kernel/link.mk
include $(if $(wildcard $(platform-dir)/link.mk), \
        $(platform-dir)/link.mk, \
        core/arch/$(ARCH)/kernel/link.mk)
## 上面是core.mk的最后一行代码

  默认的core/arch/arm/kernel/link.mk指定了多个目标,但最基本的目标就是optee core的二进制tee.elf:

##./core/arch/arm/kernel/link.mk (只保留了tee.elf的脚本,这个文件里还有很多其他的编译选项)
## 最终链接结果输出目录
link-out-dir = $(out-dir)/core
## all_objs的链接脚本
link-script-dummy = core/arch/arm/kernel/link_dummy.ld
link-script = $(if $(wildcard $(platform-dir)/kern.ld.S), \
        $(platform-dir)/kern.ld.S, \
        core/arch/arm/kernel/kern.ld.S)
link-script-pp = $(link-out-dir)/kern.ld
link-script-dep = $(link-out-dir)/.kern.ld.d

AWK     = awk

## tee.elf的链接flags
link-ldflags  = $(LDFLAGS)
link-ldflags += -T $(link-script-pp) -Map=$(link-out-dir)/tee.map
link-ldflags += --sort-section=alignment
link-ldflags += --fatal-warnings
link-ldflags += --gc-sections

## 额外的链接参数
link-ldadd  = $(LDADD)
link-ldadd += $(addprefix -L,$(libdirs))
link-ldadd += $(addprefix -l,$(libnames))

##最终参与链接的目标,就排除了一个link_dummies.o
link-objs := $(filter-out $(out-dir)/core/arch/arm/kernel/link_dummies.o, $(objs))
## tee.elf 的全部链接参数
ldargs-tee.elf := $(link-ldflags) $(link-objs) $(link-out-dir)/version.o $(link-ldadd) $(libgcccore)

## 链接脚本相关
link-script-cppflags := -DASM=1 \
    $(filter-out $(CPPFLAGS_REMOVE) $(cppflags-remove), \
        $(nostdinccore) $(CPPFLAGS) \
        $(addprefix -I,$(incdirscore) $(link-out-dir)) \
        $(cppflagscore))

## 关键目标 tee.elf
all: $(link-out-dir)/tee.elf
cleanfiles += $(link-out-dir)/tee.elf $(link-out-dir)/tee.map
cleanfiles += $(link-out-dir)/version.o
cleanfiles += $(link-out-dir)/.buildcount
cleanfiles += $(link-out-dir)/.tee.elf.cmd

## tee.elf的链接命令
$(link-out-dir)/tee.elf: $(link-objs) $(libdeps) $(link-script-pp) $(FORCE_LINK)
    @echo "old-link-objs := $(link-objs)" >$(link-out-dir)/.tee.elf.cmd
    @$(cmd-echo-silent) '  LD      $@'
    $(q)$(LDcore) $(ldargs-tee.elf) -o $@

3. optee SDK编译

    在2中include 了core.mk,并完成了optee core的编译,最终生成了二进制tee.elf, 当前编译回到./Makefile,这一步进行SDK的编译。

    SDK的编译环境在core.mk中已经设置了一部分,此时会调用ta/ta.mk来编译SDK,此脚本内部也会覆盖部分编译环境。

    SDK通常可以编译32/64位的(本质取决于用的什么编译器),在编译脚本中实际上是通过参数ta-target传给ta/ta.mk来控制的,若指定ta_arm32则会编译32位SDK,ta_arm64编译64位SDK,二者同时指定则同时编译:

# Platform config is supposed to assign the targets
## 正常来说,编译的目标可以是ta_arm32,或ta_arm64,这些应该在core.mk中指定好的,如果没指定,默认是user_ta
## user_ta貌似也能编译出来,但编译flags不太一样,此时编译器取得也是默认的编译器
ta-targets ?= user_ta

ifeq ($(CFG_WITH_USER_TA),y)
define build-ta-target
ta-target := $(1)
include ta/ta.mk
endef

## 编译ta-targets,并调用ta/ta.mk来分别构建SDK
$(foreach t, $(ta-targets), $(eval $(call build-ta-target, $(t))))
endif

  ta.mk编译SDK的步骤如下

3.1 重置部分编译环境

    基本上配置相关的编译环境变量是不会被重置的,也就是保证optee core和SDK的编译配置(如CFG_XXX)是一致的,但编译过程中用到的变量如objs等都会被重置的:

## ./ta/ta.mk (省略部分代码)
## 清除srcs/objs/libfiles/libdirs/libnames/libdeps/sm-$(sm)/incdirs变量
include mk/cleanvars.mk

## 当前编译ta_arm32 or ta_arm64
sm := $(ta-target)
sm-$(sm) := y

# Setup compiler for this sub module
COMPILER_$(sm)        ?= $(COMPILER)

## 这里一般还是mk/gcc.mk 用来确定工具链的
include mk/$(COMPILER_$(sm)).mk

## make core时候include的如mk/config.mk中的CFG_XXX,这里同样生效
ifeq ($(CFG_TA_MBEDTLS_SELF_TEST),y)
$(sm)-platform-cppflags += -DMBEDTLS_SELF_TEST
endif

## ta-mk-file-export-vars-$(sm) 中指定的flags,是最终要输出给DTA的flags,DTA中会看到这些CFG的配置
# Config variables to be explicitly exported to the dev kit conf.mk
ta-mk-file-export-vars-$(sm) += CFG_TA_FLOAT_SUPPORT
ta-mk-file-export-vars-$(sm) += CFG_CACHE_API
ta-mk-file-export-vars-$(sm) += CFG_SECURE_DATA_PATH
ta-mk-file-export-vars-$(sm) += CFG_TA_MBEDTLS_SELF_TEST
ta-mk-file-export-vars-$(sm) += CFG_TA_MBEDTLS
ta-mk-file-export-vars-$(sm) += CFG_SYSTEM_PTA
ta-mk-file-export-vars-$(sm) += CFG_TA_DYNLINK

## 这些flags在core include arm.mk的时候都已经存在了,这里只不过将ta_arm32/64相关的放到最终的编译flags中
cppflags$(sm)    := $(platform-cppflags) $($(sm)-platform-cppflags)
cflags$(sm)    := $(platform-cflags) $($(sm)-platform-cflags)
aflags$(sm)    := $(platform-aflags) $($(sm)-platform-aflags)
cppflags$(sm)    += -include $(conf-file)
cppflags$(sm) += -DTRACE_LEVEL=$(CFG_TEE_TA_LOG_LEVEL)

3.2 根据配置重新编译静态库

    3.1中设置的编译选项会直接作用在compile.mk的编译过程中,这里虽然和前面optee core一样都是通过mk/lib.mk编译静态库,但由于配置不同,最终编译的二进制也不同。这里是为DTA重新编译一套静态库,作为SDK的一部分最后输出:

## ./ta/ta.mk
## 根据以上的配置,重新编译DTA使用的静态库
base-prefix := $(sm)-

libname = utils
libdir = lib/libutils
include mk/lib.mk

libname = mpa
libdir = lib/libmpa
include mk/lib.mk

libname = utee
libdir = lib/libutee
include mk/lib.mk

ifeq ($(CFG_TA_MBEDTLS),y)
libname = mbedtls
libdir = lib/libmbedtls
include mk/lib.mk
ta-mk-file-export-vars-$(sm) += CFG_TA_MBEDTLS
endif

3.3 打包并输出SDK

    这部分代码负责打包并输出SDK,输出的内容包括:

  • include的头文件
  • 为DTA重新编译的静态库
  • DTA编译的各种*.mk文件
  • DTA需要的.c,lds,配置,脚本文件等
base-prefix :=
## 头文件
incdirs-host := $(filter-out lib/libutils%, $(incdirs$(sm)))
incfiles-extra-host := lib/libutils/ext/include/compiler.h
incfiles-extra-host += lib/libutils/ext/include/util.h
incfiles-extra-host += lib/libutils/ext/include/types_ext.h
incfiles-extra-host += $(conf-file)
incfiles-extra-host += $(conf-mk-file)
incfiles-extra-host += $(conf-cmake-file)
incfiles-extra-host += core/include/tee/tee_fs_key_manager.h
incfiles-extra-host += core/include/tee/fs_htree.h
incfiles-extra-host += core/include/signed_hdr.h

## 复制命令
define copy-file
$2/$$(notdir $1): $1
    @set -e; \
    mkdir -p $$(dir $$@) ; \
    $(cmd-echo-silent) '  INSTALL $$@' ; \
    cp $$< $$@

cleanfiles += $2/$$(notdir $1)
ta_dev_kit: $2/$$(notdir $1)
endef

## 后面全都是到export目录的各种复制
## 这里是将所有的lib,都到export目录输出一份
$(foreach f, $(libfiles), \
    $(eval $(call copy-file, $(f), $(out-dir)/export-$(sm)/lib)))

## 编译DTA用到的*.mk文件复制到export目录,这个是给用户态ta编译用的
ta-mkfiles = mk/compile.mk mk/subdir.mk mk/gcc.mk mk/cleandirs.mk \
    ta/arch/$(ARCH)/link.mk ta/arch/$(ARCH)/link_shlib.mk \
    ta/mk/ta_dev_kit.mk

$(foreach f, $(ta-mkfiles), \
    $(eval $(call copy-file, $(f), $(out-dir)/export-$(sm)/mk)))

## ta的头文件复制
# Copy the .h files for TAs
define copy-incdir
sf := $(subst $1/, , $(shell find $1 -name "*.h"))
$$(foreach h, $$(sf), $$(eval $$(call copy-file, $1/$$(h), \
    $$(patsubst %/,%,$$(subst /./,/,$2/$$(dir $$(h)))))))
endef

$(foreach d, $(incdirs$(sm)), \
    $(eval $(call copy-incdir, $(d), $(out-dir)/export-$(sm)/include)))

## 同样是头文件
# Copy the .h files needed by host
$(foreach d, $(incdirs-host), \
    $(eval $(call copy-incdir, $(d), $(out-dir)/export-$(sm)/host_include)))
$(foreach f, $(incfiles-extra-host), \
    $(eval $(call copy-file, $(f), $(out-dir)/export-$(sm)/host_include)))

## DTA头的一个.c文件和DTA的lds文件,均参与DTA的编译
# Copy the src files
ta-srcfiles = ta/arch/$(ARCH)/user_ta_header.c ta/arch/$(ARCH)/ta.ld.S
$(foreach f, $(ta-srcfiles), \
    $(eval $(call copy-file, $(f), $(out-dir)/export-$(sm)/src)))

## DTA的签名文件
ta-keys = keys/default_ta.pem
$(foreach f, $(ta-keys), \
    $(eval $(call copy-file, $(f), $(out-dir)/export-$(sm)/keys)))

## 脚本文件
ta-scripts = scripts/sign.py scripts/symbolize.py
$(foreach f, $(ta-scripts), \
    $(eval $(call copy-file, $(f), $(out-dir)/export-$(sm)/scripts)))

## 为DTA生成配置文件
# Create config file
conf-mk-file-export := $(out-dir)/export-$(sm)/mk/conf.mk
sm-$(conf-mk-file-export) := $(sm)
define mk-file-export
.PHONY: $(conf-mk-file-export)
$(conf-mk-file-export):
    @$$(cmd-echo-silent) '  CHK    ' $$@
    $(q)echo sm := $$(sm-$(conf-mk-file-export)) > $$@.tmp
    $(q)echo sm-$$(sm-$(conf-mk-file-export)) := y >> $$@.tmp
    $(q)($$(foreach v, $$(ta-mk-file-export-vars-$$(sm-$(conf-mk-file-export))), \
        $$(if $$($$(v)),echo $$(v) := $$($$(v));,))) >> $$@.tmp
    $(q)echo '$$(ta-mk-file-export-add-$$(sm-$(conf-mk-file-export)))' | sed 's/_nl_ */\n/g' >> $$@.tmp
    $(q)$(call mv-if-changed,$$@.tmp,$$@)
endef
$(eval $(mk-file-export))

cleanfiles := $(cleanfiles) $(conf-mk-file-export)
ta_dev_kit: $(conf-mk-file-export)

## 加入到编译目标
all: ta_dev_kit

四、DTA的编译

    DTA的编译开始于SDK导出的ta_dev_kit.mk, 只要include ta_dev_kit.mk即可编译出DTA,利用此脚本除了编译出DTA外,还可以编译出DTA使用的动态,静态库,只需要指定不同的目标即可:

  • 编译DTA:
BINARY:= {xxxx}    ##这里需指定一个uuid, BINARY代表要编译一个DTA,当前目录下sub.mk中指定的文件和目录即会被最终编译成一个DTA
include xxx/ta_dev_kit.mk
  • 编译DTA用到的静态库:

LIBNAME:= xxx   ##这里需指定一个静态库的名字,当前目录下sub.mk中指定的文件和目录即会被最终编译成一个DTA静态库
include ta_dev_kit.mk
  • 编译DTA用到的动态库

SHLIBNAME:= xxx   ##这里需指定一个动态库的名字,当前目录下sub.mk中指定的文件和目录即会被最终编译成一个DTA动态
include ta_dev_kit.mk

    ta_dev_kit.mk的步骤如下:

1.初始化编译环境

    DTA和前面optee core/SDK编译流程是完全无关的,其是在一个干净环境下编译的,故所有需要的配置文件都需要重新include:

## SDK/mk/ta_dev_kit.mk (省略部分代码)
# Get the dir of the ta-dev-kit, requires make version 3.81 or later
## 用户态ta(DTA)就是include这个编译出来的这里获取MAKEFILE_LIST中的最后一个文件,也就是当前ta_dev_kit.mk的路径,也就是SDK的路径
ta-dev-kit-dir := $(patsubst %/,%,$(abspath $(dir $(lastword $(MAKEFILE_LIST)))..))

.PHONY: all
all:

## 从SKD路径获取conf.mk,这个conf.mk就是编译SDK时ta-mk-file-export-vars-$(sm)变量导出的
## sm的定义也来自于这个conf.mk(如sm := ta_arm64),一个conf.mk就确定了SDK的编译环境
include $(ta-dev-kit-dir)/mk/conf.mk

## BINARY = cb3e....... (一个uuid) 代表编译一个ta(pie文件,但忽略了intepreter,看起来像dll)
## SHLIBNAME = libos_test (编译一个动态链接库文件,-shared)
## LIBNAME (编译一个静态库文件*.a)
## 三者只能同时选一个,具体编译安歇文件则记录在当前目录的sub.mk中
ifneq (1, $(words $(BINARY) $(LIBNAME) $(SHLIBNAME)))
$(error You must specify exactly one of BINARY, LIBNAME or SHLIBNAME)
endif

binary := $(BINARY)
libname := $(LIBNAME)
shlibname := $(SHLIBNAME)
shlibuuid := $(SHLIBUUID)

## 这里的默认值来自于SDK的conf.mk
cppflags$(sm)  := $($(sm)-platform-cppflags) $(CPPFLAGS_$(sm))
aflags$(sm)    := $($(sm)-platform-aflags)
cflags$(sm)    := $($(sm)-platform-cflags) $(CFLAGS_$(sm))

CFG_TEE_TA_LOG_LEVEL ?= 2
cppflags$(sm) += -DTRACE_LEVEL=$(CFG_TEE_TA_LOG_LEVEL)
cppflags$(sm) += -I. -I$(ta-dev-kit-dir)/include

## 库路径也来自于SDK目录
libdirs += $(ta-dev-kit-dir)/lib
libnames += utils utee mpa
libdeps += $(ta-dev-kit-dir)/lib/libutils.a
libdeps += $(ta-dev-kit-dir)/lib/libmpa.a
libdeps += $(ta-dev-kit-dir)/lib/libutee.a
ifeq ($(CFG_TA_MBEDTLS),y)
libnames += mbedtls
libdeps += $(ta-dev-kit-dir)/lib/libmbedtls.a
endif

# Pass config variable (CFG_) from conf.mk on the command line
## 命令行通过-D的形式加入conf.mk中CFG_开头的配置项
cppflags$(sm) += $(strip \
    $(foreach var, $(filter CFG_%,$(.VARIABLES)), \
        $(if $(filter y,$($(var))), \
            -D$(var)=1, \
            $(if $(filter xn x,x$($(var))),,-D$(var)='$($(var))'))))

## 清空输出目录
include $(ta-dev-kit-dir)/mk/cleandirs.mk

.PHONY: clean
clean:
    @$(cmd-echo-silent) '  CLEAN   $(out-dir)'
    ${q}rm -f $(cleanfiles)
    ${q}dirs="$(call cleandirs-for-rmdir)"; if [ "$$dirs" ]; then $(RMDIR) $$dirs; fi
    @$(cmd-echo-silent) '  CLEAN   $(O)'
    ${q}if [ -d "$(O)" ]; then $(RMDIR) $(O); fi

2.确认并编译所有源码文件

    DTA的编译和optee core/SDK一样,都是利用了subdirs.mk 和compile.mk这一套机制,而subdirs.mk的顶层目录就是当前目录, 当前目录的sub.mk文件则决定哪些子目录和当前目录的哪些文件要参与编译:

## SDK/mk/ta_dev_kit.mk
## 当前目录是dta的目录,这里通过subdir.mk导入所有子目录中sub.mk中记录的编译文件
subdirs = .
include  $(ta-dev-kit-dir)/mk/subdir.mk

ifneq ($(binary),)
# Build target is TA
## DTA编译要加上一个SDK中写好的.c文件,此文件也会参与编译(CFIcheck的代码可以加到这里)
vpath %.c $(ta-dev-kit-dir)/src
srcs += user_ta_header.c
endif

## 这个文件就是从./mk/compile.mk复制过来的,初始化工具链
include  $(ta-dev-kit-dir)/mk/gcc.mk

## 这个文件也是从./mk/compile.mk复制过来的,源码编译并将最终结果输出到objs
include  $(ta-dev-kit-dir)/mk/compile.mk

3.最终目标的生成

    根据输入变量的不同,最终的输出可能是一个DTA(去除了interpreter的pie)或DTA静态库(.a)或DTA动态库(.so),其规则如下:

## SDK/mk/ta_dev_kit.mk
## 如果要编译DTA
ifneq ($(binary),)
## 链接 这里用的是 ./ta/arch/arm/link.mk, 这里就不展开了,最关键的就是 -pie 然后配合ta.ld.S生成一个没有interpreter的 pie文件
include  $(ta-dev-kit-dir)/mk/link.mk
endif

## 编译静态库
ifneq ($(libname),)
# Build target is static library
all: $(libname).a
cleanfiles += $(libname).a

## 静态库直接通过AR打包
$(libname).a: $(objs)
    @echo '  AR      $@'
    $(q)rm -f $@ && $(AR$(sm)) rcs -o $@ $^
endif

## 编译动态库
ifneq (,$(shlibname))
## 这里同样不展开了,最终实际上是指定了-shared 生成了一个dll文件
include $(ta-dev-kit-dir)/mk/link_shlib.mk
endif

​注:

[1]  这里对动态链接库(dll)的写法稍作说明:

  通常来说linux里面的动态链接库我们称为so文件(或DSO),windows中称为dll文件,而在ld源码中有如下代码:

./binutil/include/bfdlink.h
enum output_type
{
  type_pde,                //Position Dependent Executable,  地址相关的可执行文件
  type_pie,                //Position Independent Executable, 地址无关的可执行文件
  type_relocatable,        //可重定位文件/目标文件/*.o文件
  type_dll,                //Dynamic Link Library, 动态链接库文件
};
​
#define bfd_link_pde(info)       ((info)->type == type_pde)                        //地址相关可执行文件,后面称为pde
#define bfd_link_dll(info)       ((info)->type == type_dll)                        //动态链接库文件,后面称为 dll
#define bfd_link_relocatable(info) ((info)->type == type_relocatable)              //可重定位文件, 后面称为relocatable
#define bfd_link_pie(info)       ((info)->type == type_pie)                        //地址无关可执行文件, 后面称为pie
#define bfd_link_executable(info)  (bfd_link_pde (info) || bfd_link_pie (info))    //可执行文件,包括地址无关和地址相关的可执行文件,后面称为exe
#define bfd_link_pic(info)       (bfd_link_dll (info) || bfd_link_pie (info))      //地址无关代码,包括动态链接库文件和地址无关可执行文件, 后面称为pic

按照链接器的分配可执行文件可以分为pde/pie/reloc/dll,这里的dll便是此意,实际上也就是我们通常说的.so/DSO.

### OP-TEE 部署指南 对于希望部署OP-TEE的操作,理解其环境配置与具体流程至关重要。OP-TEEOpen Portable Trusted Execution Environment)是一个开源项目,旨在提供一个可移植的信任执行环境解决方案[^1]。 #### 准备工作 为了成功安装并运行OP-TEE,在开始之前需准备合适的硬件平台和支持该技术的处理器架构。通常情况下,ARM Cortex-A系列是最常见的选择之一。此外,还需要确保拥有必要的开发工具链以及熟悉Linux操作系统下的编译构建过程[^2]。 #### 获取源码 可以从官方GitHub仓库克隆最新的OP-TEE源代码库来获取最新版本: ```bash git clone https://github.com/OP-TEE/optee_os.git cd optee_os ``` 这一步骤提供了访问所有必需文件的机会,包括但不限于内核补丁、用户空间API头文件等资源[^3]。 #### 编译设置 完成上述操作之后,则可以按照特定目标板的要求调整Makefile中的选项参数,并通过调用`make`命令来进行整个系统的编译工作。例如针对QEMU模拟器环境: ```bash make PLATFORM=vexpress-qemu V=1 all ``` 这里选择了vexpress-qemu作为演示用途的目标平台;实际应用时应依据所使用的设备型号做相应更改[^4]。 #### 安装固件 一旦编译顺利完成,下一步就是将生成好的二进制映像烧录到对应的存储介质上。此过程中可能涉及到刷写引导加载程序(bootloader),更新TrustZone软件包等内容。具体的实施方法依赖于制造商提供的文档说明或者社区分享的经验贴士[^5]。 #### 测试验证 最后但同样重要的是,在物理机或虚拟环境中启动系统后应当立即开展一系列基础功能测试以确认一切正常运作。可以通过运行预置的应用实例或是编写简单的TA(Test Application)来进行初步检验[^6]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值