llext: make local relocations optional

Some applications can decide to link their loadable objects for
exactly the same addresses, where they will be loaded. In those cases
local relocations aren't needed any more and can in fact break the
object if applied while the object is in a temporary storage. Add a
parameter to skip such local relocations.

Signed-off-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
This commit is contained in:
Guennadi Liakhovetski 2023-09-26 13:02:25 +02:00 committed by Anas Nashif
commit b0b4b0baa0
4 changed files with 42 additions and 18 deletions

View file

@ -78,6 +78,19 @@ sys_slist_t *llext_list(void);
*/
struct llext *llext_by_name(const char *name);
/**
* @brief llext loader parameters
*
* These are parameters, not saved in the permanent llext context, needed only
* for the loader
*/
struct llext_load_param {
/** Should local relocation be performed */
bool relocate_local;
};
#define LLEXT_LOAD_PARAM_DEFAULT {.relocate_local = true,}
/**
* @brief Load and link an extension
*
@ -88,12 +101,14 @@ struct llext *llext_by_name(const char *name);
* @param[in] loader An extension loader that provides input data and context
* @param[in] name A string identifier for the extension
* @param[out] ext A pointer to a statically allocated llext struct
* @param[in] ldr_parm Loader parameters
*
* @retval 0 Success
* @retval -ENOMEM Not enough memory
* @retval -EINVAL Invalid ELF stream
*/
int llext_load(struct llext_loader *loader, const char *name, struct llext **ext);
int llext_load(struct llext_loader *loader, const char *name, struct llext **ext,
struct llext_load_param *ldr_parm);
/**
* @brief Unload an extension

View file

@ -494,7 +494,8 @@ __weak void arch_elf_relocate_local(struct llext_loader *ldr, struct llext *ext,
{
}
static void llext_link_plt(struct llext_loader *ldr, struct llext *ext, elf_shdr_t *shdr)
static void llext_link_plt(struct llext_loader *ldr, struct llext *ext,
elf_shdr_t *shdr, bool do_local)
{
unsigned int sh_cnt = shdr->sh_size / shdr->sh_entsize;
/*
@ -547,6 +548,11 @@ static void llext_link_plt(struct llext_loader *ldr, struct llext *ext, elf_shdr
uint32_t stt = ELF_ST_TYPE(sym_tbl.st_info);
uint32_t stb = ELF_ST_BIND(sym_tbl.st_info);
if (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
@ -555,9 +561,6 @@ static void llext_link_plt(struct llext_loader *ldr, struct llext *ext, elf_shdr
size_t got_offset = llext_file_offset(ldr, rela.r_offset) -
ldr->sects[LLEXT_SECT_TEXT].sh_offset;
if (stt != STT_NOTYPE || sym_tbl.st_shndx != SHN_UNDEF)
continue;
const void *link_addr;
switch (stb) {
@ -574,24 +577,26 @@ static void llext_link_plt(struct llext_loader *ldr, struct llext *ext, elf_shdr
continue;
}
LOG_DBG("symbol %s offset %#x r-offset %#x .text offset %#x",
name, got_offset,
rela.r_offset, ldr->sects[LLEXT_SECT_TEXT].sh_offset);
/* Resolve the symbol */
*(const void **)(text + got_offset) = link_addr;
break;
case STB_LOCAL:
if (do_local) {
arch_elf_relocate_local(ldr, ext, &rela, got_offset);
}
}
LOG_DBG("symbol %s offset %#x r-offset %#x .text offset %#x stb %u",
name, got_offset,
rela.r_offset, ldr->sects[LLEXT_SECT_TEXT].sh_offset, stb);
}
}
__weak void arch_elf_relocate(elf_rela_t *rel, uintptr_t opaddr, uintptr_t opval)
{
}
static int llext_link(struct llext_loader *ldr, struct llext *ext)
static int llext_link(struct llext_loader *ldr, struct llext *ext, bool do_local)
{
uintptr_t loc = 0;
elf_shdr_t shdr;
@ -638,7 +643,7 @@ static int llext_link(struct llext_loader *ldr, struct llext *ext)
loc = (uintptr_t)ext->mem[LLEXT_MEM_DATA];
} else if (strcmp(name, ".rela.plt") == 0 ||
strcmp(name, ".rela.dyn") == 0) {
llext_link_plt(ldr, ext, &shdr);
llext_link_plt(ldr, ext, &shdr, do_local);
continue;
}
@ -724,7 +729,8 @@ static int llext_link(struct llext_loader *ldr, struct llext *ext)
/*
* Load a valid ELF as an extension
*/
static int do_llext_load(struct llext_loader *ldr, struct llext *ext)
static int do_llext_load(struct llext_loader *ldr, struct llext *ext,
struct llext_load_param *ldr_parm)
{
int ret = 0;
@ -793,7 +799,7 @@ static int do_llext_load(struct llext_loader *ldr, struct llext *ext)
}
LOG_DBG("Linking ELF...");
ret = llext_link(ldr, ext);
ret = llext_link(ldr, ext, ldr_parm ? ldr_parm->relocate_local : true);
if (ret != 0) {
LOG_ERR("Failed to link, ret %d", ret);
goto out;
@ -818,7 +824,8 @@ out:
return ret;
}
int llext_load(struct llext_loader *ldr, const char *name, struct llext **ext)
int llext_load(struct llext_loader *ldr, const char *name, struct llext **ext,
struct llext_load_param *ldr_parm)
{
int ret;
elf_ehdr_t ehdr;
@ -857,7 +864,7 @@ int llext_load(struct llext_loader *ldr, const char *name, struct llext **ext)
}
ldr->hdr = ehdr;
ret = do_llext_load(ldr, *ext);
ret = do_llext_load(ldr, *ext, ldr_parm);
break;
default:
LOG_ERR("Unsupported elf file type %x", ehdr.e_type);

View file

@ -117,8 +117,9 @@ static int cmd_llext_load_hex(const struct shell *sh, size_t argc, char *argv[])
hex_len, CONFIG_LLEXT_SHELL_MAX_SIZE, llext_buf_len);
LOG_HEXDUMP_DBG(llext_buf, 4, "4 byte MAGIC");
struct llext_load_param ldr_parm = LLEXT_LOAD_PARAM_DEFAULT;
struct llext *ext;
int res = llext_load(ldr, name, &ext);
int res = llext_load(ldr, name, &ext, &ldr_parm);
if (res == 0) {
shell_print(sh, "Successfully loaded extension %s, addr %p\n", ext->name, ext);

View file

@ -28,12 +28,13 @@ ZTEST(llext, test_llext_simple)
struct llext_buf_loader buf_loader =
LLEXT_BUF_LOADER(hello_world_elf, ARRAY_SIZE(hello_world_elf));
struct llext_loader *loader = &buf_loader.loader;
struct llext_load_param ldr_parm = LLEXT_LOAD_PARAM_DEFAULT;
struct llext *ext;
const void * const printk_fn = llext_find_sym(NULL, "printk");
zassert_equal(printk_fn, printk, "printk should be an exported symbol");
int res = llext_load(loader, name, &ext);
int res = llext_load(loader, name, &ext, &ldr_parm);
zassert_ok(res, "load should succeed");