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:
parent
69cdc32892
commit
b0b4b0baa0
4 changed files with 42 additions and 18 deletions
|
@ -78,6 +78,19 @@ sys_slist_t *llext_list(void);
|
||||||
*/
|
*/
|
||||||
struct llext *llext_by_name(const char *name);
|
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
|
* @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] loader An extension loader that provides input data and context
|
||||||
* @param[in] name A string identifier for the extension
|
* @param[in] name A string identifier for the extension
|
||||||
* @param[out] ext A pointer to a statically allocated llext struct
|
* @param[out] ext A pointer to a statically allocated llext struct
|
||||||
|
* @param[in] ldr_parm Loader parameters
|
||||||
*
|
*
|
||||||
* @retval 0 Success
|
* @retval 0 Success
|
||||||
* @retval -ENOMEM Not enough memory
|
* @retval -ENOMEM Not enough memory
|
||||||
* @retval -EINVAL Invalid ELF stream
|
* @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
|
* @brief Unload an extension
|
||||||
|
|
|
@ -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;
|
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 stt = ELF_ST_TYPE(sym_tbl.st_info);
|
||||||
uint32_t stb = ELF_ST_BIND(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);
|
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
|
* 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) -
|
size_t got_offset = llext_file_offset(ldr, rela.r_offset) -
|
||||||
ldr->sects[LLEXT_SECT_TEXT].sh_offset;
|
ldr->sects[LLEXT_SECT_TEXT].sh_offset;
|
||||||
|
|
||||||
if (stt != STT_NOTYPE || sym_tbl.st_shndx != SHN_UNDEF)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const void *link_addr;
|
const void *link_addr;
|
||||||
|
|
||||||
switch (stb) {
|
switch (stb) {
|
||||||
|
@ -574,16 +577,18 @@ static void llext_link_plt(struct llext_loader *ldr, struct llext *ext, elf_shdr
|
||||||
continue;
|
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 */
|
/* Resolve the symbol */
|
||||||
*(const void **)(text + got_offset) = link_addr;
|
*(const void **)(text + got_offset) = link_addr;
|
||||||
break;
|
break;
|
||||||
case STB_LOCAL:
|
case STB_LOCAL:
|
||||||
arch_elf_relocate_local(ldr, ext, &rela, got_offset);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -591,7 +596,7 @@ __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;
|
uintptr_t loc = 0;
|
||||||
elf_shdr_t shdr;
|
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];
|
loc = (uintptr_t)ext->mem[LLEXT_MEM_DATA];
|
||||||
} else if (strcmp(name, ".rela.plt") == 0 ||
|
} else if (strcmp(name, ".rela.plt") == 0 ||
|
||||||
strcmp(name, ".rela.dyn") == 0) {
|
strcmp(name, ".rela.dyn") == 0) {
|
||||||
llext_link_plt(ldr, ext, &shdr);
|
llext_link_plt(ldr, ext, &shdr, do_local);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -724,7 +729,8 @@ static int llext_link(struct llext_loader *ldr, struct llext *ext)
|
||||||
/*
|
/*
|
||||||
* Load a valid ELF as an extension
|
* 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;
|
int ret = 0;
|
||||||
|
|
||||||
|
@ -793,7 +799,7 @@ static int do_llext_load(struct llext_loader *ldr, struct llext *ext)
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DBG("Linking ELF...");
|
LOG_DBG("Linking ELF...");
|
||||||
ret = llext_link(ldr, ext);
|
ret = llext_link(ldr, ext, ldr_parm ? ldr_parm->relocate_local : true);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
LOG_ERR("Failed to link, ret %d", ret);
|
LOG_ERR("Failed to link, ret %d", ret);
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -818,7 +824,8 @@ out:
|
||||||
return ret;
|
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;
|
int ret;
|
||||||
elf_ehdr_t ehdr;
|
elf_ehdr_t ehdr;
|
||||||
|
@ -857,7 +864,7 @@ int llext_load(struct llext_loader *ldr, const char *name, struct llext **ext)
|
||||||
}
|
}
|
||||||
|
|
||||||
ldr->hdr = ehdr;
|
ldr->hdr = ehdr;
|
||||||
ret = do_llext_load(ldr, *ext);
|
ret = do_llext_load(ldr, *ext, ldr_parm);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_ERR("Unsupported elf file type %x", ehdr.e_type);
|
LOG_ERR("Unsupported elf file type %x", ehdr.e_type);
|
||||||
|
|
|
@ -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);
|
hex_len, CONFIG_LLEXT_SHELL_MAX_SIZE, llext_buf_len);
|
||||||
LOG_HEXDUMP_DBG(llext_buf, 4, "4 byte MAGIC");
|
LOG_HEXDUMP_DBG(llext_buf, 4, "4 byte MAGIC");
|
||||||
|
|
||||||
|
struct llext_load_param ldr_parm = LLEXT_LOAD_PARAM_DEFAULT;
|
||||||
struct llext *ext;
|
struct llext *ext;
|
||||||
int res = llext_load(ldr, name, &ext);
|
int res = llext_load(ldr, name, &ext, &ldr_parm);
|
||||||
|
|
||||||
if (res == 0) {
|
if (res == 0) {
|
||||||
shell_print(sh, "Successfully loaded extension %s, addr %p\n", ext->name, ext);
|
shell_print(sh, "Successfully loaded extension %s, addr %p\n", ext->name, ext);
|
||||||
|
|
|
@ -28,12 +28,13 @@ ZTEST(llext, test_llext_simple)
|
||||||
struct llext_buf_loader buf_loader =
|
struct llext_buf_loader buf_loader =
|
||||||
LLEXT_BUF_LOADER(hello_world_elf, ARRAY_SIZE(hello_world_elf));
|
LLEXT_BUF_LOADER(hello_world_elf, ARRAY_SIZE(hello_world_elf));
|
||||||
struct llext_loader *loader = &buf_loader.loader;
|
struct llext_loader *loader = &buf_loader.loader;
|
||||||
|
struct llext_load_param ldr_parm = LLEXT_LOAD_PARAM_DEFAULT;
|
||||||
struct llext *ext;
|
struct llext *ext;
|
||||||
const void * const printk_fn = llext_find_sym(NULL, "printk");
|
const void * const printk_fn = llext_find_sym(NULL, "printk");
|
||||||
|
|
||||||
zassert_equal(printk_fn, printk, "printk should be an exported symbol");
|
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");
|
zassert_ok(res, "load should succeed");
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue