aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJerome Forissier <jerome@forissier.org>2020-07-21 10:54:57 +0200
committerJérôme Forissier <jerome@forissier.org>2020-08-06 11:10:44 +0200
commitc88ba1258463bfa946499d2caa9341115b2fa849 (patch)
tree43deefdadd98fb4348569ce58879a96a7cdf8879
parentcf830b2b674ccc672d0124425714da7cae27c303 (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.c3
-rw-r--r--ldelf/ftrace.c2
-rw-r--r--ldelf/ta_elf.c17
-rw-r--r--ldelf/ta_elf.h5
-rw-r--r--ldelf/ta_elf_rel.c101
5 files changed, 105 insertions, 23 deletions
diff --git a/ldelf/dl.c b/ldelf/dl.c
index a5c52c90..6e737bfe 100644
--- a/ldelf/dl.c
+++ b/ldelf/dl.c
@@ -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;
}