llext: add support for relocatable objects on Xtensa
Some toolchains cannot create shared objects for Xtensa, with them we have to use relocatable objects. Add support for them to llext. Signed-off-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
This commit is contained in:
parent
4879681b7e
commit
2ccf775396
|
@ -27,19 +27,34 @@ LOG_MODULE_DECLARE(llext);
|
|||
* supporting modules must implement this.
|
||||
*/
|
||||
void arch_elf_relocate_local(struct llext_loader *ldr, struct llext *ext,
|
||||
elf_rela_t *rel, size_t got_offset)
|
||||
const elf_rela_t *rel, const elf_sym_t *sym, size_t got_offset)
|
||||
{
|
||||
uint8_t *text = ext->mem[LLEXT_MEM_TEXT];
|
||||
int type = ELF32_R_TYPE(rel->r_info);
|
||||
elf_word *got_entry = (elf_word *)(text + got_offset);
|
||||
uintptr_t sh_addr;
|
||||
|
||||
if (type == R_XTENSA_RELATIVE) {
|
||||
elf_word ptr_offset = *(elf_word *)(text + got_offset);
|
||||
|
||||
LOG_DBG("relocation type %u offset %#x value %#x",
|
||||
type, got_offset, ptr_offset);
|
||||
|
||||
/* Relocate a local symbol: Xtensa specific */
|
||||
*(elf_word *)(text + got_offset) = (elf_word)(text + ptr_offset -
|
||||
ldr->sects[LLEXT_MEM_TEXT].sh_addr);
|
||||
if (ELF_ST_TYPE(sym->st_info) == STT_SECTION) {
|
||||
elf_shdr_t *shdr = llext_peek(ldr, ldr->hdr.e_shoff +
|
||||
sym->st_shndx * ldr->hdr.e_shentsize);
|
||||
sh_addr = shdr->sh_addr;
|
||||
} else {
|
||||
sh_addr = ldr->sects[LLEXT_MEM_TEXT].sh_addr;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case R_XTENSA_RELATIVE:
|
||||
/* Relocate a local symbol: Xtensa specific */
|
||||
*got_entry += (uintptr_t)text - sh_addr;
|
||||
break;
|
||||
case R_XTENSA_32:
|
||||
*got_entry += sh_addr;
|
||||
break;
|
||||
default:
|
||||
LOG_DBG("unsupported relocation type %u", type);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DBG("relocation to %#x type %u at %p", *got_entry, type, (void *)got_entry);
|
||||
}
|
||||
|
|
|
@ -222,10 +222,11 @@ ssize_t llext_find_section(struct llext_loader *loader, const char *search_name)
|
|||
* @param[in] loader Extension loader data and context
|
||||
* @param[in] ext Extension to call function in
|
||||
* @param[in] rel Relocation data provided by elf
|
||||
* @param[in] sym Corresponding symbol table entry
|
||||
* @param[in] got_offset Offset within a relocation table
|
||||
*/
|
||||
void arch_elf_relocate_local(struct llext_loader *loader, struct llext *ext,
|
||||
elf_rela_t *rel, size_t got_offset);
|
||||
const elf_rela_t *rel, const elf_sym_t *sym, size_t got_offset);
|
||||
|
||||
/**
|
||||
* @}
|
||||
|
|
|
@ -68,6 +68,9 @@ struct llext_loader {
|
|||
*/
|
||||
void *(*peek)(struct llext_loader *ldr, size_t pos);
|
||||
|
||||
/** Total calculated .data size for relocatable extensions */
|
||||
size_t prog_data_size;
|
||||
|
||||
/** @cond ignore */
|
||||
elf_ehdr_t hdr;
|
||||
elf_shdr_t sects[LLEXT_MEM_COUNT];
|
||||
|
|
|
@ -32,7 +32,7 @@ static sys_slist_t _llext_list = SYS_SLIST_STATIC_INIT(&_llext_list);
|
|||
|
||||
static struct k_mutex llext_lock = Z_MUTEX_INITIALIZER(llext_lock);
|
||||
|
||||
ssize_t llext_find_section(struct llext_loader *ldr, const char *search_name)
|
||||
static elf_shdr_t *llext_section_by_name(struct llext_loader *ldr, const char *search_name)
|
||||
{
|
||||
elf_shdr_t *shdr;
|
||||
unsigned int i;
|
||||
|
@ -44,7 +44,7 @@ ssize_t llext_find_section(struct llext_loader *ldr, const char *search_name)
|
|||
shdr = llext_peek(ldr, pos);
|
||||
if (!shdr) {
|
||||
/* The peek() method isn't supported */
|
||||
return -EOPNOTSUPP;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *name = llext_peek(ldr,
|
||||
|
@ -52,11 +52,18 @@ ssize_t llext_find_section(struct llext_loader *ldr, const char *search_name)
|
|||
shdr->sh_name);
|
||||
|
||||
if (!strcmp(name, search_name)) {
|
||||
return shdr->sh_offset;
|
||||
return shdr;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ssize_t llext_find_section(struct llext_loader *ldr, const char *search_name)
|
||||
{
|
||||
elf_shdr_t *shdr = llext_section_by_name(ldr, search_name);
|
||||
|
||||
return shdr ? shdr->sh_offset : -ENOENT;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -214,9 +221,12 @@ static int llext_map_sections(struct llext_loader *ldr, struct llext *ext)
|
|||
{
|
||||
int i, ret;
|
||||
size_t pos;
|
||||
elf_shdr_t shdr;
|
||||
elf_shdr_t shdr, rodata = {.sh_addr = ~0},
|
||||
high_shdr = {.sh_offset = 0}, low_shdr = {.sh_offset = ~0};
|
||||
const char *name;
|
||||
|
||||
ldr->sects[LLEXT_MEM_RODATA].sh_size = 0;
|
||||
|
||||
for (i = 0, pos = ldr->hdr.e_shoff;
|
||||
i < ldr->hdr.e_shnum;
|
||||
i++, pos += ldr->hdr.e_shentsize) {
|
||||
|
@ -230,12 +240,38 @@ static int llext_map_sections(struct llext_loader *ldr, struct llext *ext)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Identify the lowest and the highest data sections */
|
||||
if (!(shdr.sh_flags & SHF_EXECINSTR) &&
|
||||
shdr.sh_type == SHT_PROGBITS) {
|
||||
if (shdr.sh_offset > high_shdr.sh_offset) {
|
||||
high_shdr = shdr;
|
||||
}
|
||||
if (shdr.sh_offset < low_shdr.sh_offset) {
|
||||
low_shdr = shdr;
|
||||
}
|
||||
}
|
||||
|
||||
name = llext_string(ldr, ext, LLEXT_MEM_SHSTRTAB, shdr.sh_name);
|
||||
|
||||
LOG_DBG("section %d name %s", i, name);
|
||||
|
||||
enum llext_mem mem_idx;
|
||||
|
||||
/*
|
||||
* .rodata section is optional. If there isn't one, use the
|
||||
* first read-only data section
|
||||
*/
|
||||
if (shdr.sh_addr && !(shdr.sh_flags & (SHF_WRITE | SHF_EXECINSTR)) &&
|
||||
shdr.sh_addr < rodata.sh_addr) {
|
||||
rodata = shdr;
|
||||
LOG_DBG("rodata: select %#zx name %s", (size_t)shdr.sh_addr, name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Keep in mind, that when using relocatable (partially linked)
|
||||
* objects, ELF segments aren't created, so ldr->sect_map[] and
|
||||
* ldr->sects[] don't contain all the sections
|
||||
*/
|
||||
if (strcmp(name, ".text") == 0) {
|
||||
mem_idx = LLEXT_MEM_TEXT;
|
||||
} else if (strcmp(name, ".data") == 0) {
|
||||
|
@ -255,6 +291,13 @@ static int llext_map_sections(struct llext_loader *ldr, struct llext *ext)
|
|||
ldr->sect_map[i] = mem_idx;
|
||||
}
|
||||
|
||||
ldr->prog_data_size = high_shdr.sh_size + high_shdr.sh_offset - low_shdr.sh_offset;
|
||||
|
||||
/* No verbatim .rodata, use an automatically selected one */
|
||||
if (!ldr->sects[LLEXT_MEM_RODATA].sh_size) {
|
||||
ldr->sects[LLEXT_MEM_RODATA] = rodata;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -556,12 +599,12 @@ static size_t llext_file_offset(struct llext_loader *ldr, size_t offset)
|
|||
}
|
||||
|
||||
__weak void arch_elf_relocate_local(struct llext_loader *ldr, struct llext *ext,
|
||||
elf_rela_t *rel, size_t got_offset)
|
||||
const elf_rela_t *rel, const elf_sym_t *sym, size_t got_offset)
|
||||
{
|
||||
}
|
||||
|
||||
static void llext_link_plt(struct llext_loader *ldr, struct llext *ext,
|
||||
elf_shdr_t *shdr, bool do_local)
|
||||
elf_shdr_t *shdr, bool do_local, elf_shdr_t *tgt)
|
||||
{
|
||||
unsigned int sh_cnt = shdr->sh_size / shdr->sh_entsize;
|
||||
/*
|
||||
|
@ -613,25 +656,36 @@ static void llext_link_plt(struct llext_loader *ldr, struct llext *ext,
|
|||
}
|
||||
|
||||
uint32_t stt = ELF_ST_TYPE(sym_tbl.st_info);
|
||||
uint32_t stb = ELF_ST_BIND(sym_tbl.st_info);
|
||||
|
||||
if (stt != STT_FUNC && (stt != STT_NOTYPE || sym_tbl.st_shndx != SHN_UNDEF)) {
|
||||
if (stt != STT_FUNC &&
|
||||
stt != STT_SECTION &&
|
||||
(stt != STT_NOTYPE || sym_tbl.st_shndx != SHN_UNDEF)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *name = llext_string(ldr, ext, LLEXT_MEM_STRTAB, sym_tbl.st_name);
|
||||
|
||||
/*
|
||||
* Both r_offset and sh_addr are addresses for which the extension
|
||||
* has been built.
|
||||
*/
|
||||
size_t got_offset = llext_file_offset(ldr, rela.r_offset) -
|
||||
ldr->sects[LLEXT_MEM_TEXT].sh_offset;
|
||||
size_t got_offset;
|
||||
|
||||
if (tgt) {
|
||||
got_offset = rela.r_offset + tgt->sh_offset -
|
||||
ldr->sects[LLEXT_MEM_TEXT].sh_offset;
|
||||
} else {
|
||||
got_offset = llext_file_offset(ldr, rela.r_offset) -
|
||||
ldr->sects[LLEXT_MEM_TEXT].sh_offset;
|
||||
}
|
||||
|
||||
uint32_t stb = ELF_ST_BIND(sym_tbl.st_info);
|
||||
const void *link_addr;
|
||||
|
||||
switch (stb) {
|
||||
case STB_GLOBAL:
|
||||
link_addr = llext_find_sym(NULL, name);
|
||||
|
||||
if (!link_addr)
|
||||
link_addr = llext_find_sym(&ext->sym_tab, name);
|
||||
|
||||
|
@ -648,9 +702,9 @@ static void llext_link_plt(struct llext_loader *ldr, struct llext *ext,
|
|||
/* Resolve the symbol */
|
||||
*(const void **)(text + got_offset) = link_addr;
|
||||
break;
|
||||
case STB_LOCAL:
|
||||
case STB_LOCAL:
|
||||
if (do_local) {
|
||||
arch_elf_relocate_local(ldr, ext, &rela, got_offset);
|
||||
arch_elf_relocate_local(ldr, ext, &rela, &sym_tbl, got_offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -697,8 +751,7 @@ static int llext_link(struct llext_loader *ldr, struct llext *ext, bool do_local
|
|||
|
||||
name = llext_string(ldr, ext, LLEXT_MEM_SHSTRTAB, shdr.sh_name);
|
||||
|
||||
if (strcmp(name, ".rel.text") == 0 ||
|
||||
strcmp(name, ".rela.text") == 0) {
|
||||
if (strcmp(name, ".rel.text") == 0) {
|
||||
loc = (uintptr_t)ext->mem[LLEXT_MEM_TEXT];
|
||||
} else if (strcmp(name, ".rel.bss") == 0 ||
|
||||
strcmp(name, ".rela.bss") == 0) {
|
||||
|
@ -706,14 +759,19 @@ static int llext_link(struct llext_loader *ldr, struct llext *ext, bool do_local
|
|||
} else if (strcmp(name, ".rel.rodata") == 0 ||
|
||||
strcmp(name, ".rela.rodata") == 0) {
|
||||
loc = (uintptr_t)ext->mem[LLEXT_MEM_RODATA];
|
||||
} else if (strcmp(name, ".rel.data") == 0 ||
|
||||
strcmp(name, ".rela.data") == 0) {
|
||||
} else if (strcmp(name, ".rel.data") == 0) {
|
||||
loc = (uintptr_t)ext->mem[LLEXT_MEM_DATA];
|
||||
} else if (strcmp(name, ".rel.exported_sym") == 0) {
|
||||
loc = (uintptr_t)ext->mem[LLEXT_MEM_EXPORT];
|
||||
} else if (strcmp(name, ".rela.plt") == 0 ||
|
||||
strcmp(name, ".rela.dyn") == 0) {
|
||||
llext_link_plt(ldr, ext, &shdr, do_local);
|
||||
llext_link_plt(ldr, ext, &shdr, do_local, NULL);
|
||||
continue;
|
||||
} else if (strncmp(name, ".rela", 5) == 0 && strlen(name) > 5) {
|
||||
elf_shdr_t *tgt = llext_section_by_name(ldr, name + 5);
|
||||
|
||||
if (tgt)
|
||||
llext_link_plt(ldr, ext, &shdr, do_local, tgt);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue