diff --git a/doc/services/llext/api.rst b/doc/services/llext/api.rst index b3d8d64152b0..c4227ca2d8a4 100644 --- a/doc/services/llext/api.rst +++ b/doc/services/llext/api.rst @@ -6,3 +6,5 @@ API Reference .. doxygengroup:: llext_symbols .. doxygengroup:: llext_loader_apis + +.. doxygengroup:: llext_inspect_apis diff --git a/doc/services/llext/load.rst b/doc/services/llext/load.rst index a72913c0bdec..939f278185d8 100644 --- a/doc/services/llext/load.rst +++ b/doc/services/llext/load.rst @@ -60,6 +60,10 @@ The returned ``void *`` can then be cast to the appropriate type and used. A wrapper for calling a function with no arguments is provided in :c:func:`llext_call_fn`. +Advanced users that need direct access to areas of the newly loaded extension +may want to refer to :c:func:`llext_get_section_info` and other LLEXT +inspection APIs. + Cleaning up after use ===================== diff --git a/include/zephyr/llext/inspect.h b/include/zephyr/llext/inspect.h new file mode 100644 index 000000000000..8173a2566821 --- /dev/null +++ b/include/zephyr/llext/inspect.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2025 Arduino SA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_LLEXT_INSPECT_H +#define ZEPHYR_LLEXT_INSPECT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +/** + * @file + * @brief LLEXT ELF inspection routines. + * + * This file contains routines to inspect the contents of an ELF file. It is + * intended to be used by applications that need advanced access to the ELF + * file structures of a loaded extension. + * + * @defgroup llext_inspect_apis ELF inspection APIs + * @ingroup llext_apis + * @{ + */ + +/** + * @brief Get information about a memory region for the specified extension. + * + * Retrieve information about a region (merged group of similar sections) in + * the extension. Any output parameter can be NULL if that information is not + * needed. + * + * @param[in] ldr Loader + * @param[in] ext Extension + * @param[in] region Region to get information about + * @param[out] hdr Variable storing the pointer to the region header + * @param[out] addr Variable storing the region load address + * @param[out] size Variable storing the region size + * + * @return 0 on success, -EINVAL if the region is invalid + */ +static inline int llext_get_region_info(const struct llext_loader *ldr, + const struct llext *ext, + enum llext_mem region, + const elf_shdr_t **hdr, + void **addr, size_t *size) +{ + if (region >= LLEXT_MEM_COUNT) { + return -EINVAL; + } + + if (hdr) { + *hdr = &ldr->sects[region]; + } + if (addr) { + *addr = ext->mem[region]; + } + if (size) { + *size = ext->mem_size[region]; + } + + return 0; +} + +/** + * @brief Get the index of a section with the specified name. + * + * Requires the @ref llext_load_param.keep_section_info flag to be set at + * extension load time. + * + * @param[in] ldr Loader + * @param[in] ext Extension + * @param[in] section_name Name of the section to look for + * + * @return Section index on success, -ENOENT if the section was not found, + * -ENOTSUP if section data is not available. + */ +int llext_section_shndx(const struct llext_loader *ldr, const struct llext *ext, + const char *section_name); + +/** + * @brief Get information about a section for the specified extension. + * + * Retrieve information about an ELF sections in the extension. Any output + * parameter can be @c NULL if that information is not needed. + * + * Requires the @ref llext_load_param.keep_section_info flag to be set at + * extension load time. + * + * @param[in] ldr Loader + * @param[in] ext Extension + * @param[in] shndx Section index + * @param[out] hdr Variable storing the pointer to the section header + * @param[out] region Variable storing the region the section belongs to + * @param[out] offset Variable storing the offset of the section in the region + * + * @return 0 on success, -EINVAL if the section index is invalid, + * -ENOTSUP if section data is not available. + */ +static inline int llext_get_section_info(const struct llext_loader *ldr, + const struct llext *ext, + int shndx, + const elf_shdr_t **hdr, + enum llext_mem *region, + size_t *offset) +{ + if (shndx <= 0 || shndx >= ext->sect_cnt) { + return -EINVAL; + } + if (!ldr->sect_map) { + return -ENOTSUP; + } + + if (hdr) { + *hdr = &ext->sect_hdrs[shndx]; + } + if (region) { + *region = ldr->sect_map[shndx].mem_idx; + } + if (offset) { + *offset = ldr->sect_map[shndx].offset; + } + + return 0; +} + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_LLEXT_INSPECT_H */ diff --git a/include/zephyr/llext/llext_internal.h b/include/zephyr/llext/llext_internal.h index f07dd5efd865..b5365aa5a751 100644 --- a/include/zephyr/llext/llext_internal.h +++ b/include/zephyr/llext/llext_internal.h @@ -21,6 +21,11 @@ extern "C" { struct llext_loader; struct llext; +struct llext_elf_sect_map { + enum llext_mem mem_idx; + size_t offset; +}; + const void *llext_loaded_sect_ptr(struct llext_loader *ldr, struct llext *ext, unsigned int sh_ndx); /** @endcond */ diff --git a/subsys/llext/llext.c b/subsys/llext/llext.c index 020a8b7b8038..918db5d4ce1a 100644 --- a/subsys/llext/llext.c +++ b/subsys/llext/llext.c @@ -23,32 +23,37 @@ static sys_slist_t _llext_list = SYS_SLIST_STATIC_INIT(&_llext_list); static struct k_mutex llext_lock = Z_MUTEX_INITIALIZER(llext_lock); -int llext_get_section_header(struct llext_loader *ldr, struct llext *ext, const char *search_name, - elf_shdr_t *shdr) +int llext_section_shndx(const struct llext_loader *ldr, const struct llext *ext, + const char *sect_name) { - 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; - } + for (i = 1; i < ext->sect_cnt; i++) { + const char *name = llext_string(ldr, ext, LLEXT_MEM_SHSTRTAB, + ext->sect_hdrs[i].sh_name); - if (!strcmp(name, search_name)) { - *shdr = *tmp; - return 0; + if (!strcmp(name, sect_name)) { + return i; } } return -ENOENT; } +int llext_get_section_header(struct llext_loader *ldr, struct llext *ext, const char *search_name, + elf_shdr_t *shdr) +{ + int ret; + + ret = llext_section_shndx(ldr, ext, search_name); + if (ret < 0) { + return ret; + } + + *shdr = ext->sect_hdrs[ret]; + return 0; +} + ssize_t llext_find_section(struct llext_loader *ldr, const char *search_name) { elf_shdr_t *shdr; diff --git a/subsys/llext/llext_priv.h b/subsys/llext/llext_priv.h index e3b9fd282a64..fbf00c31bb48 100644 --- a/subsys/llext/llext_priv.h +++ b/subsys/llext/llext_priv.h @@ -9,11 +9,7 @@ #include #include - -struct llext_elf_sect_map { - enum llext_mem mem_idx; - size_t offset; -}; +#include /* * Memory management (llext_mem.c) @@ -53,7 +49,7 @@ static inline void llext_free(void *ptr) int do_llext_load(struct llext_loader *ldr, struct llext *ext, const struct llext_load_param *ldr_parm); -static inline const char *llext_string(struct llext_loader *ldr, struct llext *ext, +static inline const char *llext_string(const struct llext_loader *ldr, const struct llext *ext, enum llext_mem mem_idx, unsigned int idx) { return (char *)ext->mem[mem_idx] + idx;