From b0b4b0baa004d7f0d448f389454ba50a6604955e Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 26 Sep 2023 13:02:25 +0200 Subject: [PATCH] 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 --- include/zephyr/llext/llext.h | 17 +++++++++- subsys/llext/llext.c | 37 +++++++++++++--------- subsys/llext/shell.c | 3 +- tests/subsys/llext/src/test_llext_simple.c | 3 +- 4 files changed, 42 insertions(+), 18 deletions(-) diff --git a/include/zephyr/llext/llext.h b/include/zephyr/llext/llext.h index 96bd64d2854..5376e2cf13a 100644 --- a/include/zephyr/llext/llext.h +++ b/include/zephyr/llext/llext.h @@ -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 diff --git a/subsys/llext/llext.c b/subsys/llext/llext.c index 6598652840b..d71f2a2a102 100644 --- a/subsys/llext/llext.c +++ b/subsys/llext/llext.c @@ -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,16 +577,18 @@ 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: - 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; 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); diff --git a/subsys/llext/shell.c b/subsys/llext/shell.c index b9a4121acc6..289ebaa4d35 100644 --- a/subsys/llext/shell.c +++ b/subsys/llext/shell.c @@ -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); diff --git a/tests/subsys/llext/src/test_llext_simple.c b/tests/subsys/llext/src/test_llext_simple.c index ef0e62b93b5..0b002c79e71 100644 --- a/tests/subsys/llext/src/test_llext_simple.c +++ b/tests/subsys/llext/src/test_llext_simple.c @@ -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");