llext: Linkable loadable extensions
Adds the linkable loadable extensions (llext) subsystem which provides functionality for reading, parsing, and linking ELF encoded executable code into a managed extension to the running elf base image. A loader interface, and default buffer loader implementation, make available to the llext subsystem the elf data. A simple management API provide the ability to load and unload extensions as needed. A shell interface for extension loading and unloading makes it easy to try. Adds initial support for armv7 thumb built elfs with very specific compiler flags. Signed-off-by: Tom Burdick <thomas.burdick@intel.com> Co-authored-by: Chen Peng1 <peng1.chen@intel.com> Co-authored-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
This commit is contained in:
parent
34e16225eb
commit
41e0a4a371
|
@ -13,6 +13,7 @@ zephyr_library_sources_ifdef(CONFIG_IRQ_OFFLOAD irq_offload.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_THREAD_LOCAL_STORAGE tls.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_USERSPACE userspace.S)
|
||||
zephyr_library_sources_ifdef(CONFIG_ARM_ZIMAGE_HEADER header.S)
|
||||
zephyr_library_sources_ifdef(CONFIG_LLEXT elf.c)
|
||||
|
||||
add_subdirectory_ifdef(CONFIG_CPU_CORTEX_M cortex_m)
|
||||
add_subdirectory_ifdef(CONFIG_CPU_CORTEX_M_HAS_CMSE cortex_m/cmse)
|
||||
|
|
37
arch/arm/core/elf.c
Normal file
37
arch/arm/core/elf.c
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/llext/elf.h>
|
||||
#include <zephyr/llext/llext.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_DECLARE(elf);
|
||||
|
||||
/**
|
||||
* @brief Architecture specific function for relocating partially linked (static) elf
|
||||
*
|
||||
* Elf files contain a series of relocations described in a section. These relocation
|
||||
* instructions are architecture specific and each architecture supporting modules
|
||||
* must implement this.
|
||||
*
|
||||
* The relocation codes for arm are well documented
|
||||
* https://github.com/ARM-software/abi-aa/blob/main/aaelf32/aaelf32.rst#relocation
|
||||
*/
|
||||
void arch_elf_relocate(elf_rel_t *rel, uintptr_t opaddr, uintptr_t opval)
|
||||
{
|
||||
elf_word reloc_type = ELF32_R_TYPE(rel->r_info);
|
||||
|
||||
switch (reloc_type) {
|
||||
case R_ARM_ABS32:
|
||||
/* Update the absolute address of a load/store instruction */
|
||||
*((uint32_t *)opaddr) = (uint32_t)opval;
|
||||
break;
|
||||
default:
|
||||
LOG_DBG("Unsupported ARM elf relocation type %d at address %lx",
|
||||
reloc_type, opaddr);
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -39,6 +39,10 @@
|
|||
ITERABLE_SECTION_ROM(zbus_channel_observation, 4)
|
||||
#endif /* CONFIG_ZBUS */
|
||||
|
||||
#ifdef CONFIG_LLEXT
|
||||
ITERABLE_SECTION_ROM(llext_const_symbol, 4)
|
||||
#endif /* CONFIG_LLEXT */
|
||||
|
||||
SECTION_DATA_PROLOGUE(symbol_to_keep,,)
|
||||
{
|
||||
__symbol_to_keep_start = .;
|
||||
|
|
67
include/zephyr/llext/buf_loader.h
Normal file
67
include/zephyr/llext/buf_loader.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_LLEXT_BUF_LOADER_H
|
||||
#define ZEPHYR_LLEXT_BUF_LOADER_H
|
||||
|
||||
#include <zephyr/llext/loader.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief LLEXT buffer loader
|
||||
* @defgroup llext_buf_loader Linkable loadable extensions buffer loader
|
||||
* @ingroup llext
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief An extension loader from a provided buffer containing an ELF
|
||||
*/
|
||||
struct llext_buf_loader {
|
||||
/** Extension loader */
|
||||
struct llext_loader loader;
|
||||
|
||||
/** @cond ignore */
|
||||
const uint8_t *buf;
|
||||
size_t len;
|
||||
size_t pos;
|
||||
/** @endcond */
|
||||
};
|
||||
|
||||
/** @cond ignore */
|
||||
int llext_buf_read(struct llext_loader *ldr, void *buf, size_t len);
|
||||
int llext_buf_seek(struct llext_loader *ldr, size_t pos);
|
||||
/** @endcond */
|
||||
|
||||
/**
|
||||
* @brief Initialize an extension buf loader
|
||||
*
|
||||
* @param _buf Buffer containing an ELF binary
|
||||
* @param _buf_len Buffer length in bytes
|
||||
*/
|
||||
#define LLEXT_BUF_LOADER(_buf, _buf_len) \
|
||||
{ \
|
||||
.loader = { \
|
||||
.read = llext_buf_read, \
|
||||
.seek = llext_buf_seek \
|
||||
}, \
|
||||
.buf = (_buf), \
|
||||
.len = (_buf_len), \
|
||||
.pos = 0 \
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_LLEXT_BUF_LOADER_H */
|
484
include/zephyr/llext/elf.h
Normal file
484
include/zephyr/llext/elf.h
Normal file
|
@ -0,0 +1,484 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
*/
|
||||
#ifndef ZEPHYR_LLEXT_ELF_H
|
||||
#define ZEPHYR_LLEXT_ELF_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief ELF types and parsing
|
||||
*
|
||||
* Reference documents can be found here https://refspecs.linuxfoundation.org/elf/
|
||||
*
|
||||
* @defgroup elf ELF data types and defines
|
||||
* @ingroup llext
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Unsigned program address */
|
||||
typedef uint32_t elf32_addr;
|
||||
/** Unsigned medium integer */
|
||||
typedef uint16_t elf32_half;
|
||||
/** Unsigned file offset */
|
||||
typedef uint32_t elf32_off;
|
||||
/** Signed integer */
|
||||
typedef int32_t elf32_sword;
|
||||
/** Unsigned integer */
|
||||
typedef uint32_t elf32_word;
|
||||
|
||||
/** Unsigned program address */
|
||||
typedef uint64_t elf64_addr;
|
||||
/** Unsigned medium integer */
|
||||
typedef uint16_t elf64_half;
|
||||
/** Unsigned file offset */
|
||||
typedef uint64_t elf64_off;
|
||||
/** Signed integer */
|
||||
typedef int32_t elf64_sword;
|
||||
/** Unsigned integer */
|
||||
typedef uint32_t elf64_word;
|
||||
/** Signed long integer */
|
||||
typedef int64_t elf64_sxword;
|
||||
/** Unsigned long integer */
|
||||
typedef uint64_t elf64_xword;
|
||||
|
||||
|
||||
/**
|
||||
* @brief ELF identifier block
|
||||
*
|
||||
* 4 byte magic (.ELF)
|
||||
* 1 byte class (Invalid, 32 bit, 64 bit)
|
||||
* 1 byte endianness (Invalid, LSB, MSB)
|
||||
* 1 byte version (1)
|
||||
* 1 byte OS ABI (0 None, 1 HP-UX, 2 NetBSD, 3 Linux)
|
||||
* 1 byte ABI (0)
|
||||
* 7 bytes padding
|
||||
*/
|
||||
#define EI_NIDENT 16
|
||||
|
||||
/**
|
||||
* @brief ELF Header(32-bit)
|
||||
*/
|
||||
struct elf32_ehdr {
|
||||
/** Magic string identifying ELF binary */
|
||||
unsigned char e_ident[EI_NIDENT];
|
||||
/** Type of ELF */
|
||||
elf32_half e_type;
|
||||
/** Machine type */
|
||||
elf32_half e_machine;
|
||||
/** Object file version */
|
||||
elf32_word e_version;
|
||||
/** Virtual address of entry */
|
||||
elf32_addr e_entry;
|
||||
/** Program header table offset */
|
||||
elf32_off e_phoff;
|
||||
/** Section header table offset */
|
||||
elf32_off e_shoff;
|
||||
/** Processor specific flags */
|
||||
elf32_word e_flags;
|
||||
/** ELF header size */
|
||||
elf32_half e_ehsize;
|
||||
/** Program header count */
|
||||
elf32_half e_phentsize;
|
||||
/** Program header count */
|
||||
elf32_half e_phnum;
|
||||
/** Section header size */
|
||||
elf32_half e_shentsize;
|
||||
/** Section header count */
|
||||
elf32_half e_shnum;
|
||||
/** Section header containing section header string table */
|
||||
elf32_half e_shstrndx;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief ELF Header(64-bit)
|
||||
*/
|
||||
struct elf64_ehdr {
|
||||
/** Magic string identifying ELF binary */
|
||||
unsigned char e_ident[EI_NIDENT];
|
||||
/** Type of ELF */
|
||||
elf64_half e_type;
|
||||
/** Machine type */
|
||||
elf64_half e_machine;
|
||||
/** Object file version */
|
||||
elf64_word e_version;
|
||||
/** Virtual address of entry */
|
||||
elf64_addr e_entry;
|
||||
/** Program header table offset */
|
||||
elf64_off e_phoff;
|
||||
/** Section header table offset */
|
||||
elf64_off e_shoff;
|
||||
/** Processor specific flags */
|
||||
elf64_word e_flags;
|
||||
/** ELF header size */
|
||||
elf64_half e_ehsize;
|
||||
/** Program header size */
|
||||
elf64_half e_phentsize;
|
||||
/** Program header count */
|
||||
elf64_half e_phnum;
|
||||
/** Section header size */
|
||||
elf64_half e_shentsize;
|
||||
/** Section header count */
|
||||
elf64_half e_shnum;
|
||||
/** Section header containing section header string table */
|
||||
elf64_half e_shstrndx;
|
||||
};
|
||||
|
||||
/** Relocatable (unlinked) ELF */
|
||||
#define ET_REL 1
|
||||
|
||||
/** Executable (without PIC/PIE) ELF */
|
||||
#define ET_EXEC 2
|
||||
|
||||
/** Dynamic (executable with PIC/PIE or shared lib) ELF */
|
||||
#define ET_DYN 3
|
||||
|
||||
/** Core Dump */
|
||||
#define ET_CORE 4
|
||||
|
||||
/**
|
||||
* @brief Section Header(32-bit)
|
||||
*/
|
||||
struct elf32_shdr {
|
||||
/** Section header name index in section header string table */
|
||||
elf32_word sh_name;
|
||||
/** Section type */
|
||||
elf32_word sh_type;
|
||||
/** Section header attributes */
|
||||
elf32_word sh_flags;
|
||||
/** Address of section in the image */
|
||||
elf32_addr sh_addr;
|
||||
/** Location of section in the ELF binary in bytes */
|
||||
elf32_off sh_offset;
|
||||
/** Section size in bytes */
|
||||
elf32_word sh_size;
|
||||
/** Section header table link index, depends on section type */
|
||||
elf32_word sh_link;
|
||||
/** Section info, depends on section type */
|
||||
elf32_word sh_info;
|
||||
/** Section address alignment */
|
||||
elf32_word sh_addralign;
|
||||
/** Section contains table of fixed size entries sh_entsize bytes large */
|
||||
elf32_word sh_entsize;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Section Header(64-bit)
|
||||
*/
|
||||
struct elf64_shdr {
|
||||
/** Section header name index in section header string table */
|
||||
elf64_word sh_name;
|
||||
/** Section type */
|
||||
elf64_word sh_type;
|
||||
/** Section header attributes */
|
||||
elf64_xword sh_flags;
|
||||
/** Address of section in the image */
|
||||
elf64_addr sh_addr;
|
||||
/** Location of section in the ELF binary in bytes */
|
||||
elf64_off sh_offset;
|
||||
/** Section size in bytes */
|
||||
elf64_xword sh_size;
|
||||
/** Section header table link index, depends on section type */
|
||||
elf64_word sh_link;
|
||||
/** Section info, depends on section type */
|
||||
elf64_word sh_info;
|
||||
/** Section address alignment */
|
||||
elf64_xword sh_addralign;
|
||||
/** Section contains table of fixed size entries sh_entsize bytes large */
|
||||
elf64_xword sh_entsize;
|
||||
};
|
||||
|
||||
#define SHT_PROGBITS 0x1
|
||||
#define SHT_SYMTAB 0x2
|
||||
#define SHT_STRTAB 0x3
|
||||
#define SHT_RELA 0x4
|
||||
#define SHT_NOBITS 0x8
|
||||
#define SHT_REL 0x9
|
||||
#define SHT_DYNSYM 0xB
|
||||
|
||||
#define SHF_WRITE 0x1
|
||||
#define SHF_ALLOC 0x2
|
||||
#define SHF_EXECINSTR 0x4
|
||||
|
||||
/**
|
||||
* @brief Symbol table entry(32-bit)
|
||||
*/
|
||||
struct elf32_sym {
|
||||
/** Name of the symbol as an index into the symbol string table */
|
||||
elf32_word st_name;
|
||||
/** Value or location of the symbol */
|
||||
elf32_addr st_value;
|
||||
/** Size of the symbol */
|
||||
elf32_word st_size;
|
||||
/** Symbol binding and type information */
|
||||
unsigned char st_info;
|
||||
/** Symbol visibility */
|
||||
unsigned char st_other;
|
||||
/** Symbols related section given by section header index */
|
||||
elf32_half st_shndx;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Symbol table entry(64-bit)
|
||||
*/
|
||||
struct elf64_sym {
|
||||
/** Name of the symbol as an index into the symbol string table */
|
||||
elf64_word st_name;
|
||||
/** Value or location of the symbol */
|
||||
elf64_addr st_value;
|
||||
/** Size of the symbol */
|
||||
elf64_xword st_size;
|
||||
/** Symbol binding and type information */
|
||||
unsigned char st_info;
|
||||
/** Symbol visibility */
|
||||
unsigned char st_other;
|
||||
/** Symbols related section given by section header index */
|
||||
elf64_half st_shndx;
|
||||
};
|
||||
|
||||
#define SHN_UNDEF 0
|
||||
#define SHN_ABS 0xfff1
|
||||
#define SHN_COMMON 0xfff2
|
||||
|
||||
#define STT_NOTYPE 0
|
||||
#define STT_OBJECT 1
|
||||
#define STT_FUNC 2
|
||||
#define STT_SECTION 3
|
||||
#define STT_FILE 4
|
||||
#define STT_COMMON 5
|
||||
#define STT_LOOS 10
|
||||
#define STT_HIOS 12
|
||||
#define STT_LOPROC 13
|
||||
#define STT_HIPROC 15
|
||||
|
||||
#define STB_LOCAL 0
|
||||
#define STB_GLOBAL 1
|
||||
#define STB_WEAK 2
|
||||
#define STB_LOOS 10
|
||||
#define STB_HIOS 12
|
||||
#define STB_LOPROC 13
|
||||
#define STB_HIPROC 15
|
||||
|
||||
/**
|
||||
* @brief Symbol binding from 32bit st_info
|
||||
*
|
||||
* @param i Value of st_info
|
||||
*/
|
||||
#define ELF32_ST_BIND(i) ((i) >> 4)
|
||||
|
||||
/**
|
||||
* @brief Symbol type from 32bit st_info
|
||||
*
|
||||
* @param i Value of st_info
|
||||
*/
|
||||
#define ELF32_ST_TYPE(i) ((i) & 0xf)
|
||||
|
||||
/**
|
||||
* @brief Symbol binding from 32bit st_info
|
||||
*
|
||||
* @param i Value of st_info
|
||||
*/
|
||||
#define ELF64_ST_BIND(i) ((i) >> 4)
|
||||
|
||||
|
||||
/**
|
||||
* @brief Symbol type from 32bit st_info
|
||||
*
|
||||
* @param i Value of st_info
|
||||
*/
|
||||
#define ELF64_ST_TYPE(i) ((i) & 0xf)
|
||||
|
||||
/**
|
||||
* @brief Relocation entry for 32-bit ELFs
|
||||
*/
|
||||
struct elf32_rel {
|
||||
/** Offset in the section to perform a relocation */
|
||||
elf32_addr r_offset;
|
||||
|
||||
/** Information about the relocation, related symbol and type */
|
||||
elf32_word r_info;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Relocation symbol index from r_info
|
||||
*
|
||||
* @param i Value of r_info
|
||||
*/
|
||||
#define ELF32_R_SYM(i) ((i) >> 8)
|
||||
|
||||
/**
|
||||
* @brief Relocation type from r_info
|
||||
*
|
||||
* @param i Value of r_info
|
||||
*/
|
||||
#define ELF32_R_TYPE(i) ((i) & 0xff)
|
||||
|
||||
/**
|
||||
* @brief Relocation entry for 64-bit ELFs
|
||||
*/
|
||||
struct elf64_rel {
|
||||
/** Offset in section to perform a relocation */
|
||||
elf64_addr r_offset;
|
||||
/** Information about relocation, related symbol and type */
|
||||
elf64_xword r_info;
|
||||
};
|
||||
|
||||
/** @brief Relocation symbol from r_info
|
||||
*
|
||||
* @param i Value of r_info
|
||||
*/
|
||||
#define ELF64_R_SYM(i) ((i) >> 32)
|
||||
|
||||
/**
|
||||
* @brief Relocation type from r_info
|
||||
*
|
||||
* @param i Value of r_info
|
||||
*/
|
||||
#define ELF64_R_TYPE(i) ((i) & 0xffffffff)
|
||||
|
||||
#define R_386_NONE 0
|
||||
#define R_386_32 1
|
||||
#define R_386_PC32 2
|
||||
#define R_386_GOT32 3
|
||||
#define R_386_PLT32 4
|
||||
#define R_386_COPY 5
|
||||
#define R_386_GLOB_DAT 6
|
||||
#define R_386_JMP_SLOT 7
|
||||
#define R_386_RELATIVE 8
|
||||
#define R_386_GOTOFF 9
|
||||
|
||||
#define R_ARM_NONE 0
|
||||
#define R_ARM_PC24 1
|
||||
#define R_ARM_ABS32 2
|
||||
#define R_ARM_REL32 3
|
||||
#define R_ARM_COPY 4
|
||||
#define R_ARM_CALL 28
|
||||
#define R_ARM_V4BX 40
|
||||
|
||||
#define R_XTENSA_NONE 0
|
||||
#define R_XTENSA_32 1
|
||||
#define R_XTENSA_SLOT0_OP 20
|
||||
|
||||
/**
|
||||
* @brief Program header(32-bit)
|
||||
*/
|
||||
struct elf32_phdr {
|
||||
elf32_word p_type;
|
||||
elf32_off p_offset;
|
||||
elf32_addr p_vaddr;
|
||||
elf32_addr p_paddr;
|
||||
elf32_word p_filesz;
|
||||
elf32_word p_memsz;
|
||||
elf32_word p_flags;
|
||||
elf32_word p_align;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Program header(64-bit)
|
||||
*/
|
||||
struct elf64_phdr {
|
||||
elf64_word p_type;
|
||||
elf64_off p_offset;
|
||||
elf64_addr p_vaddr;
|
||||
elf64_addr p_paddr;
|
||||
elf64_xword p_filesz;
|
||||
elf64_xword p_memsz;
|
||||
elf64_word p_flags;
|
||||
elf64_xword p_align;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Program segment type
|
||||
*/
|
||||
#define PT_LOAD 1
|
||||
|
||||
/**
|
||||
* @brief Dynamic section entry(32-bit)
|
||||
*/
|
||||
struct elf32_dyn {
|
||||
elf32_sword d_tag;
|
||||
union {
|
||||
elf32_word d_val;
|
||||
elf32_addr d_ptr;
|
||||
} d_un;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Dynamic section entry(64-bit)
|
||||
*/
|
||||
struct elf64_dyn {
|
||||
elf64_sxword d_tag;
|
||||
union {
|
||||
elf64_xword d_val;
|
||||
elf64_addr d_ptr;
|
||||
} d_un;
|
||||
};
|
||||
|
||||
#if defined(CONFIG_64BIT) || defined(__DOXYGEN__)
|
||||
/** Machine sized elf header structure */
|
||||
typedef struct elf64_ehdr elf_ehdr_t;
|
||||
/** Machine sized section header structure */
|
||||
typedef struct elf64_shdr elf_shdr_t;
|
||||
/** Machine sized program header structure */
|
||||
typedef struct elf64_phdr elf_phdr_t;
|
||||
/** Machine sized program address */
|
||||
typedef elf64_addr elf_addr;
|
||||
/** Machine sized small integer */
|
||||
typedef elf64_half elf_half;
|
||||
/** Machine sized integer */
|
||||
typedef elf64_xword elf_word;
|
||||
/** Machine sized relocation struct */
|
||||
typedef struct elf64_rela elf_rel_t;
|
||||
/** Machine sized symbol struct */
|
||||
typedef struct elf64_sym elf_sym_t;
|
||||
/** Machine sized macro alias for obtaining a relocation symbol */
|
||||
#define ELF_R_SYM ELF64_R_SYM
|
||||
/** Machine sized macro alias for obtaining a relocation type */
|
||||
#define ELF_R_TYPE ELF64_R_TYPE
|
||||
/** Machine sized macro alias for obtaining a symbol bind */
|
||||
#define ELF_ST_BIND ELF64_ST_BIND
|
||||
/** Machine sized macro alias for obtaining a symbol type */
|
||||
#define ELF_ST_TYPE ELF64_ST_TYPE
|
||||
#else
|
||||
/** Machine sized elf header structure */
|
||||
typedef struct elf32_ehdr elf_ehdr_t;
|
||||
/** Machine sized section header structure */
|
||||
typedef struct elf32_shdr elf_shdr_t;
|
||||
/** Machine sized program header structure */
|
||||
typedef struct elf32_phdr elf_phdr_t;
|
||||
/** Machine sized program address */
|
||||
typedef elf32_addr elf_addr;
|
||||
/** Machine sized small integer */
|
||||
typedef elf32_half elf_half;
|
||||
/** Machine sized integer */
|
||||
typedef elf32_word elf_word;
|
||||
/** Machine sized relocation struct */
|
||||
typedef struct elf32_rel elf_rel_t;
|
||||
/** Machine sized symbol struct */
|
||||
typedef struct elf32_sym elf_sym_t;
|
||||
/** Machine sized macro alias for obtaining a relocation symbol */
|
||||
#define ELF_R_SYM ELF32_R_SYM
|
||||
/** Machine sized macro alias for obtaining a relocation type */
|
||||
#define ELF_R_TYPE ELF32_R_TYPE
|
||||
/** Machine sized macro alias for obtaining a symbol bind */
|
||||
#define ELF_ST_BIND ELF32_ST_BIND
|
||||
/** Machine sized macro alias for obtaining a symbol type */
|
||||
#define ELF_ST_TYPE ELF32_ST_TYPE
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#endif /* ZEPHYR_LLEXT_ELF_H */
|
146
include/zephyr/llext/llext.h
Normal file
146
include/zephyr/llext/llext.h
Normal file
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_LLEXT_H
|
||||
#define ZEPHYR_LLEXT_H
|
||||
|
||||
#include <zephyr/sys/slist.h>
|
||||
#include <zephyr/llext/elf.h>
|
||||
#include <zephyr/llext/symbol.h>
|
||||
#include <zephyr/llext/loader.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Linkable loadable extensions
|
||||
* @defgroup llext Linkable loadable extensions
|
||||
* @ingroup os_services
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Enum of memory regions for lookup tables
|
||||
*/
|
||||
enum llext_mem {
|
||||
LLEXT_MEM_TEXT,
|
||||
LLEXT_MEM_DATA,
|
||||
LLEXT_MEM_RODATA,
|
||||
LLEXT_MEM_BSS,
|
||||
|
||||
LLEXT_MEM_COUNT,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Linkable loadable extension
|
||||
*/
|
||||
struct llext {
|
||||
/** @cond ignore */
|
||||
sys_snode_t _llext_list;
|
||||
/** @endcond */
|
||||
|
||||
/** Name of the llext */
|
||||
char name[16];
|
||||
|
||||
/** Lookup table of llext memory regions */
|
||||
void *mem[LLEXT_MEM_COUNT];
|
||||
|
||||
/** Total size of the llext memory usage */
|
||||
size_t mem_size;
|
||||
|
||||
/** Exported symbols from the llext, may be linked against by other llext */
|
||||
struct llext_symtable sym_tab;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief List head of loaded extensions
|
||||
*/
|
||||
sys_slist_t *llext_list(void);
|
||||
|
||||
/**
|
||||
* @brief Find an llext by name
|
||||
*
|
||||
* @param[in] name String name of the llext
|
||||
* @retval NULL if no llext not found
|
||||
* @retval llext if llext found
|
||||
*/
|
||||
struct llext *llext_by_name(const char *name);
|
||||
|
||||
/**
|
||||
* @brief Load and link an extension
|
||||
*
|
||||
* Loads relevant ELF data into memory and provides a structure to work with it.
|
||||
*
|
||||
* Only relocatable ELF files are currently supported (partially linked).
|
||||
*
|
||||
* @param[in] loader An extension loader that provides input data and context
|
||||
* @param[in] name A string identifier for the module
|
||||
* @param[out] ext A pointer to a statically allocated llext struct
|
||||
*
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief Unload an extension
|
||||
*
|
||||
* @param[in] ext Extension to unload
|
||||
*/
|
||||
void llext_unload(struct llext *ext);
|
||||
|
||||
/**
|
||||
* @brief Find the address for an arbitrary symbol name.
|
||||
*
|
||||
* @param[in] sym_table Symbol table to lookup symbol in, if NULL uses base table
|
||||
* @param[in] sym_name Symbol name to find
|
||||
*
|
||||
* @retval NULL if no symbol found
|
||||
* @retval addr Address of symbol in memory if found
|
||||
*/
|
||||
const void * const llext_find_sym(const struct llext_symtable *sym_table, const char *sym_name);
|
||||
|
||||
/**
|
||||
* @brief Call a function by name
|
||||
*
|
||||
* Expects a symbol representing a void fn(void) style function exists
|
||||
* and may be called.
|
||||
*
|
||||
* @param[in] ext Extension to call function in
|
||||
* @param[in] sym_name Function name (exported symbol) in the extension
|
||||
*
|
||||
* @retval 0 success
|
||||
* @retval -EINVAL invalid symbol name
|
||||
*/
|
||||
int llext_call_fn(struct llext *ext, const char *sym_name);
|
||||
|
||||
/**
|
||||
* @brief Architecture specific function for updating op codes given a relocation
|
||||
*
|
||||
* Elf files contain a series of relocations described in a section. These relocation
|
||||
* instructions are architecture specific and each architecture supporting extensions
|
||||
* must implement this. They are instructions on how to rewrite opcodes given
|
||||
* the actual placement of some symbolic data such as a section, function,
|
||||
* or object.
|
||||
*
|
||||
* @param[in] rel Relocation data provided by elf
|
||||
* @param[in] opaddr Address of operation to rewrite with relocation
|
||||
* @param[in] opval Value of looked up symbol to relocate
|
||||
*/
|
||||
void arch_elf_relocate(elf_rel_t *rel, uintptr_t opaddr, uintptr_t opval);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_MODULE_H */
|
95
include/zephyr/llext/loader.h
Normal file
95
include/zephyr/llext/loader.h
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_LLEXT_LOADER_H
|
||||
#define ZEPHYR_LLEXT_LOADER_H
|
||||
|
||||
#include <zephyr/llext/elf.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Loader context for llext
|
||||
* @defgroup llext_loader Loader context for llext
|
||||
* @ingroup llext
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Enum of sections for lookup tables
|
||||
*/
|
||||
enum llext_section {
|
||||
LLEXT_SECT_TEXT,
|
||||
LLEXT_SECT_DATA,
|
||||
LLEXT_SECT_RODATA,
|
||||
LLEXT_SECT_BSS,
|
||||
|
||||
LLEXT_SECT_REL_TEXT,
|
||||
LLEXT_SECT_REL_DATA,
|
||||
LLEXT_SECT_REL_RODATA,
|
||||
LLEXT_SECT_REL_BSS,
|
||||
|
||||
LLEXT_SECT_SYMTAB,
|
||||
LLEXT_SECT_STRTAB,
|
||||
LLEXT_SECT_SHSTRTAB,
|
||||
|
||||
LLEXT_SECT_COUNT,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Linkable loadable extension loader context
|
||||
*/
|
||||
struct llext_loader {
|
||||
/**
|
||||
* @brief Read (copy) from the loader
|
||||
*
|
||||
* Copies len bytes into buf from the current position of the
|
||||
* loader.
|
||||
*
|
||||
* @param[in] ldr Loader
|
||||
* @param[in] out Output location
|
||||
* @param[in] len Length to copy into the output location
|
||||
*
|
||||
* @retval 0 Success
|
||||
* @retval -errno Error reading (any errno)
|
||||
*/
|
||||
int (*read)(struct llext_loader *ldr, void *out, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Seek to a new absolute location
|
||||
*
|
||||
* Changes the location of the loader position to a new absolute
|
||||
* given position.
|
||||
*
|
||||
* @param[in] ldr Loader
|
||||
* @param[in] pos Position in stream to move loader
|
||||
*
|
||||
* @retval 0 Success
|
||||
* @retval -errno Error reading (any errno)
|
||||
*/
|
||||
int (*seek)(struct llext_loader *s, size_t pos);
|
||||
|
||||
/** @cond ignore */
|
||||
elf_ehdr_t hdr;
|
||||
elf_shdr_t sects[LLEXT_SECT_COUNT];
|
||||
uint32_t *sect_map;
|
||||
uint32_t sect_cnt;
|
||||
uint32_t sym_cnt;
|
||||
/** @endcond */
|
||||
};
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_LLEXT_LOADER_H */
|
90
include/zephyr/llext/symbol.h
Normal file
90
include/zephyr/llext/symbol.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_LLEXT_SYMBOL_H
|
||||
#define ZEPHYR_LLEXT_SYMBOL_H
|
||||
|
||||
#include <zephyr/sys/iterable_sections.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/**
|
||||
* @brief Linkable loadable extension symbol
|
||||
* @defgroup llext_symbols LLEXT symbols
|
||||
* @ingroup llext
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Constant symbols are unchangeable named memory addresses
|
||||
*
|
||||
* Symbols may be named function or global objects that have been exported
|
||||
* for linking. These constant symbols are useful in the base image
|
||||
* as they may be placed in ROM.
|
||||
*/
|
||||
struct llext_const_symbol {
|
||||
/** Name of symbol */
|
||||
const char *const name;
|
||||
|
||||
/** Address of symbol */
|
||||
const void *const addr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Symbols are named memory addresses
|
||||
*
|
||||
* Symbols may be named function or global objects that have been exported
|
||||
* for linking. These are mutable and should come from extensions where
|
||||
* the location may need updating depending on where memory is placed.
|
||||
*/
|
||||
struct llext_symbol {
|
||||
/** Name of symbol */
|
||||
char *name;
|
||||
|
||||
/** Address of symbol */
|
||||
void *addr;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief A symbol table
|
||||
*
|
||||
* An array of symbols
|
||||
*/
|
||||
struct llext_symtable {
|
||||
/** Number of symbols in the table */
|
||||
size_t sym_cnt;
|
||||
|
||||
/** Array of symbols */
|
||||
struct llext_symbol *syms;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Export a constant symbol to a table of symbols
|
||||
*
|
||||
* Takes a symbol (function or object) by symbolic name and adds the name
|
||||
* and address of the symbol to a table of symbols that may be used for linking.
|
||||
*
|
||||
* @param x Symbol to export
|
||||
*/
|
||||
#define EXPORT_SYMBOL(x) \
|
||||
static const STRUCT_SECTION_ITERABLE(llext_const_symbol, x ## _sym) = { \
|
||||
.name = STRINGIFY(x), .addr = x, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* ZEPHYR_LLEXT_SYMBOL_H */
|
|
@ -20,6 +20,7 @@
|
|||
#include <zephyr/syscall_handler.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/sys/cbprintf.h>
|
||||
#include <zephyr/llext/symbol.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
/* Option present only when CONFIG_USERSPACE enabled. */
|
||||
|
@ -210,6 +211,7 @@ void printk(const char *fmt, ...)
|
|||
|
||||
va_end(ap);
|
||||
}
|
||||
EXPORT_SYMBOL(printk);
|
||||
#endif /* defined(CONFIG_PRINTK) */
|
||||
|
||||
#ifndef CONFIG_PICOLIBC
|
||||
|
|
|
@ -43,6 +43,7 @@ add_subdirectory_ifdef(CONFIG_IMG_MANAGER dfu)
|
|||
add_subdirectory_ifdef(CONFIG_INPUT input)
|
||||
add_subdirectory_ifdef(CONFIG_JWT jwt)
|
||||
add_subdirectory_ifdef(CONFIG_MODEM_MODULES modem)
|
||||
add_subdirectory_ifdef(CONFIG_LLEXT llext)
|
||||
add_subdirectory_ifdef(CONFIG_NET_BUF net)
|
||||
add_subdirectory_ifdef(CONFIG_RETENTION retention)
|
||||
add_subdirectory_ifdef(CONFIG_SENSING sensing)
|
||||
|
|
|
@ -21,6 +21,7 @@ source "subsys/fs/Kconfig"
|
|||
source "subsys/input/Kconfig"
|
||||
source "subsys/ipc/Kconfig"
|
||||
source "subsys/jwt/Kconfig"
|
||||
source "subsys/llext/Kconfig"
|
||||
source "subsys/logging/Kconfig"
|
||||
source "subsys/lorawan/Kconfig"
|
||||
source "subsys/mem_mgmt/Kconfig"
|
||||
|
|
6
subsys/llext/CMakeLists.txt
Normal file
6
subsys/llext/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
if(CONFIG_LLEXT)
|
||||
zephyr_library()
|
||||
zephyr_library_sources(llext.c)
|
||||
zephyr_library_sources(buf_loader.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_LLEXT_SHELL shell.c)
|
||||
endif()
|
27
subsys/llext/Kconfig
Normal file
27
subsys/llext/Kconfig
Normal file
|
@ -0,0 +1,27 @@
|
|||
# Copyright (c) 2023 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig LLEXT
|
||||
bool "Linkable loadable extensions"
|
||||
help
|
||||
Enable the linkable loadable extension subsystem
|
||||
|
||||
if LLEXT
|
||||
|
||||
config LLEXT_HEAP_SIZE
|
||||
int "llext heap memory size in kilobytes"
|
||||
default 8
|
||||
help
|
||||
Heap size in kilobytes available to llext for dynamic allocation
|
||||
|
||||
config LLEXT_SHELL
|
||||
bool "llext shell commands"
|
||||
depends on SHELL
|
||||
help
|
||||
Manage llext with shell commands for loading, unloading, and introspection
|
||||
|
||||
module = LLEXT
|
||||
module-str = llext
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
|
||||
endif
|
31
subsys/llext/buf_loader.c
Normal file
31
subsys/llext/buf_loader.c
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#include <zephyr/llext/buf_loader.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <string.h>
|
||||
|
||||
int llext_buf_read(struct llext_loader *l, void *buf, size_t len)
|
||||
{
|
||||
struct llext_buf_loader *buf_l = CONTAINER_OF(l, struct llext_buf_loader, loader);
|
||||
size_t end = MIN(buf_l->pos + len, buf_l->len);
|
||||
size_t read_len = end - buf_l->pos;
|
||||
|
||||
memcpy(buf, buf_l->buf + buf_l->pos, read_len);
|
||||
buf_l->pos = end;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int llext_buf_seek(struct llext_loader *l, size_t pos)
|
||||
{
|
||||
struct llext_buf_loader *buf_l = CONTAINER_OF(l, struct llext_buf_loader, loader);
|
||||
|
||||
buf_l->pos = MIN(pos, buf_l->len);
|
||||
|
||||
return 0;
|
||||
}
|
757
subsys/llext/llext.c
Normal file
757
subsys/llext/llext.c
Normal file
|
@ -0,0 +1,757 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#include "zephyr/sys/__assert.h"
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/llext/elf.h>
|
||||
#include <zephyr/llext/loader.h>
|
||||
#include <zephyr/llext/llext.h>
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(llext, CONFIG_LLEXT_LOG_LEVEL);
|
||||
|
||||
#include <string.h>
|
||||
|
||||
K_HEAP_DEFINE(llext_heap, CONFIG_LLEXT_HEAP_SIZE * 1024);
|
||||
|
||||
static const char ELF_MAGIC[] = {0x7f, 'E', 'L', 'F'};
|
||||
|
||||
static inline int llext_read(struct llext_loader *l, void *buf, size_t len)
|
||||
{
|
||||
return l->read(l, buf, len);
|
||||
}
|
||||
|
||||
static inline int llext_seek(struct llext_loader *l, size_t pos)
|
||||
{
|
||||
return l->seek(l, pos);
|
||||
}
|
||||
|
||||
static sys_slist_t _llext_list = SYS_SLIST_STATIC_INIT(&_llext_list);
|
||||
|
||||
sys_slist_t *llext_list(void)
|
||||
{
|
||||
return &_llext_list;
|
||||
}
|
||||
|
||||
struct llext *llext_by_name(const char *name)
|
||||
{
|
||||
sys_slist_t *mlist = llext_list();
|
||||
sys_snode_t *node = sys_slist_peek_head(mlist);
|
||||
struct llext *ext = CONTAINER_OF(node, struct llext, _llext_list);
|
||||
|
||||
while (node != NULL) {
|
||||
if (strncmp(ext->name, name, sizeof(ext->name)) == 0) {
|
||||
return ext;
|
||||
}
|
||||
node = sys_slist_peek_next(node);
|
||||
ext = CONTAINER_OF(node, struct llext, _llext_list);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const void * const llext_find_sym(const struct llext_symtable *sym_table, const char *sym_name)
|
||||
{
|
||||
if (sym_table == NULL) {
|
||||
/* Buildin symbol table */
|
||||
STRUCT_SECTION_FOREACH(llext_const_symbol, sym) {
|
||||
if (strcmp(sym->name, sym_name) == 0) {
|
||||
return sym->addr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* find symbols in module */
|
||||
for (size_t i = 0; i < sym_table->sym_cnt; i++) {
|
||||
if (strcmp(sym_table->syms[i].name, sym_name) == 0) {
|
||||
return sym_table->syms[i].addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find all relevant string and symbol tables
|
||||
*/
|
||||
static int llext_find_tables(struct llext_loader *ldr)
|
||||
{
|
||||
int ret = 0;
|
||||
size_t pos = ldr->hdr.e_shoff;
|
||||
elf_shdr_t shdr;
|
||||
|
||||
ldr->sects[LLEXT_SECT_SHSTRTAB] =
|
||||
ldr->sects[LLEXT_SECT_STRTAB] =
|
||||
ldr->sects[LLEXT_SECT_SYMTAB] = (elf_shdr_t){0};
|
||||
|
||||
/* Find symbol and string tables */
|
||||
for (int i = 0, str_cnt = 0; i < ldr->hdr.e_shnum && str_cnt < 3; i++) {
|
||||
ret = llext_seek(ldr, pos);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("failed seeking to position %u\n", pos);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = llext_read(ldr, &shdr, sizeof(elf_shdr_t));
|
||||
if (ret != 0) {
|
||||
LOG_ERR("failed reading section header at position %u\n", pos);
|
||||
goto out;
|
||||
}
|
||||
|
||||
pos += ldr->hdr.e_shentsize;
|
||||
|
||||
LOG_DBG("section %d at %x: name %d, type %d, flags %x, addr %x, size %d",
|
||||
i,
|
||||
ldr->hdr.e_shoff + i * ldr->hdr.e_shentsize,
|
||||
shdr.sh_name,
|
||||
shdr.sh_type,
|
||||
shdr.sh_flags,
|
||||
shdr.sh_addr,
|
||||
shdr.sh_size);
|
||||
|
||||
switch (shdr.sh_type) {
|
||||
case SHT_SYMTAB:
|
||||
case SHT_DYNSYM:
|
||||
LOG_DBG("symtab at %d", i);
|
||||
ldr->sects[LLEXT_SECT_SYMTAB] = shdr;
|
||||
ldr->sect_map[i] = LLEXT_SECT_SYMTAB;
|
||||
str_cnt++;
|
||||
break;
|
||||
case SHT_STRTAB:
|
||||
if (ldr->hdr.e_shstrndx == i) {
|
||||
LOG_DBG("shstrtab at %d", i);
|
||||
ldr->sects[LLEXT_SECT_SHSTRTAB] = shdr;
|
||||
ldr->sect_map[i] = LLEXT_SECT_SHSTRTAB;
|
||||
} else {
|
||||
LOG_DBG("strtab at %d", i);
|
||||
ldr->sects[LLEXT_SECT_STRTAB] = shdr;
|
||||
ldr->sect_map[i] = LLEXT_SECT_STRTAB;
|
||||
}
|
||||
str_cnt++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ldr->sects[LLEXT_SECT_SHSTRTAB].sh_type ||
|
||||
!ldr->sects[LLEXT_SECT_STRTAB].sh_type ||
|
||||
!ldr->sects[LLEXT_SECT_SYMTAB].sh_type) {
|
||||
LOG_ERR("Some sections are missing or present multiple times!");
|
||||
ret = -ENOENT;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Maps the section indexes and copies special section headers for easier use
|
||||
*/
|
||||
static int llext_map_sections(struct llext_loader *ldr)
|
||||
{
|
||||
int ret = 0;
|
||||
size_t pos = ldr->hdr.e_shoff;
|
||||
elf_shdr_t shdr;
|
||||
char name[32];
|
||||
|
||||
for (int i = 0; i < ldr->hdr.e_shnum; i++) {
|
||||
ret = llext_seek(ldr, pos);
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = llext_read(ldr, &shdr, sizeof(elf_shdr_t));
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
pos += ldr->hdr.e_shentsize;
|
||||
|
||||
elf_word str_idx = shdr.sh_name;
|
||||
|
||||
ret = llext_seek(ldr, ldr->sects[LLEXT_SECT_SHSTRTAB].sh_offset + str_idx);
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = llext_read(ldr, name, sizeof(name));
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
name[sizeof(name) - 1] = '\0';
|
||||
|
||||
LOG_DBG("section %d name %s", i, name);
|
||||
|
||||
enum llext_section sect_idx;
|
||||
|
||||
if (strncmp(name, ".text", sizeof(name)) == 0) {
|
||||
sect_idx = LLEXT_SECT_TEXT;
|
||||
} else if (strncmp(name, ".data", sizeof(name)) == 0) {
|
||||
sect_idx = LLEXT_SECT_DATA;
|
||||
} else if (strncmp(name, ".rodata", sizeof(name)) == 0) {
|
||||
sect_idx = LLEXT_SECT_RODATA;
|
||||
} else if (strncmp(name, ".bss", sizeof(name)) == 0) {
|
||||
sect_idx = LLEXT_SECT_BSS;
|
||||
} else {
|
||||
LOG_DBG("Not copied section %s", name);
|
||||
continue;
|
||||
}
|
||||
|
||||
ldr->sects[sect_idx] = shdr;
|
||||
ldr->sect_map[i] = sect_idx;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline enum llext_section llext_sect_from_mem(enum llext_mem m)
|
||||
{
|
||||
enum llext_section s;
|
||||
|
||||
switch (m) {
|
||||
case LLEXT_MEM_BSS:
|
||||
s = LLEXT_SECT_BSS;
|
||||
break;
|
||||
case LLEXT_MEM_DATA:
|
||||
s = LLEXT_SECT_DATA;
|
||||
break;
|
||||
case LLEXT_MEM_RODATA:
|
||||
s = LLEXT_SECT_RODATA;
|
||||
break;
|
||||
case LLEXT_MEM_TEXT:
|
||||
s = LLEXT_SECT_TEXT;
|
||||
break;
|
||||
default:
|
||||
CODE_UNREACHABLE;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static int llext_allocate_mem(struct llext_loader *ldr, struct llext *ext)
|
||||
{
|
||||
int ret = 0;
|
||||
enum llext_section sect_idx;
|
||||
|
||||
for (enum llext_mem mem_idx = 0; mem_idx < LLEXT_MEM_COUNT; mem_idx++) {
|
||||
sect_idx = llext_sect_from_mem(mem_idx);
|
||||
|
||||
if (ldr->sects[sect_idx].sh_size > 0) {
|
||||
ext->mem[mem_idx] =
|
||||
k_heap_aligned_alloc(&llext_heap, sizeof(uintptr_t),
|
||||
ldr->sects[sect_idx].sh_size,
|
||||
K_NO_WAIT);
|
||||
|
||||
if (ext->mem[mem_idx] == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int llext_copy_sections(struct llext_loader *ldr, struct llext *ext)
|
||||
{
|
||||
int ret = 0;
|
||||
enum llext_section sect_idx;
|
||||
|
||||
for (enum llext_mem mem_idx = 0; mem_idx < LLEXT_MEM_COUNT; mem_idx++) {
|
||||
sect_idx = llext_sect_from_mem(mem_idx);
|
||||
|
||||
if (ldr->sects[sect_idx].sh_size > 0) {
|
||||
ret = llext_seek(ldr, ldr->sects[sect_idx].sh_offset);
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = llext_read(ldr, ext->mem[mem_idx], ldr->sects[sect_idx].sh_size);
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int llext_count_export_syms(struct llext_loader *ldr)
|
||||
{
|
||||
int ret = 0;
|
||||
elf_sym_t sym;
|
||||
size_t ent_size = ldr->sects[LLEXT_SECT_SYMTAB].sh_entsize;
|
||||
size_t syms_size = ldr->sects[LLEXT_SECT_SYMTAB].sh_size;
|
||||
size_t pos = ldr->sects[LLEXT_SECT_SYMTAB].sh_offset;
|
||||
size_t sym_cnt = syms_size / sizeof(elf_sym_t);
|
||||
char name[32];
|
||||
|
||||
LOG_DBG("symbol count %u", sym_cnt);
|
||||
|
||||
for (int i = 0; i < sym_cnt; i++) {
|
||||
ret = llext_seek(ldr, pos);
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = llext_read(ldr, &sym, ent_size);
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
pos += ent_size;
|
||||
|
||||
uint32_t stt = ELF_ST_TYPE(sym.st_info);
|
||||
uint32_t stb = ELF_ST_BIND(sym.st_info);
|
||||
uint32_t sect = sym.st_shndx;
|
||||
|
||||
ret = llext_seek(ldr, ldr->sects[LLEXT_SECT_STRTAB].sh_offset + sym.st_name);
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = llext_read(ldr, name, sizeof(name));
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
name[sizeof(name) - 1] = '\0';
|
||||
|
||||
if (stt == STT_FUNC && stb == STB_GLOBAL) {
|
||||
LOG_DBG("function symbol %d, name %s, type tag %d, bind %d, sect %d",
|
||||
i, name, stt, stb, sect);
|
||||
ldr->sym_cnt++;
|
||||
} else {
|
||||
LOG_DBG("unhandled symbol %d, name %s, type tag %d, bind %d, sect %d",
|
||||
i, name, stt, stb, sect);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int llext_allocate_symtab(struct llext_loader *ldr, struct llext *ext)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ext->sym_tab.syms = k_heap_alloc(&llext_heap, ldr->sym_cnt * sizeof(struct llext_symbol),
|
||||
K_NO_WAIT);
|
||||
ext->sym_tab.sym_cnt = ldr->sym_cnt;
|
||||
memset(ext->sym_tab.syms, 0, ldr->sym_cnt * sizeof(struct llext_symbol));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int llext_copy_symbols(struct llext_loader *ldr, struct llext *ext)
|
||||
{
|
||||
int ret = 0;
|
||||
elf_sym_t sym;
|
||||
size_t ent_size = ldr->sects[LLEXT_SECT_SYMTAB].sh_entsize;
|
||||
size_t syms_size = ldr->sects[LLEXT_SECT_SYMTAB].sh_size;
|
||||
size_t pos = ldr->sects[LLEXT_SECT_SYMTAB].sh_offset;
|
||||
size_t sym_cnt = syms_size / sizeof(elf_sym_t);
|
||||
char name[32];
|
||||
int i, j = 0;
|
||||
|
||||
for (i = 0; i < sym_cnt; i++) {
|
||||
ret = llext_seek(ldr, pos);
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = llext_read(ldr, &sym, ent_size);
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
pos += ent_size;
|
||||
|
||||
uint32_t stt = ELF_ST_TYPE(sym.st_info);
|
||||
uint32_t stb = ELF_ST_BIND(sym.st_info);
|
||||
uint32_t sect = sym.st_shndx;
|
||||
|
||||
ret = llext_seek(ldr, ldr->sects[LLEXT_SECT_STRTAB].sh_offset + sym.st_name);
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
llext_read(ldr, name, sizeof(name));
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (stt == STT_FUNC && stb == STB_GLOBAL && sect != SHN_UNDEF) {
|
||||
ext->sym_tab.syms[j].name = k_heap_alloc(&llext_heap,
|
||||
sizeof(name),
|
||||
K_NO_WAIT);
|
||||
strcpy(ext->sym_tab.syms[j].name, name);
|
||||
ext->sym_tab.syms[j].addr =
|
||||
(void *)((uintptr_t)ext->mem[ldr->sect_map[sym.st_shndx]]
|
||||
+ sym.st_value);
|
||||
LOG_DBG("function symbol %d name %s addr %p",
|
||||
j, name, ext->sym_tab.syms[j].addr);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int llext_link(struct llext_loader *ldr, struct llext *ext)
|
||||
{
|
||||
int ret = 0;
|
||||
uintptr_t loc = 0;
|
||||
elf_shdr_t shdr;
|
||||
elf_rel_t rel;
|
||||
elf_sym_t sym;
|
||||
size_t pos = ldr->hdr.e_shoff;
|
||||
elf_word rel_cnt = 0;
|
||||
char name[32];
|
||||
|
||||
for (int i = 0; i < ldr->hdr.e_shnum - 1; i++) {
|
||||
ret = llext_seek(ldr, pos);
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = llext_read(ldr, &shdr, sizeof(elf_shdr_t));
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
pos += ldr->hdr.e_shentsize;
|
||||
|
||||
/* find relocation sections */
|
||||
if (shdr.sh_type != SHT_REL && shdr.sh_type != SHT_RELA) {
|
||||
continue;
|
||||
}
|
||||
|
||||
rel_cnt = shdr.sh_size / sizeof(elf_rel_t);
|
||||
|
||||
|
||||
ret = llext_seek(ldr, ldr->sects[LLEXT_SECT_SHSTRTAB].sh_offset + shdr.sh_name);
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = llext_read(ldr, name, sizeof(name));
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (strncmp(name, ".rel.text", sizeof(name)) == 0 ||
|
||||
strncmp(name, ".rela.text", sizeof(name)) == 0) {
|
||||
loc = (uintptr_t)ext->mem[LLEXT_MEM_TEXT];
|
||||
} else if (strncmp(name, ".rel.bss", sizeof(name)) == 0) {
|
||||
loc = (uintptr_t)ext->mem[LLEXT_MEM_BSS];
|
||||
} else if (strncmp(name, ".rel.rodata", sizeof(name)) == 0) {
|
||||
loc = (uintptr_t)ext->mem[LLEXT_MEM_RODATA];
|
||||
} else if (strncmp(name, ".rel.data", sizeof(name)) == 0) {
|
||||
loc = (uintptr_t)ext->mem[LLEXT_MEM_DATA];
|
||||
}
|
||||
|
||||
LOG_DBG("relocation section %s (%d) linked to section %d has %d relocations",
|
||||
name, i, shdr.sh_link, rel_cnt);
|
||||
|
||||
for (int j = 0; j < rel_cnt; j++) {
|
||||
/* get each relocation entry */
|
||||
ret = llext_seek(ldr, shdr.sh_offset + j * sizeof(elf_rel_t));
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = llext_read(ldr, &rel, sizeof(elf_rel_t));
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* get corresponding symbol */
|
||||
ret = llext_seek(ldr, ldr->sects[LLEXT_SECT_SYMTAB].sh_offset
|
||||
+ ELF_R_SYM(rel.r_info) * sizeof(elf_sym_t));
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = llext_read(ldr, &sym, sizeof(elf_sym_t));
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = llext_seek(ldr, ldr->sects[LLEXT_SECT_STRTAB].sh_offset +
|
||||
sym.st_name);
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = llext_read(ldr, name, sizeof(name));
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
LOG_DBG("relocation %d:%d info %x (type %d, sym %d) offset %d sym_name "
|
||||
"%s sym_type %d sym_bind %d sym_ndx %d",
|
||||
i, j, rel.r_info, ELF_R_TYPE(rel.r_info), ELF_R_SYM(rel.r_info),
|
||||
rel.r_offset, name, ELF_ST_TYPE(sym.st_info),
|
||||
ELF_ST_BIND(sym.st_info), sym.st_shndx);
|
||||
|
||||
uintptr_t link_addr, op_loc, op_code;
|
||||
|
||||
/* If symbol is undefined, then we need to look it up */
|
||||
if (sym.st_shndx == SHN_UNDEF) {
|
||||
link_addr = (uintptr_t)llext_find_sym(NULL, name);
|
||||
|
||||
if (link_addr == 0) {
|
||||
LOG_ERR("Undefined symbol with no entry in "
|
||||
"symbol table %s, offset %d, link section %d",
|
||||
name, rel.r_offset, shdr.sh_link);
|
||||
ret = -ENODATA;
|
||||
goto out;
|
||||
} else {
|
||||
op_code = (uintptr_t)(loc + rel.r_offset);
|
||||
|
||||
LOG_INF("found symbol %s at 0x%lx, updating op code 0x%lx",
|
||||
name, link_addr, op_code);
|
||||
}
|
||||
} else if (ELF_ST_TYPE(sym.st_info) == STT_SECTION) {
|
||||
link_addr = (uintptr_t)ext->mem[ldr->sect_map[sym.st_shndx]];
|
||||
|
||||
LOG_INF("found section symbol %s addr 0x%lx", name, link_addr);
|
||||
} else {
|
||||
/* Nothing to relocate here */
|
||||
continue;
|
||||
}
|
||||
|
||||
op_loc = loc + rel.r_offset;
|
||||
|
||||
LOG_INF("relocating (linking) symbol %s type %d binding %d ndx %d offset "
|
||||
"%d link section %d",
|
||||
name, ELF_ST_TYPE(sym.st_info), ELF_ST_BIND(sym.st_info),
|
||||
sym.st_shndx, rel.r_offset, shdr.sh_link);
|
||||
|
||||
LOG_INF("writing relocation symbol %s type %d sym %d at addr 0x%lx "
|
||||
"addr 0x%lx",
|
||||
name, ELF_R_TYPE(rel.r_info), ELF_R_SYM(rel.r_info),
|
||||
op_loc, link_addr);
|
||||
|
||||
/* relocation */
|
||||
arch_elf_relocate(&rel, op_loc, link_addr);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Load a valid ELF as an extension
|
||||
*/
|
||||
static int do_llext_load(struct llext_loader *ldr, struct llext *ext)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
memset(ldr->sects, 0, sizeof(ldr->sects));
|
||||
ldr->sect_cnt = 0;
|
||||
ldr->sym_cnt = 0;
|
||||
|
||||
size_t sect_map_sz = ldr->hdr.e_shnum * sizeof(uint32_t);
|
||||
|
||||
ldr->sect_map = k_heap_alloc(&llext_heap, sect_map_sz, K_NO_WAIT);
|
||||
if (!ldr->sect_map) {
|
||||
LOG_ERR("Failed to allocate memory for section map, size %u", sect_map_sz);
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
memset(ldr->sect_map, 0, ldr->hdr.e_shnum*sizeof(uint32_t));
|
||||
ldr->sect_cnt = ldr->hdr.e_shnum;
|
||||
|
||||
LOG_DBG("Finding ELF tables...");
|
||||
ret = llext_find_tables(ldr);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Failed to find important ELF tables, ret %d", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
LOG_DBG("Mapping ELF sections...");
|
||||
ret = llext_map_sections(ldr);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Failed to map ELF sections, ret %d", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
LOG_DBG("Allocation memory for ELF sections...");
|
||||
ret = llext_allocate_mem(ldr, ext);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Failed to map memory for ELF sections, ret %d", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
LOG_DBG("Copying sections...");
|
||||
ret = llext_copy_sections(ldr, ext);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Failed to copy ELF sections, ret %d", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
LOG_DBG("Counting exported symbols...");
|
||||
ret = llext_count_export_syms(ldr);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Failed to count exported ELF symbols, ret %d", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
LOG_DBG("Allocating memory for symbol table...");
|
||||
ret = llext_allocate_symtab(ldr, ext);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Failed to allocate extension symbol table, ret %d", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
LOG_DBG("Copying symbols...");
|
||||
ret = llext_copy_symbols(ldr, ext);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Failed to copy symbols, ret %d", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
LOG_DBG("Linking ELF...");
|
||||
ret = llext_link(ldr, ext);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Failed to link, ret %d", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (ldr->sect_map != NULL) {
|
||||
k_heap_free(&llext_heap, ldr->sect_map);
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
LOG_DBG("Failed to load extension, freeing memory...");
|
||||
for (enum llext_mem mem_idx = 0; mem_idx < LLEXT_MEM_COUNT; mem_idx++) {
|
||||
if (ext->mem[mem_idx] != NULL) {
|
||||
k_heap_free(&llext_heap, ext->mem[mem_idx]);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < ext->sym_tab.sym_cnt; i++) {
|
||||
if (ext->sym_tab.syms[i].name != NULL) {
|
||||
k_heap_free(&llext_heap, ext->sym_tab.syms[i].name);
|
||||
}
|
||||
}
|
||||
k_heap_free(&llext_heap, ext->sym_tab.syms);
|
||||
} else {
|
||||
LOG_DBG("loaded module, .text at %p, .rodata at %p", ext->mem[LLEXT_MEM_TEXT],
|
||||
ext->mem[LLEXT_MEM_RODATA]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int llext_load(struct llext_loader *ldr, const char *name, struct llext **ext)
|
||||
{
|
||||
int ret = 0;
|
||||
elf_ehdr_t ehdr;
|
||||
|
||||
ret = llext_seek(ldr, 0);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Failed to seek for ELF header");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = llext_read(ldr, &ehdr, sizeof(ehdr));
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Failed to read ELF header");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* check whether this is an valid elf file */
|
||||
if (memcmp(ehdr.e_ident, ELF_MAGIC, sizeof(ELF_MAGIC)) != 0) {
|
||||
LOG_HEXDUMP_ERR(ehdr.e_ident, 16, "Invalid ELF, magic does not match");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (ehdr.e_type) {
|
||||
case ET_REL:
|
||||
case ET_DYN:
|
||||
LOG_DBG("Loading relocatable or shared elf");
|
||||
*ext = k_heap_alloc(&llext_heap, sizeof(struct llext), K_NO_WAIT);
|
||||
if (*ext == NULL) {
|
||||
LOG_ERR("Not enough memory for extension metadata");
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
memset(*ext, 0, sizeof(struct llext));
|
||||
|
||||
for (int i = 0; i < LLEXT_MEM_COUNT; i++) {
|
||||
(*ext)->mem[i] = NULL;
|
||||
}
|
||||
|
||||
ldr->hdr = ehdr;
|
||||
ret = do_llext_load(ldr, *ext);
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Unsupported elf file type %x", ehdr.e_type);
|
||||
*ext = NULL;
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
strncpy((*ext)->name, name, sizeof((*ext)->name));
|
||||
(*ext)->name[sizeof((*ext)->name) - 1] = '\0';
|
||||
sys_slist_append(&_llext_list, &(*ext)->_llext_list);
|
||||
LOG_INF("Loaded extension %s", (*ext)->name);
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void llext_unload(struct llext *ext)
|
||||
{
|
||||
__ASSERT(ext, "Expected non-null extension");
|
||||
|
||||
sys_slist_find_and_remove(&_llext_list, &ext->_llext_list);
|
||||
|
||||
for (int i = 0; i < LLEXT_MEM_COUNT; i++) {
|
||||
if (ext->mem[i] != NULL) {
|
||||
LOG_DBG("freeing memory region %d", i);
|
||||
k_heap_free(&llext_heap, ext->mem[i]);
|
||||
ext->mem[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (ext->sym_tab.syms != NULL) {
|
||||
for (int i = 0; i < ext->sym_tab.sym_cnt; i++) {
|
||||
k_heap_free(&llext_heap, ext->sym_tab.syms[i].name);
|
||||
}
|
||||
k_heap_free(&llext_heap, ext->sym_tab.syms);
|
||||
}
|
||||
|
||||
k_heap_free(&llext_heap, ext);
|
||||
}
|
||||
|
||||
int llext_call_fn(struct llext *ext, const char *sym_name)
|
||||
{
|
||||
void (*fn)(void);
|
||||
|
||||
fn = llext_find_sym(&ext->sym_tab, sym_name);
|
||||
if (fn == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
fn();
|
||||
|
||||
return 0;
|
||||
}
|
178
subsys/llext/shell.c
Normal file
178
subsys/llext/shell.c
Normal file
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#include <zephyr/sys/slist.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/shell/shell.h>
|
||||
#include <zephyr/llext/elf.h>
|
||||
#include <zephyr/llext/llext.h>
|
||||
#include <zephyr/llext/buf_loader.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(llext_shell, CONFIG_LLEXT_LOG_LEVEL);
|
||||
|
||||
#define LLEXT_LIST_HELP "List loaded extensions and their size in memory"
|
||||
|
||||
#define LLEXT_LOAD_HEX_HELP \
|
||||
"Load an elf file encoded in hex directly from the shell input. Syntax:\n" \
|
||||
"<ext_name> <ext_hex_string>"
|
||||
|
||||
#define LLEXT_UNLOAD_HELP \
|
||||
"Unload an extension by name. Syntax:\n" \
|
||||
"<ext_name>"
|
||||
|
||||
#define LLEXT_LIST_SYMBOLS_HELP \
|
||||
"List extension symbols. Syntax:\n" \
|
||||
"<ext_name>"
|
||||
|
||||
#define LLEXT_CALL_FN_HELP \
|
||||
"Call extension function with prototype void fn(void). Syntax:\n" \
|
||||
"<ext_name> <function_name>"
|
||||
|
||||
static int cmd_llext_list_symbols(const struct shell *sh, size_t argc, char *argv[])
|
||||
{
|
||||
struct llext *m = llext_by_name(argv[1]);
|
||||
|
||||
if (m == NULL) {
|
||||
shell_print(sh, "No such llext %s", argv[1]);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
shell_print(sh, "Extension: %s symbols", m->name);
|
||||
shell_print(sh, "| Symbol | Address |\n");
|
||||
for (elf_word i = 0; i < m->sym_tab.sym_cnt; i++) {
|
||||
shell_print(sh, "| %16s | %p |\n", m->sym_tab.syms[i].name,
|
||||
m->sym_tab.syms[i].addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void llext_name_get(size_t idx, struct shell_static_entry *entry)
|
||||
{
|
||||
sys_slist_t *ext_list = llext_list();
|
||||
sys_snode_t *node = sys_slist_peek_head(ext_list);
|
||||
|
||||
entry->syntax = NULL;
|
||||
|
||||
for (int i = 0; i < idx; i++) {
|
||||
node = sys_slist_peek_next(node);
|
||||
|
||||
if (node == NULL) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
struct llext *ext = CONTAINER_OF(node, struct llext, _llext_list);
|
||||
|
||||
entry->syntax = ext->name;
|
||||
out:
|
||||
entry->syntax = NULL;
|
||||
entry->help = NULL;
|
||||
entry->subcmd = NULL;
|
||||
|
||||
}
|
||||
|
||||
SHELL_DYNAMIC_CMD_CREATE(msub_llext_name, llext_name_get);
|
||||
|
||||
static int cmd_llext_list(const struct shell *sh, size_t argc, char *argv[])
|
||||
{
|
||||
sys_snode_t *node;
|
||||
struct llext *ext;
|
||||
|
||||
shell_print(sh, "| Name | Size |\n");
|
||||
SYS_SLIST_FOR_EACH_NODE(llext_list(), node) {
|
||||
ext = CONTAINER_OF(node, struct llext, _llext_list);
|
||||
shell_print(sh, "| %16s | %12d |\n", ext->name, ext->mem_size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define LLEXT_MAX_SIZE 8192
|
||||
static uint8_t llext_buf[LLEXT_MAX_SIZE];
|
||||
|
||||
static int cmd_llext_load_hex(const struct shell *sh, size_t argc, char *argv[])
|
||||
{
|
||||
char name[16];
|
||||
size_t hex_len = strnlen(argv[2], LLEXT_MAX_SIZE*2+1);
|
||||
size_t bin_len = hex_len/2;
|
||||
|
||||
if (bin_len > LLEXT_MAX_SIZE) {
|
||||
shell_print(sh, "Extension %d bytes too large to load, max %d bytes\n", hex_len/2,
|
||||
LLEXT_MAX_SIZE);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
strncpy(name, argv[1], sizeof(name));
|
||||
|
||||
size_t llext_buf_len = hex2bin(argv[2], hex_len, llext_buf, LLEXT_MAX_SIZE);
|
||||
struct llext_buf_loader buf_loader = LLEXT_BUF_LOADER(llext_buf, llext_buf_len);
|
||||
struct llext_loader *ldr = &buf_loader.loader;
|
||||
|
||||
LOG_DBG("hex2bin hex len %d, llext buf sz %d, read %d",
|
||||
hex_len, LLEXT_MAX_SIZE, llext_buf_len);
|
||||
LOG_HEXDUMP_DBG(llext_buf, 4, "4 byte MAGIC");
|
||||
|
||||
struct llext *ext;
|
||||
int res = llext_load(ldr, name, &ext);
|
||||
|
||||
if (res == 0) {
|
||||
shell_print(sh, "Successfully loaded extension %s, addr %p\n", ext->name, ext);
|
||||
} else {
|
||||
shell_print(sh, "Failed to load extension %s, return code %d\n", name, res);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_llext_unload(const struct shell *sh, size_t argc, char *argv[])
|
||||
{
|
||||
struct llext *ext = llext_by_name(argv[1]);
|
||||
|
||||
if (ext == NULL) {
|
||||
shell_print(sh, "No such extension %s", argv[1]);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
llext_unload(ext);
|
||||
shell_print(sh, "Unloaded extension %s\n", argv[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_llext_call_fn(const struct shell *sh, size_t argc, char *argv[])
|
||||
{
|
||||
struct llext *ext = llext_by_name(argv[1]);
|
||||
|
||||
if (ext == NULL) {
|
||||
shell_print(sh, "No such extension %s", argv[1]);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
llext_call_fn(ext, argv[2]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* clang-format off */
|
||||
SHELL_STATIC_SUBCMD_SET_CREATE(sub_llext,
|
||||
SHELL_CMD(list, NULL, LLEXT_LIST_HELP, cmd_llext_list),
|
||||
SHELL_CMD_ARG(load_hex, NULL, LLEXT_LOAD_HEX_HELP, cmd_llext_load_hex,
|
||||
3, 0),
|
||||
SHELL_CMD_ARG(unload, &msub_llext_name, LLEXT_UNLOAD_HELP, cmd_llext_unload, 2, 0),
|
||||
SHELL_CMD_ARG(list_symbols, &msub_llext_name, LLEXT_LIST_SYMBOLS_HELP,
|
||||
cmd_llext_list_symbols, 2, 0),
|
||||
SHELL_CMD_ARG(call_fn, &msub_llext_name, LLEXT_CALL_FN_HELP,
|
||||
cmd_llext_call_fn, 3, 0),
|
||||
|
||||
SHELL_SUBCMD_SET_END
|
||||
);
|
||||
/* clang-format on */
|
||||
|
||||
SHELL_CMD_REGISTER(llext, &sub_llext, "Loadable extension commands", NULL);
|
Loading…
Reference in a new issue