Skip to content

Commit

Permalink
llext: add ELF inspection APIs
Browse files Browse the repository at this point in the history
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 <l.burelli@arduino.cc>
  • Loading branch information
pillo79 committed Feb 10, 2025
1 parent 80b1b3a commit e9c78e0
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 22 deletions.
2 changes: 2 additions & 0 deletions doc/services/llext/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ API Reference
.. doxygengroup:: llext_symbols

.. doxygengroup:: llext_loader_apis

.. doxygengroup:: llext_inspect_apis
4 changes: 4 additions & 0 deletions doc/services/llext/load.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
=====================

Expand Down
141 changes: 141 additions & 0 deletions include/zephyr/llext/inspect.h
Original file line number Diff line number Diff line change
@@ -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 <stddef.h>
#include <zephyr/llext/llext.h>
#include <zephyr/llext/loader.h>
#include <zephyr/llext/llext_internal.h>

/**
* @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 */
5 changes: 5 additions & 0 deletions include/zephyr/llext/llext_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
37 changes: 21 additions & 16 deletions subsys/llext/llext.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 2 additions & 6 deletions subsys/llext/llext_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,7 @@

#include <zephyr/kernel.h>
#include <zephyr/llext/llext.h>

struct llext_elf_sect_map {
enum llext_mem mem_idx;
size_t offset;
};
#include <zephyr/llext/llext_internal.h>

/*
* Memory management (llext_mem.c)
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit e9c78e0

Please sign in to comment.