aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJerome Forissier <jerome@forissier.org>2020-07-21 10:54:55 +0200
committerJérôme Forissier <jerome@forissier.org>2020-08-06 11:10:44 +0200
commitcf830b2b674ccc672d0124425714da7cae27c303 (patch)
treeb6ce35adad7450a0873a820fa498040c55c70a9e
parent405a5072d8bd25c18fd151d97e452d6863786f03 (diff)
ldelf: add support for weak symbols
Preparing for C++ support in TAs. When ldelf performs a symbol lookup in a TA, it currently considers only global symbols that are not undefined. It turns out that g++ can generates dynamic relocations referencing symbols that are weak and undefined [1], in other words: weak declarations. Those should end up resolving to zero. This commit updates the symbol resolution to first look for global defined symbols, then weak defined, then weak undefined. [1] __pthread_key_create, pthread_mutex_lock, pthread_mutex_unlock, __gnu_Unwind_Find_exidx. 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/ta_elf_rel.c54
1 files changed, 41 insertions, 13 deletions
diff --git a/ldelf/ta_elf_rel.c b/ldelf/ta_elf_rel.c
index 782d1682..3822f4c7 100644
--- a/ldelf/ta_elf_rel.c
+++ b/ldelf/ta_elf_rel.c
@@ -35,19 +35,27 @@ static uint32_t elf_hash(const char *name)
static bool __resolve_sym(struct ta_elf *elf, unsigned int st_bind,
unsigned int st_type, size_t st_shndx,
size_t st_name, size_t st_value, const char *name,
- vaddr_t *val)
+ vaddr_t *val, bool weak_ok)
{
- if (st_bind != STB_GLOBAL)
- return false;
- if (st_shndx == SHN_UNDEF || st_shndx == SHN_XINDEX)
- return false;
+ bool bind_ok = false;
+
if (!st_name)
return false;
if (st_name > elf->dynstr_size)
err(TEE_ERROR_BAD_FORMAT, "Symbol name out of range");
-
if (strcmp(name, elf->dynstr + st_name))
return false;
+ if (st_bind == STB_GLOBAL || (weak_ok && st_bind == STB_WEAK))
+ bind_ok = true;
+ if (!bind_ok)
+ return false;
+ if (st_bind == STB_WEAK && st_shndx == SHN_UNDEF) {
+ if (val)
+ *val = 0;
+ return true;
+ }
+ 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");
@@ -66,7 +74,8 @@ static bool __resolve_sym(struct ta_elf *elf, unsigned int st_bind,
}
static TEE_Result resolve_sym_helper(uint32_t hash, const char *name,
- vaddr_t *val, struct ta_elf *elf)
+ vaddr_t *val, struct ta_elf *elf,
+ bool weak_ok)
{
/*
* Using uint32_t here for convenience because both Elf64_Word
@@ -98,7 +107,7 @@ static TEE_Result resolve_sym_helper(uint32_t hash, const char *name,
ELF32_ST_TYPE(sym[n].st_info),
sym[n].st_shndx,
sym[n].st_name,
- sym[n].st_value, name, val))
+ sym[n].st_value, name, val, weak_ok))
return TEE_SUCCESS;
}
} else {
@@ -120,7 +129,7 @@ static TEE_Result resolve_sym_helper(uint32_t hash, const char *name,
ELF64_ST_TYPE(sym[n].st_info),
sym[n].st_shndx,
sym[n].st_name,
- sym[n].st_value, name, val))
+ sym[n].st_value, name, val, weak_ok))
return TEE_SUCCESS;
}
}
@@ -128,17 +137,36 @@ static TEE_Result resolve_sym_helper(uint32_t hash, const char *name,
return TEE_ERROR_ITEM_NOT_FOUND;
}
+/*
+ * Look for named symbol in @elf, or all modules if @elf == NULL. Global symbols
+ * 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.
+ */
TEE_Result ta_elf_resolve_sym(const char *name, vaddr_t *val,
struct ta_elf *elf)
{
uint32_t hash = elf_hash(name);
- if (elf)
- return resolve_sym_helper(hash, name, val, elf);
+ if (elf) {
+ /* Search global symbols */
+ if (!resolve_sym_helper(hash, name, val, elf,
+ false /* !weak_ok */))
+ return TEE_SUCCESS;
+ /* Search weak symbols */
+ if (!resolve_sym_helper(hash, name, val, elf,
+ true /* weak_ok */))
+ return TEE_SUCCESS;
+ }
- TAILQ_FOREACH(elf, &main_elf_queue, link)
- if (!resolve_sym_helper(hash, name, val, elf))
+ TAILQ_FOREACH(elf, &main_elf_queue, link) {
+ if (!resolve_sym_helper(hash, name, val, elf,
+ false /* !weak_ok */))
return TEE_SUCCESS;
+ if (!resolve_sym_helper(hash, name, val, elf,
+ true /* weak_ok */))
+ return TEE_SUCCESS;
+ }
return TEE_ERROR_ITEM_NOT_FOUND;
}