From e9c78e05a698fc3bb5518e3e7e616b3598356eb4 Mon Sep 17 00:00:00 2001 From: Luca Burelli Date: Fri, 7 Feb 2025 18:12:18 +0100 Subject: [PATCH] llext: add ELF inspection APIs Add APIs to inspect the contents of an ELF file loaded as an extension. This is useful for applications that need to access the contents of the extension in a more fine-grained way than the existing LLEXT APIs. Use of these APIs requires the 'keep_elf_data' option to be provided via struct llext_load_param to the 'llext_load()' call. Signed-off-by: Luca Burelli --- doc/services/llext/api.rst | 2 + doc/services/llext/load.rst | 4 + include/zephyr/llext/inspect.h | 141 ++++++++++++++++++++++++++ include/zephyr/llext/llext_internal.h | 5 + subsys/llext/llext.c | 37 ++++--- subsys/llext/llext_priv.h | 8 +- 6 files changed, 175 insertions(+), 22 deletions(-) create mode 100644 include/zephyr/llext/inspect.h 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;