diff options
author | Jerome Forissier <jerome@forissier.org> | 2020-07-21 10:54:57 +0200 |
---|---|---|
committer | Jérôme Forissier <jerome@forissier.org> | 2020-08-06 11:10:44 +0200 |
commit | c88ba1258463bfa946499d2caa9341115b2fa849 (patch) | |
tree | 43deefdadd98fb4348569ce58879a96a7cdf8879 | |
parent | cf830b2b674ccc672d0124425714da7cae27c303 (diff) |
ldelf: arm: support R_ARM_TLS_DTPMOD32 and R_ARM_TLS_DTPOFF32 relocations
Preparing for C++ support in TAs.
This commit adds support for Thread Local Storage (TLS) relocation
types R_ARM_TLS_DTPMOD32 and R_ARM_TLS_DTPOFF32.
OP-TEE does not support multi-threaded TAs so in principle there is no
need to handle the TLS relocations. However, this commit will allow to
run C++ TAs built with the "official" arm-linux-gnueabihf compiler
(which is built with threading support enabled), as long as no
multi-thread feature is explicitly used by the TA. In other words, it
avoids the need to re-build a toolchain with --disable-threads.
Signed-off-by: Jerome Forissier <jerome@forissier.org>
Acked-by: Etienne Carriere <etienne.carriere@linaro.org>
Reviewed-by: Jens Wiklander <jens.wiklander@linaro.org>
-rw-r--r-- | ldelf/dl.c | 3 | ||||
-rw-r--r-- | ldelf/ftrace.c | 2 | ||||
-rw-r--r-- | ldelf/ta_elf.c | 17 | ||||
-rw-r--r-- | ldelf/ta_elf.h | 5 | ||||
-rw-r--r-- | ldelf/ta_elf_rel.c | 101 |
5 files changed, 105 insertions, 23 deletions
@@ -32,6 +32,7 @@ TEE_Result dlsym_entry(struct dl_entry_arg *arg) return TEE_ERROR_ITEM_NOT_FOUND; } - return ta_elf_resolve_sym(arg->dlsym.symbol, &arg->dlsym.val, elf); + return ta_elf_resolve_sym(arg->dlsym.symbol, &arg->dlsym.val, NULL, + elf); } diff --git a/ldelf/ftrace.c b/ldelf/ftrace.c index 4aa1116c..934bf43b 100644 --- a/ldelf/ftrace.c +++ b/ldelf/ftrace.c @@ -26,7 +26,7 @@ bool ftrace_init(struct ftrace_buf **fbuf_ptr) int count = 0; size_t fbuf_size = 0; - res = ta_elf_resolve_sym("__ftrace_info", &val, NULL); + res = ta_elf_resolve_sym("__ftrace_info", &val, NULL, NULL); if (res) return false; diff --git a/ldelf/ta_elf.c b/ldelf/ta_elf.c index 4ee37d1a..bc3f916c 100644 --- a/ldelf/ta_elf.c +++ b/ldelf/ta_elf.c @@ -29,6 +29,19 @@ static vaddr_t ta_stack_size; struct ta_elf_queue main_elf_queue = TAILQ_HEAD_INITIALIZER(main_elf_queue); +/* + * Main application is always ID 1, shared libraries with TLS take IDs 2 and + * above + */ +static void assign_tls_mod_id(struct ta_elf *elf) +{ + static size_t last_tls_mod_id = 1; + + if (elf->is_main) + assert(last_tls_mod_id == 1); /* Main always comes first */ + elf->tls_mod_id = last_tls_mod_id++; +} + static struct ta_elf *queue_elf_helper(const TEE_UUID *uuid) { struct ta_elf *elf = calloc(1, sizeof(*elf)); @@ -431,6 +444,8 @@ static void parse_load_segments(struct ta_elf *elf) } else if (phdr[n].p_type == PT_ARM_EXIDX) { elf->exidx_start = phdr[n].p_vaddr; elf->exidx_size = phdr[n].p_filesz; + } else if (phdr[n].p_type == PT_TLS) { + assign_tls_mod_id(elf); } } else { Elf64_Phdr *phdr = elf->phdr; @@ -1486,7 +1501,7 @@ TEE_Result ta_elf_set_init_fini_info(bool is_32bit) vaddr_t info_va = 0; size_t cnt = 0; - res = ta_elf_resolve_sym("__init_fini_info", &info_va, NULL); + res = ta_elf_resolve_sym("__init_fini_info", &info_va, NULL, NULL); if (res) { if (res == TEE_ERROR_ITEM_NOT_FOUND) { /* Older TA */ diff --git a/ldelf/ta_elf.h b/ldelf/ta_elf.h index 7e4ad644..db3e1648 100644 --- a/ldelf/ta_elf.h +++ b/ldelf/ta_elf.h @@ -64,6 +64,9 @@ struct ta_elf { vaddr_t exidx_start; size_t exidx_size; + /* Thread Local Storage */ + size_t tls_mod_id; + uint32_t handle; struct ta_head *head; @@ -102,7 +105,7 @@ static inline void ta_elf_stack_trace_a64(uint64_t fp __unused, #endif /*CFG_UNWIND*/ TEE_Result ta_elf_resolve_sym(const char *name, vaddr_t *val, - struct ta_elf *elf); + struct ta_elf **found_elf, 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); diff --git a/ldelf/ta_elf_rel.c b/ldelf/ta_elf_rel.c index 3822f4c7..0aa1b958 100644 --- a/ldelf/ta_elf_rel.c +++ b/ldelf/ta_elf_rel.c @@ -57,14 +57,19 @@ static bool __resolve_sym(struct ta_elf *elf, unsigned int st_bind, if (st_shndx == SHN_UNDEF || st_shndx == SHN_XINDEX) return false; - if (st_value > (elf->max_addr - elf->load_addr)) - err(TEE_ERROR_BAD_FORMAT, "Symbol location out of range"); - switch (st_type) { case STT_NOTYPE: case STT_OBJECT: case STT_FUNC: - *val = st_value + elf->load_addr; + if (st_value > (elf->max_addr - elf->load_addr)) + err(TEE_ERROR_BAD_FORMAT, + "Symbol location out of range"); + if (val) + *val = st_value + elf->load_addr; + break; + case STT_TLS: + if (val) + *val = st_value; break; default: err(TEE_ERROR_NOT_SUPPORTED, "Symbol type not supported"); @@ -142,8 +147,11 @@ static TEE_Result resolve_sym_helper(uint32_t hash, const char *name, * are searched first, then weak ones. Last option, when at least one weak but * undefined symbol exists, resolve to zero. Otherwise return * TEE_ERROR_ITEM_NOT_FOUND. + * @val (if != 0) receives the symbol value + * @found_elf (if != 0) receives the module where the symbol is found */ TEE_Result ta_elf_resolve_sym(const char *name, vaddr_t *val, + struct ta_elf **found_elf, struct ta_elf *elf) { uint32_t hash = elf_hash(name); @@ -152,28 +160,51 @@ TEE_Result ta_elf_resolve_sym(const char *name, vaddr_t *val, /* Search global symbols */ if (!resolve_sym_helper(hash, name, val, elf, false /* !weak_ok */)) - return TEE_SUCCESS; + goto success; /* Search weak symbols */ if (!resolve_sym_helper(hash, name, val, elf, true /* weak_ok */)) - return TEE_SUCCESS; + goto success; } TAILQ_FOREACH(elf, &main_elf_queue, link) { if (!resolve_sym_helper(hash, name, val, elf, false /* !weak_ok */)) - return TEE_SUCCESS; + goto success; if (!resolve_sym_helper(hash, name, val, elf, true /* weak_ok */)) - return TEE_SUCCESS; + goto success; } return TEE_ERROR_ITEM_NOT_FOUND; + +success: + if (found_elf) + *found_elf = elf; + return TEE_SUCCESS; } -static void resolve_sym(const char *name, vaddr_t *val) +static void e32_get_sym_name(const Elf32_Sym *sym_tab, size_t num_syms, + const char *str_tab, size_t str_tab_size, + Elf32_Rel *rel, const char **name) { - TEE_Result res = ta_elf_resolve_sym(name, val, NULL); + size_t sym_idx = 0; + size_t name_idx = 0; + + sym_idx = ELF32_R_SYM(rel->r_info); + if (sym_idx >= num_syms) + err(TEE_ERROR_BAD_FORMAT, "Symbol index out of range"); + sym_idx = confine_array_index(sym_idx, num_syms); + + name_idx = sym_tab[sym_idx].st_name; + if (name_idx >= str_tab_size) + err(TEE_ERROR_BAD_FORMAT, "Name index out of range"); + *name = str_tab + name_idx; +} + +static void resolve_sym(const char *name, vaddr_t *val, struct ta_elf **mod) +{ + TEE_Result res = ta_elf_resolve_sym(name, val, mod, NULL); if (res) err(res, "Symbol %s not found", name); @@ -183,23 +214,42 @@ static void e32_process_dyn_rel(const Elf32_Sym *sym_tab, size_t num_syms, const char *str_tab, size_t str_tab_size, Elf32_Rel *rel, Elf32_Addr *where) { - size_t sym_idx = 0; const char *name = NULL; vaddr_t val = 0; - size_t name_idx = 0; + + e32_get_sym_name(sym_tab, num_syms, str_tab, str_tab_size, rel, &name); + resolve_sym(name, &val, NULL); + *where = val; +} + +static void e32_tls_get_module(const Elf32_Sym *sym_tab, size_t num_syms, + const char *str_tab, size_t str_tab_size, + Elf32_Rel *rel, struct ta_elf **mod) +{ + const char *name = NULL; + size_t sym_idx = 0; sym_idx = ELF32_R_SYM(rel->r_info); if (sym_idx >= num_syms) err(TEE_ERROR_BAD_FORMAT, "Symbol index out of range"); sym_idx = confine_array_index(sym_idx, num_syms); + if (!sym_idx || sym_tab[sym_idx].st_shndx != SHN_UNDEF) { + /* No symbol, or symbol is defined in current module */ + return; + } - name_idx = sym_tab[sym_idx].st_name; - if (name_idx >= str_tab_size) - err(TEE_ERROR_BAD_FORMAT, "Name index out of range"); - name = str_tab + name_idx; + e32_get_sym_name(sym_tab, num_syms, str_tab, str_tab_size, rel, &name); + resolve_sym(name, NULL, mod); +} - resolve_sym(name, &val); - *where = val; +static void e32_tls_resolve(const Elf32_Sym *sym_tab, size_t num_syms, + const char *str_tab, size_t str_tab_size, + Elf32_Rel *rel, vaddr_t *val) +{ + const char *name = NULL; + + e32_get_sym_name(sym_tab, num_syms, str_tab, str_tab_size, rel, &name); + resolve_sym(name, val, NULL); } static void e32_relocate(struct ta_elf *elf, unsigned int rel_sidx) @@ -272,8 +322,10 @@ static void e32_relocate(struct ta_elf *elf, unsigned int rel_sidx) rel_end = rel + shdr[rel_sidx].sh_size / sizeof(Elf32_Rel); for (; rel < rel_end; rel++) { + struct ta_elf *mod = NULL; Elf32_Addr *where = NULL; size_t sym_idx = 0; + vaddr_t val = 0; /* Check the address is inside TA memory */ if (rel->r_offset >= (elf->max_addr - elf->load_addr)) @@ -311,6 +363,17 @@ static void e32_relocate(struct ta_elf *elf, unsigned int rel_sidx) e32_process_dyn_rel(sym_tab, num_syms, str_tab, str_tab_size, rel, where); break; + case R_ARM_TLS_DTPMOD32: + mod = elf; + e32_tls_get_module(sym_tab, num_syms, str_tab, + str_tab_size, rel, &mod); + *where = mod->tls_mod_id; + break; + case R_ARM_TLS_DTPOFF32: + e32_tls_resolve(sym_tab, num_syms, str_tab, + str_tab_size, rel, &val); + *where = val; + break; default: err(TEE_ERROR_BAD_FORMAT, "Unknown relocation type %d", ELF32_R_TYPE(rel->r_info)); @@ -338,7 +401,7 @@ static void e64_process_dyn_rela(const Elf64_Sym *sym_tab, size_t num_syms, err(TEE_ERROR_BAD_FORMAT, "Name index out of range"); name = str_tab + name_idx; - resolve_sym(name, &val); + resolve_sym(name, &val, NULL); *where = val; } |