aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJerome Forissier <jerome@forissier.org>2020-07-21 10:54:59 +0200
committerJérôme Forissier <jerome@forissier.org>2020-08-06 11:10:44 +0200
commitfe6849487f7845d474828af162ddd882cbd3bb99 (patch)
tree52fe13a1c8362b8626eeaddd9f99afd47906708f
parentc88ba1258463bfa946499d2caa9341115b2fa849 (diff)
ldelf: arm64: support R_AARCH64_TLS_TPREL relocations
Preparing for C++ support in TAs. This commit adds support for relocation type R_AARCH64_TLS_TPREL. Although OP-TEE does not support multi-threaded TAs, introducing basic support for multi-threading will allow binaries generated by the aarch64-linux-gnueabihf-g++ compiler to work properly. Indeed, this compiler is configured for a multi-threadded environment by default. The way R_AARCH64_TLS_TPREL works is simple: the runtime library needs to keep a per-thread copy of all the TLS segments in the application. When the compiler needs to access a thread-specific symbol, it emits this relocation. The loader will then replace the value by the offset of the desired symbol in the Thread Control Block of the current thread (the address of the TCB is obtained via the TPIDR_EL0 register). The runtime code that sets up the TCB from the TLS segments will come in a later commit. 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/include/elf_common.h1
-rw-r--r--ldelf/ta_elf.c30
-rw-r--r--ldelf/ta_elf.h9
-rw-r--r--ldelf/ta_elf_rel.c36
4 files changed, 69 insertions, 7 deletions
diff --git a/ldelf/include/elf_common.h b/ldelf/include/elf_common.h
index da18d395..ca59a46d 100644
--- a/ldelf/include/elf_common.h
+++ b/ldelf/include/elf_common.h
@@ -656,6 +656,7 @@ typedef struct {
#define R_AARCH64_GLOB_DAT 1025 /* Set GOT entry to data address. */
#define R_AARCH64_JUMP_SLOT 1026 /* Set GOT entry to code address. */
#define R_AARCH64_RELATIVE 1027 /* Add load address of shared object. */
+#define R_AARCH64_TLS_TPREL 1030 /* Offset of the TLS block in the TCB */
#define R_ARM_NONE 0 /* No relocation. */
#define R_ARM_PC24 1
diff --git a/ldelf/ta_elf.c b/ldelf/ta_elf.c
index bc3f916c..cbeffd86 100644
--- a/ldelf/ta_elf.c
+++ b/ldelf/ta_elf.c
@@ -451,11 +451,16 @@ static void parse_load_segments(struct ta_elf *elf)
Elf64_Phdr *phdr = elf->phdr;
for (n = 0; n < elf->e_phnum; n++)
- if (phdr[n].p_type == PT_LOAD)
+ if (phdr[n].p_type == PT_LOAD) {
add_segment(elf, phdr[n].p_offset,
phdr[n].p_vaddr, phdr[n].p_filesz,
phdr[n].p_memsz, phdr[n].p_flags,
phdr[n].p_align);
+ } else if (phdr[n].p_type == PT_TLS) {
+ elf->tls_start = phdr[n].p_vaddr;
+ elf->tls_filesz = phdr[n].p_filesz;
+ elf->tls_memsz = phdr[n].p_memsz;
+ }
}
}
@@ -959,6 +964,27 @@ static void clean_elf_load_main(struct ta_elf *elf)
TAILQ_INIT(&elf->segs);
}
+#ifdef ARM64
+/*
+ * Allocates an offset in the TA's Thread Control Block for the TLS segment of
+ * the @elf module.
+ */
+#define TCB_HEAD_SIZE (2 * sizeof(long))
+static void set_tls_offset(struct ta_elf *elf)
+{
+ static size_t next_offs = TCB_HEAD_SIZE;
+
+ if (!elf->tls_start)
+ return;
+
+ /* Module has a TLS segment */
+ elf->tls_tcb_offs = next_offs;
+ next_offs += elf->tls_memsz;
+}
+#else
+static void set_tls_offset(struct ta_elf *elf __unused) {}
+#endif
+
static void load_main(struct ta_elf *elf)
{
init_elf(elf);
@@ -968,6 +994,7 @@ static void load_main(struct ta_elf *elf)
copy_section_headers(elf);
save_symtab(elf);
close_handle(elf);
+ set_tls_offset(elf);
elf->head = (struct ta_head *)elf->load_addr;
if (elf->head->depr_entry != UINT64_MAX) {
@@ -1071,6 +1098,7 @@ void ta_elf_load_dependency(struct ta_elf *elf, bool is_32bit)
copy_section_headers(elf);
save_symtab(elf);
close_handle(elf);
+ set_tls_offset(elf);
}
void ta_elf_finalize_mappings(struct ta_elf *elf)
diff --git a/ldelf/ta_elf.h b/ldelf/ta_elf.h
index db3e1648..316ecf23 100644
--- a/ldelf/ta_elf.h
+++ b/ldelf/ta_elf.h
@@ -65,7 +65,16 @@ struct ta_elf {
size_t exidx_size;
/* Thread Local Storage */
+
size_t tls_mod_id;
+ /* PT_TLS segment */
+ vaddr_t tls_start;
+ size_t tls_filesz; /* Covers the .tdata section */
+ size_t tls_memsz; /* Covers the .tdata and .tbss sections */
+#ifdef ARM64
+ /* Offset of the copy of the TLS block in the TLS area of the TCB */
+ size_t tls_tcb_offs;
+#endif
uint32_t handle;
diff --git a/ldelf/ta_elf_rel.c b/ldelf/ta_elf_rel.c
index 0aa1b958..a757dda1 100644
--- a/ldelf/ta_elf_rel.c
+++ b/ldelf/ta_elf_rel.c
@@ -382,13 +382,11 @@ static void e32_relocate(struct ta_elf *elf, unsigned int rel_sidx)
}
#ifdef ARM64
-static void e64_process_dyn_rela(const Elf64_Sym *sym_tab, size_t num_syms,
- const char *str_tab, size_t str_tab_size,
- Elf64_Rela *rela, Elf64_Addr *where)
+static void e64_get_sym_name(const Elf64_Sym *sym_tab, size_t num_syms,
+ const char *str_tab, size_t str_tab_size,
+ Elf64_Rela *rela, const char **name)
{
size_t sym_idx = 0;
- const char *name = NULL;
- uintptr_t val = 0;
size_t name_idx = 0;
sym_idx = ELF64_R_SYM(rela->r_info);
@@ -399,12 +397,34 @@ static void e64_process_dyn_rela(const Elf64_Sym *sym_tab, size_t 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;
+ *name = str_tab + name_idx;
+}
+
+static void e64_process_dyn_rela(const Elf64_Sym *sym_tab, size_t num_syms,
+ const char *str_tab, size_t str_tab_size,
+ Elf64_Rela *rela, Elf64_Addr *where)
+{
+ const char *name = NULL;
+ uintptr_t val = 0;
+ e64_get_sym_name(sym_tab, num_syms, str_tab, str_tab_size, rela, &name);
resolve_sym(name, &val, NULL);
*where = val;
}
+static void e64_process_tls_rela(const Elf64_Sym *sym_tab, size_t num_syms,
+ const char *str_tab, size_t str_tab_size,
+ Elf64_Rela *rela, Elf64_Addr *where)
+{
+ struct ta_elf *mod = NULL;
+ const char *name = NULL;
+ vaddr_t symval = 0;
+
+ e64_get_sym_name(sym_tab, num_syms, str_tab, str_tab_size, rela, &name);
+ ta_elf_resolve_sym(name, &symval, &mod, NULL, NULL);
+ *where = symval + mod->tls_tcb_offs + rela->r_addend;
+}
+
static void e64_relocate(struct ta_elf *elf, unsigned int rel_sidx)
{
Elf64_Shdr *shdr = elf->shdr;
@@ -509,6 +529,10 @@ static void e64_relocate(struct ta_elf *elf, unsigned int rel_sidx)
e64_process_dyn_rela(sym_tab, num_syms, str_tab,
str_tab_size, rela, where);
break;
+ case R_AARCH64_TLS_TPREL:
+ e64_process_tls_rela(sym_tab, num_syms, str_tab,
+ str_tab_size, rela, where);
+ break;
default:
err(TEE_ERROR_BAD_FORMAT, "Unknown relocation type %zd",
ELF64_R_TYPE(rela->r_info));