aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ldelf/ta_elf.c213
-rw-r--r--ldelf/ta_elf.h1
-rw-r--r--lib/libdl/dlfcn.c3
-rw-r--r--lib/libutee/arch/arm/user_ta_entry.c38
-rw-r--r--lib/libutee/include/user_ta_header.h47
-rw-r--r--ta/arch/arm/link.mk13
6 files changed, 310 insertions, 5 deletions
diff --git a/ldelf/ta_elf.c b/ldelf/ta_elf.c
index 733592e4..5c2a2566 100644
--- a/ldelf/ta_elf.c
+++ b/ldelf/ta_elf.c
@@ -913,9 +913,14 @@ void ta_elf_load_main(const TEE_UUID *uuid, uint32_t *is_32bit, uint64_t *sp,
void ta_elf_finalize_load_main(uint64_t *entry)
{
struct ta_elf *elf = TAILQ_FIRST(&main_elf_queue);
+ TEE_Result res = TEE_SUCCESS;
assert(elf->is_main);
+ res = ta_elf_set_init_fini_info(elf->is_32bit);
+ if (res)
+ err(res, "ta_elf_set_init_fini_info");
+
if (elf->is_legacy)
*entry = elf->head->depr_entry;
else
@@ -1193,5 +1198,213 @@ TEE_Result ta_elf_add_library(const TEE_UUID *uuid)
DMSG("ELF (%pUl) at %#"PRIxVA,
(void *)&elf->uuid, elf->load_addr);
+ return ta_elf_set_init_fini_info(ta->is_32bit);
+}
+
+/* Get address/size of .init_array and .fini_array from the dynamic segment */
+static void get_init_fini_array(struct ta_elf *elf, unsigned int type,
+ vaddr_t addr, size_t memsz, vaddr_t *init,
+ size_t *init_cnt, vaddr_t *fini,
+ size_t *fini_cnt)
+{
+ size_t addrsz = 0;
+ size_t dyn_entsize = 0;
+ size_t num_dyns = 0;
+ size_t n = 0;
+ unsigned int tag = 0;
+ size_t val = 0;
+
+ assert(type == PT_DYNAMIC);
+
+ if (elf->is_32bit) {
+ dyn_entsize = sizeof(Elf32_Dyn);
+ addrsz = 4;
+ } else {
+ dyn_entsize = sizeof(Elf64_Dyn);
+ addrsz = 8;
+ }
+
+ assert(!(memsz % dyn_entsize));
+ num_dyns = memsz / dyn_entsize;
+
+ for (n = 0; n < num_dyns; n++) {
+ read_dyn(elf, addr, n, &tag, &val);
+ if (tag == DT_INIT_ARRAY)
+ *init = val + elf->load_addr;
+ else if (tag == DT_FINI_ARRAY)
+ *fini = val + elf->load_addr;
+ else if (tag == DT_INIT_ARRAYSZ)
+ *init_cnt = val / addrsz;
+ else if (tag == DT_FINI_ARRAYSZ)
+ *fini_cnt = val / addrsz;
+ }
+}
+
+/* Get address/size of .init_array and .fini_array in @elf (if present) */
+static void elf_get_init_fini_array(struct ta_elf *elf, vaddr_t *init,
+ size_t *init_cnt, vaddr_t *fini,
+ size_t *fini_cnt)
+{
+ size_t n = 0;
+
+ if (elf->is_32bit) {
+ Elf32_Phdr *phdr = elf->phdr;
+
+ for (n = 0; n < elf->e_phnum; n++) {
+ if (phdr[n].p_type == PT_DYNAMIC) {
+ get_init_fini_array(elf, phdr[n].p_type,
+ phdr[n].p_vaddr,
+ phdr[n].p_memsz,
+ init, init_cnt, fini,
+ fini_cnt);
+ return;
+ }
+ }
+ } else {
+ Elf64_Phdr *phdr = elf->phdr;
+
+ for (n = 0; n < elf->e_phnum; n++) {
+ if (phdr[n].p_type == PT_DYNAMIC) {
+ get_init_fini_array(elf, phdr[n].p_type,
+ phdr[n].p_vaddr,
+ phdr[n].p_memsz,
+ init, init_cnt, fini,
+ fini_cnt);
+ return;
+ }
+ }
+ }
+}
+
+static TEE_Result realloc_ifs(vaddr_t va, size_t cnt, bool is_32bit)
+{
+ struct __init_fini_info32 *info32 = (struct __init_fini_info32 *)va;
+ struct __init_fini_info *info = (struct __init_fini_info *)va;
+ struct __init_fini32 *ifs32 = NULL;
+ struct __init_fini *ifs = NULL;
+ size_t prev_cnt = 0;
+ void *ptr = NULL;
+
+ if (is_32bit) {
+ ptr = (void *)(vaddr_t)info32->ifs;
+ ptr = realloc(ptr, cnt * sizeof(struct __init_fini32));
+ if (!ptr)
+ return TEE_ERROR_OUT_OF_MEMORY;
+ ifs32 = ptr;
+ prev_cnt = info32->size;
+ if (cnt > prev_cnt)
+ memset(ifs32 + prev_cnt, 0,
+ (cnt - prev_cnt) * sizeof(*ifs32));
+ info32->ifs = (uint32_t)(vaddr_t)ifs32;
+ info32->size = cnt;
+ } else {
+ ptr = realloc(info->ifs, cnt * sizeof(struct __init_fini));
+ if (!ptr)
+ return TEE_ERROR_OUT_OF_MEMORY;
+ ifs = ptr;
+ prev_cnt = info->size;
+ if (cnt > prev_cnt)
+ memset(ifs + prev_cnt, 0,
+ (cnt - prev_cnt) * sizeof(*ifs));
+ info->ifs = ifs;
+ info->size = cnt;
+ }
+
+ return TEE_SUCCESS;
+}
+
+static void fill_ifs(vaddr_t va, size_t idx, struct ta_elf *elf, bool is_32bit)
+{
+ struct __init_fini_info32 *info32 = (struct __init_fini_info32 *)va;
+ struct __init_fini_info *info = (struct __init_fini_info *)va;
+ struct __init_fini32 *ifs32 = NULL;
+ struct __init_fini *ifs = NULL;
+ size_t init_cnt = 0;
+ size_t fini_cnt = 0;
+ vaddr_t init = 0;
+ vaddr_t fini = 0;
+
+ if (is_32bit) {
+ assert(idx < info32->size);
+ ifs32 = &((struct __init_fini32 *)(vaddr_t)info32->ifs)[idx];
+
+ if (ifs32->flags & __IFS_VALID)
+ return;
+
+ elf_get_init_fini_array(elf, &init, &init_cnt, &fini,
+ &fini_cnt);
+
+ ifs32->init = (uint32_t)init;
+ ifs32->init_size = init_cnt;
+
+ ifs32->fini = (uint32_t)fini;
+ ifs32->fini_size = fini_cnt;
+
+ ifs32->flags |= __IFS_VALID;
+ } else {
+ assert(idx < info->size);
+ ifs = &info->ifs[idx];
+
+ if (ifs->flags & __IFS_VALID)
+ return;
+
+ elf_get_init_fini_array(elf, &init, &init_cnt, &fini,
+ &fini_cnt);
+
+ ifs->init = (void (**)(void))init;
+ ifs->init_size = init_cnt;
+
+ ifs->fini = (void (**)(void))fini;
+ ifs->fini_size = fini_cnt;
+
+ ifs->flags |= __IFS_VALID;
+ }
+}
+
+/*
+ * Set or update __init_fini_info in the TA with information from the ELF
+ * queue
+ */
+TEE_Result ta_elf_set_init_fini_info(bool is_32bit)
+{
+ struct __init_fini_info *info = NULL;
+ TEE_Result res = TEE_SUCCESS;
+ struct ta_elf *elf = NULL;
+ vaddr_t info_va = 0;
+ size_t cnt = 0;
+
+ res = ta_elf_resolve_sym("__init_fini_info", &info_va, NULL);
+ if (res) {
+ if (res == TEE_ERROR_ITEM_NOT_FOUND) {
+ /* Older TA */
+ return TEE_SUCCESS;
+ }
+ return res;
+ }
+ assert(info_va);
+
+ info = (struct __init_fini_info *)info_va;
+ if (info->reserved)
+ return TEE_ERROR_NOT_SUPPORTED;
+
+ TAILQ_FOREACH(elf, &main_elf_queue, link)
+ cnt++;
+
+ /* Queue has at least one file (main) */
+ assert(cnt);
+
+ res = realloc_ifs(info_va, cnt, is_32bit);
+ if (res)
+ goto err;
+
+ cnt = 0;
+ TAILQ_FOREACH(elf, &main_elf_queue, link) {
+ fill_ifs(info_va, cnt, elf, is_32bit);
+ cnt++;
+ }
+
return TEE_SUCCESS;
+err:
+ free(info);
+ return res;
}
diff --git a/ldelf/ta_elf.h b/ldelf/ta_elf.h
index 373a2da0..7e4ad644 100644
--- a/ldelf/ta_elf.h
+++ b/ldelf/ta_elf.h
@@ -104,5 +104,6 @@ static inline void ta_elf_stack_trace_a64(uint64_t fp __unused,
TEE_Result ta_elf_resolve_sym(const char *name, vaddr_t *val,
struct ta_elf *elf);
TEE_Result ta_elf_add_library(const TEE_UUID *uuid);
+TEE_Result ta_elf_set_init_fini_info(bool is_32bit);
#endif /*TA_ELF_H*/
diff --git a/lib/libdl/dlfcn.c b/lib/libdl/dlfcn.c
index b2b076e8..d1d7e31a 100644
--- a/lib/libdl/dlfcn.c
+++ b/lib/libdl/dlfcn.c
@@ -10,6 +10,7 @@
#include <string.h>
#include <tee_api.h>
#include <tee_internal_api_extensions.h>
+#include <user_ta_header.h>
static TEE_TASessionHandle sess = TEE_HANDLE_NULL;
static size_t hcount;
@@ -63,6 +64,8 @@ void *dlopen(const char *filename, int flags)
res = invoke_system_pta(PTA_SYSTEM_DLOPEN, param_types, params);
if (res)
goto err;
+
+ __utee_call_elf_init_fn();
}
hcount++;
diff --git a/lib/libutee/arch/arm/user_ta_entry.c b/lib/libutee/arch/arm/user_ta_entry.c
index 41ff4d8e..b317bb30 100644
--- a/lib/libutee/arch/arm/user_ta_entry.c
+++ b/lib/libutee/arch/arm/user_ta_entry.c
@@ -33,6 +33,42 @@ extern struct ta_head ta_head;
uint32_t ta_param_types;
TEE_Param ta_params[TEE_NUM_PARAMS];
+struct __init_fini_info __init_fini_info;
+
+void __utee_call_elf_init_fn(void)
+{
+ size_t n = __init_fini_info.size;
+ size_t i = 0;
+ size_t j = 0;
+
+ for (i = 1; i <= n; i++) {
+ /* Reverse order: dependencies first */
+ struct __init_fini *ifs = &__init_fini_info.ifs[n - i];
+
+ if (!(ifs->flags & __IFS_INIT_HAS_RUN))
+ for (j = 0; j < ifs->init_size; j++)
+ ifs->init[j]();
+
+ ifs->flags |= __IFS_INIT_HAS_RUN;
+ }
+}
+
+void __utee_call_elf_fini_fn(void)
+{
+ size_t n = __init_fini_info.size;
+ size_t i = 0;
+ size_t j = 0;
+
+ for (i = 0; i < n; i++) {
+ struct __init_fini *ifs = &__init_fini_info.ifs[i];
+
+ if (!(ifs->flags & __IFS_FINI_HAS_RUN))
+ for (j = 1; j <= ifs->fini_size; j++)
+ ifs->fini[ifs->fini_size - j]();
+
+ ifs->flags |= __IFS_FINI_HAS_RUN;
+ }
+}
static TEE_Result init_instance(void)
{
@@ -40,6 +76,7 @@ static TEE_Result init_instance(void)
__utee_gprof_init();
malloc_add_pool(ta_heap, ta_heap_size);
_TEE_MathAPI_Init();
+ __utee_call_elf_init_fn();
return TA_CreateEntryPoint();
}
@@ -47,6 +84,7 @@ static void uninit_instance(void)
{
__utee_gprof_fini();
TA_DestroyEntryPoint();
+ __utee_call_elf_fini_fn();
}
static void ta_header_save_params(uint32_t param_types,
diff --git a/lib/libutee/include/user_ta_header.h b/lib/libutee/include/user_ta_header.h
index f9e7c071..6f81fd64 100644
--- a/lib/libutee/include/user_ta_header.h
+++ b/lib/libutee/include/user_ta_header.h
@@ -73,6 +73,53 @@ unsigned long ftrace_return(void);
void __ftrace_return(void);
#endif
+/*
+ * Pointers to ELF initialization and finalization functions are extracted by
+ * ldelf and stored on the TA heap. They can be accessed via the TA global
+ * variable __init_fini_info::ifs, but the functions are meant to called via
+ * __utee_call_elf_init_fn() and __utee_call_elf_fini_fn().
+ */
+
+struct __init_fini {
+ uint32_t flags;
+ uint16_t init_size;
+ uint16_t fini_size;
+
+ void (**init)(void); /* @init_size entries */
+ void (**fini)(void); /* @fini_size entries */
+};
+
+#define __IFS_VALID BIT(0)
+#define __IFS_INIT_HAS_RUN BIT(1)
+#define __IFS_FINI_HAS_RUN BIT(2)
+
+struct __init_fini_info {
+ uint32_t reserved;
+ uint16_t size;
+ uint16_t pad;
+ struct __init_fini *ifs; /* @size entries */
+};
+
+/* 32-bit variants for a 64-bit ldelf to access a 32-bit TA */
+
+struct __init_fini32 {
+ uint32_t flags;
+ uint16_t init_size;
+ uint16_t fini_size;
+ uint32_t init;
+ uint32_t fini;
+};
+
+struct __init_fini_info32 {
+ uint32_t reserved;
+ uint16_t size;
+ uint16_t pad;
+ uint32_t ifs;
+};
+
+void __utee_call_elf_init_fn(void);
+void __utee_call_elf_fini_fn(void);
+
#define TA_PROP_STR_SINGLE_INSTANCE "gpd.ta.singleInstance"
#define TA_PROP_STR_MULTI_SESSION "gpd.ta.multiSession"
#define TA_PROP_STR_KEEP_ALIVE "gpd.ta.instanceKeepAlive"
diff --git a/ta/arch/arm/link.mk b/ta/arch/arm/link.mk
index c33f6d15..9c98fe1c 100644
--- a/ta/arch/arm/link.mk
+++ b/ta/arch/arm/link.mk
@@ -34,15 +34,18 @@ link-ldflags += -z max-page-size=4096 # OP-TEE always uses 4K alignment
link-ldflags += --as-needed # Do not add dependency on unused shlib
link-ldflags += $(link-ldflags$(sm))
-ifeq ($(CFG_FTRACE_SUPPORT),y)
$(link-out-dir$(sm))/dyn_list:
@$(cmd-echo-silent) ' GEN $@'
$(q)mkdir -p $(dir $@)
- $(q)echo "{__ftrace_info;};" >$@
+ $(q)echo "{" >$@
+ $(q)echo "__init_fini_info;" >>$@
+ifeq ($(CFG_FTRACE_SUPPORT),y)
+ $(q)echo "__ftrace_info;" >>$@
+endif
+ $(q)echo "};" >>$@
link-ldflags += --dynamic-list $(link-out-dir$(sm))/dyn_list
-ftracedep = $(link-out-dir$(sm))/dyn_list
+dynlistdep = $(link-out-dir$(sm))/dyn_list
cleanfiles += $(link-out-dir$(sm))/dyn_list
-endif
link-ldadd = $(user-ta-ldadd) $(addprefix -L,$(libdirs))
link-ldadd += --start-group $(addprefix -l,$(libnames)) --end-group
@@ -68,7 +71,7 @@ $(link-script-pp$(sm)): $(link-script$(sm)) $(conf-file) $(link-script-pp-makefi
$(link-out-dir$(sm))/$(user-ta-uuid).elf: $(objs) $(libdeps) \
$(link-script-pp$(sm)) \
- $(ftracedep) \
+ $(dynlistdep) \
$(additional-link-deps)
@$(cmd-echo-silent) ' LD $$@'
$(q)$(LD$(sm)) $(ldargs-$(user-ta-uuid).elf) -o $$@