llext: fix handling of non-standard sections in relocatable case

When building partially linked / relocatable objects no ELF segments
are created and it becomes more difficult to predict which sections
the compiler will build and use. In this case a .data.rel.local
section is created by the compiler and it is needed to link .rodata
strings in a twister test. We can handle arbitrary sections at run-
time if .peek() is supported. If it isn't we need to allocate and
copy the section. For now we simply error out in such cases. Fixing
that would represent a larger change and can be done incrementally.

This also fixes the relocation calculation to point to the correct
symbol address instead of the memory location, where it's currently
residing, because that can be a temporary buffer as is the case with
SOF.

Signed-off-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
This commit is contained in:
Guennadi Liakhovetski 2024-05-08 15:18:46 +02:00 committed by Maureen Helm
commit ebde53904d
2 changed files with 55 additions and 7 deletions

View file

@ -530,7 +530,8 @@ static int llext_export_symbols(struct llext_loader *ldr, struct llext *ext)
return 0;
}
static int llext_copy_symbols(struct llext_loader *ldr, struct llext *ext)
static int llext_copy_symbols(struct llext_loader *ldr, struct llext *ext,
bool pre_located)
{
size_t ent_size = ldr->sects[LLEXT_MEM_SYMTAB].sh_entsize;
size_t syms_size = ldr->sects[LLEXT_MEM_SYMTAB].sh_size;
@ -564,16 +565,58 @@ static int llext_copy_symbols(struct llext_loader *ldr, struct llext *ext)
if ((stt == STT_FUNC || stt == STT_OBJECT) &&
stb == STB_GLOBAL && sect != SHN_UNDEF) {
enum llext_mem mem_idx = ldr->sect_map[sect];
const char *name = llext_string(ldr, ext, LLEXT_MEM_STRTAB, sym.st_name);
__ASSERT(j <= sym_tab->sym_cnt, "Miscalculated symbol number %u\n", j);
sym_tab->syms[j].name = name;
sym_tab->syms[j].addr = (void *)((uintptr_t)ext->mem[mem_idx] +
sym.st_value -
(ldr->hdr.e_type == ET_REL ? 0 :
ldr->sects[mem_idx].sh_addr));
uintptr_t section_addr;
void *base;
if (sect < LLEXT_MEM_BSS) {
/*
* This is just a slight optimisation for cached
* sections, we could use the generic path below
* for all of them
*/
base = ext->mem[ldr->sect_map[sect]];
section_addr = ldr->sects[ldr->sect_map[sect]].sh_addr;
} else {
/* Section header isn't stored, have to read it */
size_t shdr_pos = ldr->hdr.e_shoff + sect * ldr->hdr.e_shentsize;
elf_shdr_t shdr;
ret = llext_seek(ldr, shdr_pos);
if (ret != 0) {
LOG_ERR("failed seeking to position %zu\n", shdr_pos);
return ret;
}
ret = llext_read(ldr, &shdr, sizeof(elf_shdr_t));
if (ret != 0) {
LOG_ERR("failed reading section header at position %zu\n",
shdr_pos);
return ret;
}
base = llext_peek(ldr, shdr.sh_offset);
if (!base) {
LOG_ERR("cannot handle arbitrary sections without .peek\n");
return -EOPNOTSUPP;
}
section_addr = shdr.sh_addr;
}
if (pre_located) {
sym_tab->syms[j].addr = (uint8_t *)sym.st_value +
(ldr->hdr.e_type == ET_REL ? section_addr : 0);
} else {
sym_tab->syms[j].addr = (uint8_t *)base + sym.st_value -
(ldr->hdr.e_type == ET_REL ? 0 : section_addr);
}
LOG_DBG("function symbol %d name %s addr %p",
j, name, sym_tab->syms[j].addr);
j++;
@ -954,7 +997,7 @@ static int do_llext_load(struct llext_loader *ldr, struct llext *ext,
}
LOG_DBG("Copying symbols...");
ret = llext_copy_symbols(ldr, ext);
ret = llext_copy_symbols(ldr, ext, ldr_parm ? ldr_parm->pre_located : false);
if (ret != 0) {
LOG_ERR("Failed to copy symbols, ret %d", ret);
goto out;