/* * Copyright (c) 2023 Intel Corporation * Copyright (c) 2024 Schneider Electric * * SPDX-License-Identifier: Apache-2.0 */ #ifndef ZEPHYR_LLEXT_H #define ZEPHYR_LLEXT_H #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif /** * @file * @brief Support for linkable loadable extensions * * This file describes the APIs for loading and interacting with Linkable * Loadable Extensions (LLEXTs) in Zephyr. * * @defgroup llext_apis Linkable loadable extensions * @since 3.5 * @version 0.1.0 * @ingroup os_services * @{ */ /** * @brief List of memory regions stored or referenced in the LLEXT subsystem * * This enum lists the different types of memory regions that are used by the * LLEXT subsystem. The names match common ELF file section names; but note * that at load time multiple ELF sections with similar flags may be merged * together into a single memory region. */ enum llext_mem { LLEXT_MEM_TEXT, /**< Executable code */ LLEXT_MEM_DATA, /**< Initialized data */ LLEXT_MEM_RODATA, /**< Read-only data */ LLEXT_MEM_BSS, /**< Uninitialized data */ LLEXT_MEM_EXPORT, /**< Exported symbol table */ LLEXT_MEM_SYMTAB, /**< Symbol table */ LLEXT_MEM_STRTAB, /**< Symbol name strings */ LLEXT_MEM_SHSTRTAB, /**< Section name strings */ LLEXT_MEM_PREINIT, /**< Array of early setup functions */ LLEXT_MEM_INIT, /**< Array of setup functions */ LLEXT_MEM_FINI, /**< Array of cleanup functions */ LLEXT_MEM_COUNT, /**< Number of regions managed by LLEXT */ }; /** @cond ignore */ /* Number of memory partitions used by LLEXT */ #define LLEXT_MEM_PARTITIONS (LLEXT_MEM_BSS+1) struct llext_loader; /** @endcond */ /* Maximim number of dependency LLEXTs */ #define LLEXT_MAX_DEPENDENCIES 8 /** * @brief Structure describing a linkable loadable extension * * This structure holds the data for a loaded extension. It is created by the * @ref llext_load function and destroyed by the @ref llext_unload function. */ struct llext { /** @cond ignore */ sys_snode_t _llext_list; #ifdef CONFIG_USERSPACE struct k_mem_partition mem_parts[LLEXT_MEM_PARTITIONS]; struct k_mem_domain mem_domain; #endif /** @endcond */ /** Name of the llext */ char name[16]; /** Lookup table of memory regions */ void *mem[LLEXT_MEM_COUNT]; /** Is the memory for this region allocated on heap? */ bool mem_on_heap[LLEXT_MEM_COUNT]; /** Size of each stored region */ size_t mem_size[LLEXT_MEM_COUNT]; /** Total llext allocation size */ size_t alloc_size; /** * Table of all global symbols in the extension; used internally as * part of the linking process. E.g. if the extension is built out of * several files, if any symbols are referenced between files, this * table will be used to link them. */ struct llext_symtable sym_tab; /** * Table of symbols exported by the llext via @ref LL_EXTENSION_SYMBOL. * This can be used in the main Zephyr binary to find symbols in the * extension. */ struct llext_symtable exp_tab; /** Extension use counter, prevents unloading while in use */ unsigned int use_count; /** 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 * * This structure contains advanced parameters for @ref llext_load. */ struct llext_load_param { /** Perform local relocation */ bool relocate_local; /** * Use the virtual symbol addresses from the ELF, not addresses within * the memory buffer, when calculating relocation targets. It also * means, that the application will take care to place the extension at * those pre-defined addresses, so the LLEXT core doesn't have to do any * allocation and copying internally. */ bool pre_located; /** * Extensions can implement custom ELF sections to be loaded in specific * memory regions, detached from other sections of compatible types. * This optional callback checks whether a section should be detached. */ bool (*section_detached)(const elf_shdr_t *shdr); }; /** Default initializer for @ref llext_load_param */ #define LLEXT_LOAD_PARAM_DEFAULT { .relocate_local = true, } /** * @brief Find an llext by name * * @param[in] name String name of the llext * @returns a pointer to the @ref llext, or `NULL` if not found */ struct llext *llext_by_name(const char *name); /** * @brief Iterate over all loaded extensions * * Calls a provided callback function for each registered extension or until the * callback function returns a non-0 value. * * @param[in] fn callback function * @param[in] arg a private argument to be provided to the callback function * @returns the value returned by the last callback invocation * @retval 0 if no extensions are registered */ int llext_iterate(int (*fn)(struct llext *ext, void *arg), void *arg); /** * @brief Load and link an extension * * Loads relevant ELF data into memory and provides a structure to work with it. * * @param[in] loader An extension loader that provides input data and context * @param[in] name A string identifier for the extension * @param[out] ext Pointer to the newly allocated @ref llext structure * @param[in] ldr_parm Optional advanced load parameters (may be `NULL`) * * @returns the previous extension use count on success, or a negative error code. * @retval -ENOMEM Not enough memory * @retval -ENOEXEC Invalid ELF stream * @retval -ENOTSUP Unsupported ELF features */ int llext_load(struct llext_loader *loader, const char *name, struct llext **ext, const struct llext_load_param *ldr_parm); /** * @brief Unload an extension * * @param[in] ext Extension to unload */ int llext_unload(struct llext **ext); /** @brief Entry point function signature for an extension. */ typedef void (*llext_entry_fn_t)(void *user_data); /** * @brief Calls bringup functions for an extension. * * Must be called before accessing any symbol in the extension. Will execute * the extension's own setup functions in the caller context. * @see llext_bootstrap * * @param[in] ext Extension to initialize. * @returns 0 on success, or a negative error code. * @retval -EFAULT A relocation issue was detected */ int llext_bringup(struct llext *ext); /** * @brief Calls teardown functions for an extension. * * Will execute the extension's own cleanup functions in the caller context. * After this function completes, the extension is no longer usable and must be * fully unloaded with @ref llext_unload. * @see llext_bootstrap * * @param[in] ext Extension to de-initialize. * @returns 0 on success, or a negative error code. * @retval -EFAULT A relocation issue was detected */ int llext_teardown(struct llext *ext); /** * @brief Bring up, execute, and teardown an extension. * * Calls the extension's own setup functions, an additional entry point and * the extension's cleanup functions in the current thread context. * * This is a convenient wrapper around @ref llext_bringup and @ref * llext_teardown that matches the @ref k_thread_entry_t signature, so it can * be directly started as a new user or kernel thread via @ref k_thread_create. * * @param[in] ext Extension to execute. Passed as `p1` in @ref k_thread_create. * @param[in] entry_fn Main entry point of the thread after performing * extension setup. Passed as `p2` in @ref k_thread_create. * @param[in] user_data Argument passed to @a entry_fn. Passed as `p3` in * @ref k_thread_create. */ void llext_bootstrap(struct llext *ext, llext_entry_fn_t entry_fn, void *user_data); /** * @brief Get pointers to setup or cleanup functions for an extension. * * This syscall can be used to get the addresses of all the functions that * have to be called for full extension setup or cleanup. * * @see llext_bootstrap * * @param[in] ext Extension to initialize. * @param[in] is_init `true` to get functions to be called at setup time, * `false` to get the cleanup ones. * @param[inout] buf Buffer to store the function pointers in. Can be `NULL` * to only get the minimum required size. * @param[in] size Allocated size of the buffer in bytes. * @returns the size used by the array in bytes, or a negative error code. * @retval -EFAULT A relocation issue was detected * @retval -ENOMEM Array does not fit in the allocated buffer */ __syscall ssize_t llext_get_fn_table(struct llext *ext, bool is_init, void *buf, size_t size); /** * @brief Find the address for an arbitrary symbol. * * Searches for a symbol address, either in the list of symbols exported by * the main Zephyr binary or in an extension's symbol table. * * @param[in] sym_table Symbol table to lookup symbol in, or `NULL` to search * in the main Zephyr symbol table * @param[in] sym_name Symbol name to find * * @returns the address of symbol in memory, or `NULL` if not found */ const void *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 -ENOENT Symbol name not found */ int llext_call_fn(struct llext *ext, const char *sym_name); /** * @brief Add an extension to a memory domain. * * Allows an extension to be executed in user mode threads when memory * protection hardware is enabled by adding memory partitions covering the * extension's memory regions to a memory domain. * * @param[in] ext Extension to add to a domain * @param[in] domain Memory domain to add partitions to * * @returns 0 on success, or a negative error code. * @retval -ENOSYS Option @kconfig{CONFIG_USERSPACE} is not enabled or supported */ int llext_add_domain(struct llext *ext, struct k_mem_domain *domain); /** * @brief Architecture specific opcode update function * * ELF files include sections describing a series of _relocations_, which are * instructions on how to rewrite opcodes given the actual placement of some * symbolic data such as a section, function, or object. These relocations * are architecture specific and each architecture supporting LLEXT must * implement this. * * @param[in] rel Relocation data provided by ELF * @param[in] loc Address of opcode to rewrite * @param[in] sym_base_addr Address of symbol referenced by relocation * @param[in] sym_name Name of symbol referenced by relocation * @param[in] load_bias `.text` load address * @retval 0 Success * @retval -ENOTSUP Unsupported relocation * @retval -ENOEXEC Invalid relocation */ int arch_elf_relocate(elf_rela_t *rel, uintptr_t loc, uintptr_t sym_base_addr, const char *sym_name, uintptr_t load_bias); /** * @brief Locates an ELF section in the file. * * Searches for a section by name in the ELF file and returns its offset. * * @param[in] loader Extension loader data and context * @param[in] search_name Section name to search for * @returns the section offset or a negative error code */ ssize_t llext_find_section(struct llext_loader *loader, const char *search_name); /** * @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] rel_addr Address where relocation should be performed * @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, uint8_t *rel_addr, 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] rel_addr Address where relocation should be performed * @param[in] link_addr target address for table-based relocations */ void arch_elf_relocate_global(struct llext_loader *loader, struct llext *ext, const elf_rela_t *rel, const elf_sym_t *sym, uint8_t *rel_addr, const void *link_addr); /** * @} */ #ifdef __cplusplus } #endif #include #endif /* ZEPHYR_LLEXT_H */