diff --git a/arch/xtensa/core/elf.c b/arch/xtensa/core/elf.c index cd021b950406b5..0ce9885eed66c2 100644 --- a/arch/xtensa/core/elf.c +++ b/arch/xtensa/core/elf.c @@ -10,8 +10,18 @@ #include #include -LOG_MODULE_DECLARE(llext); +LOG_MODULE_DECLARE(llext, CONFIG_LLEXT_LOG_LEVEL); +/* + * ELF relocation tables on Xtensa contain relocations of different types. They + * specify how the relocation should be performed. Which relocations are used + * depends on the type of the ELF object (e.g. shared or partially linked + * object), structure of the object (single or multiple source files), compiler + * flags used (e.g. -fPIC), etc. Also not all relocation table entries should be + * acted upon. Some of them describe relocations that have already been + * resolved by the linker. We have to distinguish them from actionable + * relocations and only need to handle the latter ones. + */ #define R_XTENSA_NONE 0 #define R_XTENSA_32 1 #define R_XTENSA_RTLD 2 @@ -19,71 +29,126 @@ LOG_MODULE_DECLARE(llext); #define R_XTENSA_JMP_SLOT 4 #define R_XTENSA_RELATIVE 5 #define R_XTENSA_PLT 6 +#define R_XTENSA_ASM_EXPAND 11 #define R_XTENSA_SLOT0_OP 20 -/** - * @brief Architecture specific function for relocating shared elf - * - * Elf files contain a series of relocations described in multiple sections. - * These relocation instructions are architecture specific and each architecture - * supporting modules must implement this. - */ -void arch_elf_relocate_local(struct llext_loader *ldr, struct llext *ext, - const elf_rela_t *rel, const elf_sym_t *sym, size_t got_offset) +static void xtensa_elf_relocate(struct llext_loader *ldr, struct llext *ext, + const elf_rela_t *rel, uint8_t *text, uintptr_t addr, + uint8_t *loc, int type, uint32_t stb) { - 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 (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 ? : (uintptr_t)llext_peek(ldr, shdr->sh_offset); - } else { - sh_addr = ldr->sects[LLEXT_MEM_TEXT].sh_addr; - } + elf_word *got_entry = (elf_word *)loc; switch (type) { case R_XTENSA_RELATIVE: - /* Relocate a local symbol: Xtensa specific */ - *got_entry += (uintptr_t)text - sh_addr; + /* Relocate a local symbol: Xtensa specific. Seems to only be used with PIC */ + *got_entry += (uintptr_t)text - addr; + break; + case R_XTENSA_GLOB_DAT: + case R_XTENSA_JMP_SLOT: + if (stb == STB_GLOBAL) { + *got_entry = addr; + } break; case R_XTENSA_32: - *got_entry += sh_addr; + /* Used for both LOCAL and GLOBAL bindings */ + *got_entry += addr; break; case R_XTENSA_SLOT0_OP: + /* Apparently only actionable with LOCAL bindings */ ; - uint8_t *opc = (uint8_t *)got_entry; - - /* Check the opcode: is this an L32R? And does it have to be relocated? */ - if ((opc[0] & 0xf) != 1 || opc[1] || opc[2]) - break; - elf_sym_t rsym; - int ret = llext_seek(ldr, ldr->sects[LLEXT_MEM_SYMTAB].sh_offset + ELF_R_SYM(rel->r_info) * sizeof(elf_sym_t)); + if (!ret) { ret = llext_read(ldr, &rsym, sizeof(elf_sym_t)); } - if (ret) + if (ret) { + LOG_ERR("Failed to read a symbol table entry, LLEXT linking might fail."); return; + } + /* + * So far in all observed use-cases + * llext_loaded_sect_ptr(ldr, ext, rsym.st_shndx) was already + * available as the "addr" argument of this function, supplied + * by arch_elf_relocate_local() from its non-STT_SECTION branch. + */ uintptr_t link_addr = (uintptr_t)llext_loaded_sect_ptr(ldr, ext, rsym.st_shndx) + rsym.st_value + rel->r_addend; - ssize_t value = (link_addr - (((uintptr_t)got_entry + 3) & ~3)) >> 2; - opc[1] = value & 0xff; - opc[2] = (value >> 8) & 0xff; + /* Check the opcode */ + if ((loc[0] & 0xf) == 1 && !loc[1] && !loc[2]) { + /* L32R: low nibble is 1 */ + loc[1] = value & 0xff; + loc[2] = (value >> 8) & 0xff; + } else if ((loc[0] & 0xf) == 5 && !(loc[0] & 0xc0) && !loc[1] && !loc[2]) { + /* CALLn: low nibble is 5 */ + loc[0] = (loc[0] & 0x3f) | ((value << 6) & 0xc0); + loc[1] = (value >> 2) & 0xff; + loc[2] = (value >> 10) & 0xff; + } else { + LOG_DBG("%p: unhandled OPC or no relocation %02x%02x%02x inf %#x offs %#x", + (void *)loc, loc[2], loc[1], loc[0], + rel->r_info, rel->r_offset); + break; + } + break; + case R_XTENSA_ASM_EXPAND: + /* Nothing to do */ break; default: - LOG_DBG("unsupported relocation type %u", type); + LOG_DBG("Unsupported relocation type %u", type); return; } - LOG_DBG("relocation to %#x type %u at %p", *got_entry, type, (void *)got_entry); + LOG_DBG("Applied relocation to %#x type %u at %p", + *(uint32_t *)((uintptr_t)got_entry & ~3), type, (void *)got_entry); +} + +/** + * @brief Architecture specific function for STB_LOCAL ELF relocations + */ +void arch_elf_relocate_local(struct llext_loader *ldr, struct llext *ext, const elf_rela_t *rel, + const elf_sym_t *sym, size_t got_offset, + const struct llext_load_param *ldr_parm) +{ + uint8_t *text = ext->mem[LLEXT_MEM_TEXT]; + uint8_t *loc = text + got_offset; + int type = ELF32_R_TYPE(rel->r_info); + uintptr_t 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 && + (!ldr_parm->section_detached || !ldr_parm->section_detached(shdr)) ? + shdr->sh_addr : (uintptr_t)llext_peek(ldr, shdr->sh_offset); + } else { + sh_addr = ldr->sects[LLEXT_MEM_TEXT].sh_addr; + } + + xtensa_elf_relocate(ldr, ext, rel, text, sh_addr, loc, type, ELF_ST_BIND(sym->st_info)); +} + +/** + * @brief Architecture specific function for STB_GLOBAL ELF relocations + */ +void arch_elf_relocate_global(struct llext_loader *ldr, struct llext *ext, const elf_rela_t *rel, + const elf_sym_t *sym, size_t got_offset, const void *link_addr) +{ + uint8_t *text = ext->mem[LLEXT_MEM_TEXT]; + elf_word *got_entry = (elf_word *)(text + got_offset); + int type = ELF32_R_TYPE(rel->r_info); + + /* For global relocations we expect the initial value for R_XTENSA_RELATIVE to be zero */ + if (type == R_XTENSA_RELATIVE && *got_entry) { + LOG_WRN("global: non-zero relative value %#x", *got_entry); + } + + xtensa_elf_relocate(ldr, ext, rel, text, (uintptr_t)link_addr, (uint8_t *)got_entry, type, + ELF_ST_BIND(sym->st_info)); } diff --git a/cmake/compiler/gcc/target_xtensa.cmake b/cmake/compiler/gcc/target_xtensa.cmake index 5321204c2df5e7..0b398023f67bdd 100644 --- a/cmake/compiler/gcc/target_xtensa.cmake +++ b/cmake/compiler/gcc/target_xtensa.cmake @@ -3,8 +3,6 @@ # Flags not supported by llext linker # (regexps are supported and match whole word) set(LLEXT_REMOVE_FLAGS - -fno-pic - -fno-pie -ffunction-sections -fdata-sections -g.* @@ -14,7 +12,20 @@ set(LLEXT_REMOVE_FLAGS # Flags to be added to llext code compilation set(LLEXT_APPEND_FLAGS - -fPIC -nostdlib -nodefaultlibs ) + +if(CONFIG_LLEXT_BUILD_PIC) +set(LLEXT_REMOVE_FLAGS ${LLEXT_REMOVE_FLAGS} + -fno-pic + -fno-pie +) +set(LLEXT_APPEND_FLAGS ${LLEXT_APPEND_FLAGS} + -fPIC +) +else() +set(LLEXT_APPEND_FLAGS ${LLEXT_APPEND_FLAGS} + -ffreestanding +) +endif() diff --git a/cmake/compiler/xt-clang/target.cmake b/cmake/compiler/xt-clang/target.cmake index 41a72aa782e905..978bc7c7860f48 100644 --- a/cmake/compiler/xt-clang/target.cmake +++ b/cmake/compiler/xt-clang/target.cmake @@ -5,8 +5,6 @@ include(${ZEPHYR_BASE}/cmake/compiler/xcc/target.cmake) # Flags not supported by llext linker # (regexps are supported and match whole word) set(LLEXT_REMOVE_FLAGS - -fno-pic - -fno-pie -ffunction-sections -fdata-sections -g.* @@ -16,7 +14,21 @@ set(LLEXT_REMOVE_FLAGS # Flags to be added to llext code compilation set(LLEXT_APPEND_FLAGS - -fPIC -nostdlib -nodefaultlibs ) + +if(CONFIG_LLEXT_BUILD_PIC) +set(LLEXT_REMOVE_FLAGS ${LLEXT_REMOVE_FLAGS} + -fno-pic + -fno-pie +) +set(LLEXT_APPEND_FLAGS ${LLEXT_APPEND_FLAGS} + -fPIC +) +else() +set(LLEXT_APPEND_FLAGS ${LLEXT_APPEND_FLAGS} + -ffreestanding + -mno-generate-flix +) +endif() diff --git a/include/zephyr/llext/llext.h b/include/zephyr/llext/llext.h index 569ffd4746c4a7..6d6d8c782f5e38 100644 --- a/include/zephyr/llext/llext.h +++ b/include/zephyr/llext/llext.h @@ -120,8 +120,24 @@ struct llext { /** Array of extensions, whose symbols this extension accesses */ struct llext *dependency[LLEXT_MAX_DEPENDENCIES]; + + /** @cond ignore */ + unsigned int sect_cnt; + elf_shdr_t *sect_hdrs; + bool sect_hdrs_on_heap; + /** @endcond */ }; +static inline const elf_shdr_t *llext_section_headers(const struct llext *ext) +{ + return ext->sect_hdrs; +} + +static inline unsigned int llext_section_count(const struct llext *ext) +{ + return ext->sect_cnt; +} + /** * @brief Advanced llext_load parameters * @@ -336,16 +352,47 @@ int arch_elf_relocate(elf_rela_t *rel, uintptr_t loc, ssize_t llext_find_section(struct llext_loader *loader, const char *search_name); /** - * @brief Architecture specific function for updating addresses via relocation table + * @brief Extract ELF section header by name. + * + * Searches for a section by name in the ELF file and retrieves its full header. + * + * @param[in] loader Extension loader data and context + * @param[in] ext Extension to be searched + * @param[in] search_name Section name to search for + * @param[out] shdr Buffer for the section header + * @retval 0 Success + * @retval -ENOTSUP "peek" method not supported + * @retval -ENOENT section not found + */ +int llext_get_section_header(struct llext_loader *loader, struct llext *ext, + const char *search_name, elf_shdr_t *shdr); + +/** + * @brief Architecture specific function for local binding relocations + * + * @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 or in the code + * @param[in] ldr_parm Loader parameters + */ +void arch_elf_relocate_local(struct llext_loader *loader, struct llext *ext, const elf_rela_t *rel, + const elf_sym_t *sym, size_t got_offset, + const struct llext_load_param *ldr_parm); + +/** + * @brief Architecture specific function for global binding relocations * * @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 + * @param[in] got_offset Offset within a relocation table or in the code + * @param[in] link_addr target address for table-based relocations */ -void arch_elf_relocate_local(struct llext_loader *loader, struct llext *ext, - const elf_rela_t *rel, const elf_sym_t *sym, size_t got_offset); +void arch_elf_relocate_global(struct llext_loader *loader, struct llext *ext, const elf_rela_t *rel, + const elf_sym_t *sym, size_t got_offset, const void *link_addr); /** * @} diff --git a/include/zephyr/llext/loader.h b/include/zephyr/llext/loader.h index 6684d98a747bf9..3d043806ef7374 100644 --- a/include/zephyr/llext/loader.h +++ b/include/zephyr/llext/loader.h @@ -98,10 +98,7 @@ struct llext_loader { /** @cond ignore */ elf_ehdr_t hdr; elf_shdr_t sects[LLEXT_MEM_COUNT]; - elf_shdr_t *sect_hdrs; - bool sect_hdrs_on_heap; struct llext_elf_sect_map *sect_map; - uint32_t sect_cnt; /** @endcond */ }; diff --git a/subsys/llext/Kconfig b/subsys/llext/Kconfig index 696620b16d8880..55800dcd0cf107 100644 --- a/subsys/llext/Kconfig +++ b/subsys/llext/Kconfig @@ -49,6 +49,16 @@ config LLEXT_HEAP_SIZE help Heap size in kilobytes available to llext for dynamic allocation +config LLEXT_BUILD_PIC + bool "Use -fPIC when building LLEXT" + depends on XTENSA + default y if LLEXT_TYPE_ELF_SHAREDLIB + help + By default LLEXT compilation is performed with -fno-pic -fno-pie compiler + flags. Some platforms can benefit from using -fPIC instead, in which case + most internal linking is performed by the linker at build time. Select "y" + to make use of that advantage. + config LLEXT_SHELL bool "llext shell commands" depends on SHELL diff --git a/subsys/llext/llext.c b/subsys/llext/llext.c index 4e0ff98c13af3d..c1871a0c6f07a3 100644 --- a/subsys/llext/llext.c +++ b/subsys/llext/llext.c @@ -23,12 +23,34 @@ 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) +int llext_get_section_header(struct llext_loader *ldr, struct llext *ext, const char *search_name, + elf_shdr_t *shdr) { - /* Note that this API is used after llext_load(), so the ldr->sect_hdrs - * cache is already freed. A direct search covers all situations. - */ + const elf_shdr_t *tmp; + unsigned int i; + + for (i = 0, tmp = ext->sect_hdrs; + i < ext->sect_cnt; + i++, tmp++) { + const char *name = llext_peek(ldr, + ldr->sects[LLEXT_MEM_SHSTRTAB].sh_offset + + tmp->sh_name); + + if (!name) { + return -ENOTSUP; + } + + if (!strcmp(name, search_name)) { + *shdr = *tmp; + return 0; + } + } + + return -ENOENT; +} +ssize_t llext_find_section(struct llext_loader *ldr, const char *search_name) +{ elf_shdr_t *shdr; unsigned int i; size_t pos; @@ -253,6 +275,10 @@ int llext_unload(struct llext **ext) *ext = NULL; k_mutex_unlock(&llext_lock); + if (tmp->sect_hdrs_on_heap) { + llext_free(tmp->sect_hdrs); + } + llext_free_regions(tmp); llext_free(tmp->sym_tab.syms); llext_free(tmp->exp_tab.syms); diff --git a/subsys/llext/llext_link.c b/subsys/llext/llext_link.c index 0049360c4954f6..7dacb112a006af 100644 --- a/subsys/llext/llext_link.c +++ b/subsys/llext/llext_link.c @@ -33,7 +33,14 @@ __weak int arch_elf_relocate(elf_rela_t *rel, uintptr_t loc, } __weak void arch_elf_relocate_local(struct llext_loader *ldr, struct llext *ext, - const elf_rela_t *rel, const elf_sym_t *sym, size_t got_offset) + const elf_rela_t *rel, const elf_sym_t *sym, size_t got_offset, + const struct llext_load_param *ldr_parm) +{ +} + +__weak void arch_elf_relocate_global(struct llext_loader *ldr, struct llext *ext, + const elf_rela_t *rel, const elf_sym_t *sym, size_t got_offset, + const void *link_addr) { } @@ -249,12 +256,10 @@ static void llext_link_plt(struct llext_loader *ldr, struct llext *ext, elf_shdr } /* Resolve the symbol */ - *(const void **)(text + got_offset) = link_addr; + arch_elf_relocate_global(ldr, ext, &rela, &sym, got_offset, link_addr); break; case STB_LOCAL: - if (ldr_parm->relocate_local) { - arch_elf_relocate_local(ldr, ext, &rela, &sym, got_offset); - } + arch_elf_relocate_local(ldr, ext, &rela, &sym, got_offset, ldr_parm); } LOG_DBG("symbol %s offset %#zx r-offset %#zx .text offset %#zx stb %u", @@ -272,8 +277,8 @@ int llext_link(struct llext_loader *ldr, struct llext *ext, const struct llext_l const char *name; int i, ret; - for (i = 0; i < ldr->sect_cnt; ++i) { - elf_shdr_t *shdr = ldr->sect_hdrs + i; + for (i = 0; i < ext->sect_cnt; ++i) { + elf_shdr_t *shdr = ext->sect_hdrs + i; /* find proper relocation sections */ switch (shdr->sh_type) { @@ -300,7 +305,7 @@ int llext_link(struct llext_loader *ldr, struct llext *ext, const struct llext_l continue; } - if (shdr->sh_info >= ldr->sect_cnt || + if (shdr->sh_info >= ext->sect_cnt || shdr->sh_size % shdr->sh_entsize != 0) { LOG_ERR("Sanity checks failed for section %d " "(info %zd, size %zd, entsize %zd)", i, @@ -327,7 +332,12 @@ int llext_link(struct llext_loader *ldr, struct llext *ext, const struct llext_l strcmp(name, ".rela.dyn") == 0) { tgt = NULL; } else { - tgt = ldr->sect_hdrs + shdr->sh_info; + /* + * Entries in .rel.X and .rela.X sections describe references in + * section .X to local or global symbols. They point to entries + * in the symbol table, describing respective symbols + */ + tgt = ext->sect_hdrs + shdr->sh_info; } llext_link_plt(ldr, ext, shdr, ldr_parm, tgt); @@ -465,6 +475,20 @@ int llext_link(struct llext_loader *ldr, struct llext *ext, const struct llext_l sys_cache_instr_invd_range(ext->mem[i], ext->mem_size[i]); } } + + /* Detached section caches should be synchronized in place */ + if (ldr_parm->section_detached) { + for (i = 0; i < ext->sect_cnt; ++i) { + elf_shdr_t *shdr = ext->sect_hdrs + i; + + if (ldr_parm->section_detached(shdr)) { + void *base = llext_peek(ldr, shdr->sh_offset); + + sys_cache_data_flush_range(base, shdr->sh_size); + sys_cache_instr_invd_range(base, shdr->sh_size); + } + } + } #endif return 0; diff --git a/subsys/llext/llext_load.c b/subsys/llext/llext_load.c index d433f6668fe409..2f55e74ae4c92e 100644 --- a/subsys/llext/llext_load.c +++ b/subsys/llext/llext_load.c @@ -103,29 +103,29 @@ static int llext_load_elf_data(struct llext_loader *ldr, struct llext *ext) return -ENOEXEC; } - ldr->sect_cnt = ldr->hdr.e_shnum; + ext->sect_cnt = ldr->hdr.e_shnum; - size_t sect_map_sz = ldr->sect_cnt * sizeof(ldr->sect_map[0]); + size_t sect_map_sz = ext->sect_cnt * sizeof(ldr->sect_map[0]); ldr->sect_map = llext_alloc(sect_map_sz); if (!ldr->sect_map) { LOG_ERR("Failed to allocate section map, size %zu", sect_map_sz); return -ENOMEM; } - for (int i = 0; i < ldr->sect_cnt; i++) { + for (int i = 0; i < ext->sect_cnt; i++) { ldr->sect_map[i].mem_idx = LLEXT_MEM_COUNT; ldr->sect_map[i].offset = 0; } - ldr->sect_hdrs = (elf_shdr_t *) llext_peek(ldr, ldr->hdr.e_shoff); - if (ldr->sect_hdrs) { - ldr->sect_hdrs_on_heap = false; + ext->sect_hdrs = (elf_shdr_t *)llext_peek(ldr, ldr->hdr.e_shoff); + if (ext->sect_hdrs) { + ext->sect_hdrs_on_heap = false; } else { - size_t sect_hdrs_sz = ldr->sect_cnt * sizeof(ldr->sect_hdrs[0]); + size_t sect_hdrs_sz = ext->sect_cnt * sizeof(ext->sect_hdrs[0]); - ldr->sect_hdrs_on_heap = true; - ldr->sect_hdrs = llext_alloc(sect_hdrs_sz); - if (!ldr->sect_hdrs) { + ext->sect_hdrs_on_heap = true; + ext->sect_hdrs = llext_alloc(sect_hdrs_sz); + if (!ext->sect_hdrs) { LOG_ERR("Failed to allocate section headers, size %zu", sect_hdrs_sz); return -ENOMEM; } @@ -136,7 +136,7 @@ static int llext_load_elf_data(struct llext_loader *ldr, struct llext *ext) return ret; } - ret = llext_read(ldr, ldr->sect_hdrs, sect_hdrs_sz); + ret = llext_read(ldr, ext->sect_hdrs, sect_hdrs_sz); if (ret != 0) { LOG_ERR("Failed to read section headers"); return ret; @@ -149,15 +149,15 @@ static int llext_load_elf_data(struct llext_loader *ldr, struct llext *ext) /* * Find all relevant string and symbol tables */ -static int llext_find_tables(struct llext_loader *ldr) +static int llext_find_tables(struct llext_loader *ldr, struct llext *ext) { int table_cnt, i; memset(ldr->sects, 0, sizeof(ldr->sects)); /* Find symbol and string tables */ - for (i = 0, table_cnt = 0; i < ldr->sect_cnt && table_cnt < 3; ++i) { - elf_shdr_t *shdr = ldr->sect_hdrs + i; + for (i = 0, table_cnt = 0; i < ext->sect_cnt && table_cnt < 3; ++i) { + elf_shdr_t *shdr = ext->sect_hdrs + i; LOG_DBG("section %d at 0x%zx: name %d, type %d, flags 0x%zx, " "addr 0x%zx, size %zd, link %d, info %d", @@ -216,8 +216,8 @@ static int llext_map_sections(struct llext_loader *ldr, struct llext *ext, int i, j; const char *name; - for (i = 0; i < ldr->sect_cnt; ++i) { - elf_shdr_t *shdr = ldr->sect_hdrs + i; + for (i = 0; i < ext->sect_cnt; ++i) { + elf_shdr_t *shdr = ext->sect_hdrs + i; name = llext_string(ldr, ext, LLEXT_MEM_SHSTRTAB, shdr->sh_name); @@ -437,8 +437,8 @@ static int llext_map_sections(struct llext_loader *ldr, struct llext *ext, * Calculate each ELF section's offset inside its memory region. This * is done as a separate pass so the final regions are already defined. */ - for (i = 0; i < ldr->sect_cnt; ++i) { - elf_shdr_t *shdr = ldr->sect_hdrs + i; + for (i = 0; i < ext->sect_cnt; ++i) { + elf_shdr_t *shdr = ext->sect_hdrs + i; enum llext_mem mem_idx = ldr->sect_map[i].mem_idx; if (mem_idx != LLEXT_MEM_COUNT) { @@ -585,30 +585,31 @@ static int llext_copy_symbols(struct llext_loader *ldr, struct llext *ext, sym_tab->syms[j].name = name; - elf_shdr_t *shdr = ldr->sect_hdrs + shndx; + elf_shdr_t *shdr = ext->sect_hdrs + shndx; uintptr_t section_addr = shdr->sh_addr; - const void *base; - - base = llext_loaded_sect_ptr(ldr, ext, shndx); - if (!base) { - /* If the section is not mapped, try to peek. - * Be noisy about it, since this is addressing - * data that was missed by llext_map_sections. - */ - base = llext_peek(ldr, shdr->sh_offset); - if (base) { - LOG_DBG("section %d peeked at %p", shndx, base); - } else { - LOG_ERR("No data for section %d", shndx); - return -ENOTSUP; - } - } if (ldr_parm->pre_located && (!ldr_parm->section_detached || !ldr_parm->section_detached(shdr))) { sym_tab->syms[j].addr = (uint8_t *)sym.st_value + (ldr->hdr.e_type == ET_REL ? section_addr : 0); } else { + const void *base; + + base = llext_loaded_sect_ptr(ldr, ext, shndx); + if (!base) { + /* If the section is not mapped, try to peek. + * Be noisy about it, since this is addressing + * data that was missed by llext_map_sections. + */ + base = llext_peek(ldr, shdr->sh_offset); + if (base) { + LOG_DBG("section %d peeked at %p", shndx, base); + } else { + LOG_ERR("No data for section %d", shndx); + return -ENOTSUP; + } + } + sym_tab->syms[j].addr = (uint8_t *)base + sym.st_value - (ldr->hdr.e_type == ET_REL ? 0 : section_addr); } @@ -663,7 +664,7 @@ int do_llext_load(struct llext_loader *ldr, struct llext *ext, #endif LOG_DBG("Finding ELF tables..."); - ret = llext_find_tables(ldr); + ret = llext_find_tables(ldr, ext); if (ret != 0) { LOG_ERR("Failed to find important ELF tables, ret %d", ret); goto out; @@ -711,11 +712,13 @@ int do_llext_load(struct llext_loader *ldr, struct llext *ext, goto out; } - LOG_DBG("Linking ELF..."); - ret = llext_link(ldr, ext, ldr_parm); - if (ret != 0) { - LOG_ERR("Failed to link, ret %d", ret); - goto out; + if (ldr_parm->relocate_local) { + LOG_DBG("Linking ELF..."); + ret = llext_link(ldr, ext, ldr_parm); + if (ret != 0) { + LOG_ERR("Failed to link, ret %d", ret); + goto out; + } } ret = llext_export_symbols(ldr, ext); @@ -735,11 +738,6 @@ int do_llext_load(struct llext_loader *ldr, struct llext *ext, llext_free(ldr->sect_map); ldr->sect_map = NULL; - if (ldr->sect_hdrs_on_heap) { - llext_free(ldr->sect_hdrs); - } - ldr->sect_hdrs = NULL; - /* Until proper inter-llext linking is implemented, the symbol table is * not useful outside of the loading process; keep it only if debugging * is enabled and no error is detected. diff --git a/tests/subsys/llext/simple/src/test_llext_simple.c b/tests/subsys/llext/simple/src/test_llext_simple.c index 6368a1b60ad12d..025a715ddbf0f0 100644 --- a/tests/subsys/llext/simple/src/test_llext_simple.c +++ b/tests/subsys/llext/simple/src/test_llext_simple.c @@ -11,6 +11,7 @@ #if defined(CONFIG_FILE_SYSTEM_LITTLEFS) #include #endif +#include #include #include #include @@ -411,6 +412,7 @@ ZTEST(llext, test_find_section) struct llext_loader *loader = &buf_loader.loader; struct llext_load_param ldr_parm = LLEXT_LOAD_PARAM_DEFAULT; struct llext *ext = NULL; + elf_shdr_t shdr; res = llext_load(loader, "find_section", &ext, &ldr_parm); zassert_ok(res, "load should succeed"); @@ -418,6 +420,11 @@ ZTEST(llext, test_find_section) section_ofs = llext_find_section(loader, ".data"); zassert_true(section_ofs > 0, "find_section returned %zd", section_ofs); + res = llext_get_section_header(loader, ext, ".data", &shdr); + zassert_ok(res, "get_section_header() should succeed"); + zassert_equal(shdr.sh_offset, section_ofs, + "different section offset %zd from get_section_header", shdr.sh_offset); + uintptr_t symbol_ptr = (uintptr_t)llext_find_sym(&ext->exp_tab, "number"); uintptr_t section_ptr = (uintptr_t)find_section_ext + section_ofs;