Linkable Loadable Extensions (LLEXT)
The llext subsystem provides a toolbox for extending the functionality of an application at runtime with linkable loadable code.
Extensions can be loaded from precompiled ELF formatted data which is verified, loaded, and linked with other extensions. Extensions can be manipulated and introspected to some degree, as well as unloaded when no longer needed.
An extension may be loaded using any implementation of a llext_loader
which has a set of function pointers that provide the necessary functionality
to read the ELF data. A loader also provides some minimal context (memory)
needed by the llext_load()
function. An implementation over a buffer
containing an ELF in addressable memory in memory is available as
llext_buf_loader
.
LLEXT Extension Development Kit
To build the extensions to be loaded by the llext subsystem, it’s necessary to have access to the headers and compiler flags used by the main Zephyr application. While building an extension as part of the Zephyr application is straightforward, it can be more complex as a standalone project.
The LLEXT Extension Development Kit (EDK) provides a set of headers and compile
flags that can be used to build extensions as standalone projects. The EDK
can be generated by the Zephyr build system by running the following command
which uses the llext-edk
target to generate the EDK:
west build -t llext-edk
The generated EDK can be found in the build directory under the zephyr
directory. It’s a tarball that contains the headers and compile flags needed
to build extensions. The extension developer can then include the headers
and use the compile flags in their build system to build the extension.
Compile flags
Convenience files cmake.cflags
(for CMake based projects) and
Makefile.cflags
are provided by the EDK containing the needed compile flags.
The main flag is LLEXT_CFLAGS
which contains the flags needed to build an
extension. Also provided is a granular set of flags that can be used in support
of different use cases, such as when building mocks for unit tests. The provided
flags are:
LLEXT_CFLAGS
: Flags needed to build an extension.LLEXT_ALL_INCLUDE_CFLAGS
: Include flags needed to include all headers.LLEXT_INCLUDE_CFLAGS
: Include flags for non build generated headers.LLEXT_GENERATED_INCLUDE_CFLAGS
: Include flags for build generated headers.LLEXT_BASE_CFLAGS
: All other flags needed to build an extension, excluding the include flags.LLEXT_GENERATED_IMACROS_CFLAGS
: Include only-imacros
flags for build generated headers.
LLEXT EDK Kconfig options
The LLEXT EDK can be configured using the following Kconfig options:
CONFIG_LLEXT_EDK_NAME
The name of the generated EDK tarball.
CONFIG_LLEXT_EDK_USERSPACE_ONLY
If set, the EDK will include headers that do not contain code to route syscalls to the kernel. This is useful when building extensions that will run exclusively in user mode.
EDK Sample
Refer to Linkable loadable extensions EDK for an example of how to use the LLEXT EDK.
API Reference
- group llext
Linkable loadable extensions.
- Since
3.5
- Version
0.1.0
Enums
-
enum llext_mem
List of ELF regions that are stored or referenced in the llext.
Values:
-
enumerator LLEXT_MEM_TEXT
-
enumerator LLEXT_MEM_DATA
-
enumerator LLEXT_MEM_RODATA
-
enumerator LLEXT_MEM_BSS
-
enumerator LLEXT_MEM_EXPORT
-
enumerator LLEXT_MEM_SYMTAB
-
enumerator LLEXT_MEM_STRTAB
-
enumerator LLEXT_MEM_SHSTRTAB
-
enumerator LLEXT_MEM_COUNT
-
enumerator LLEXT_MEM_TEXT
Functions
-
struct llext *llext_by_name(const char *name)
Find an llext by name.
- Parameters:
name – [in] String name of the llext
- Return values:
NULL – if no llext not found
llext – if llext found
-
int llext_iterate(int (*fn)(struct llext *ext, void *arg), void *arg)
Iterate overall registered llext instances.
Calls a provided callback function for each registered extension or until the callback function returns a non-0 value.
- Parameters:
fn – [in] callback function
arg – [in] a private argument to be provided to the callback function
- Return values:
0 – if no extensions are registered
value – returned by the most recent callback invocation
-
int llext_load(struct llext_loader *loader, const char *name, struct llext **ext, struct llext_load_param *ldr_parm)
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).
- Parameters:
loader – [in] An extension loader that provides input data and context
name – [in] A string identifier for the extension
ext – [out] This will hold the pointer to the llext struct
ldr_parm – [in] Loader parameters
- Return values:
0 – Success
> – 0 extension use count
-ENOMEM – Not enough memory
-EINVAL – Invalid ELF stream
-
int llext_unload(struct llext **ext)
Unload an extension.
- Parameters:
ext – [in] Extension to unload
-
const void *llext_find_sym(const struct llext_symtable *sym_table, const char *sym_name)
Find the address for an arbitrary symbol name.
- Parameters:
sym_table – [in] Symbol table to lookup symbol in, if NULL uses base table
sym_name – [in] Symbol name to find
- Return values:
NULL – if no symbol found
addr – Address of symbol in memory if found
-
int llext_call_fn(struct llext *ext, const char *sym_name)
Call a function by name.
Expects a symbol representing a void fn(void) style function exists and may be called.
- Parameters:
ext – [in] Extension to call function in
sym_name – [in] Function name (exported symbol) in the extension
- Return values:
0 – success
-EINVAL – invalid symbol name
-
int llext_add_domain(struct llext *ext, struct k_mem_domain *domain)
Add the known memory partitions of the extension to a memory domain.
Allows an extension to be executed in supervisor or user mode threads when memory protection hardware is enabled.
- Parameters:
ext – [in] Extension to add to a domain
domain – [in] Memory domain to add partitions to
- Return values:
0 – success
-errno – error
-
int arch_elf_relocate(elf_rela_t *rel, uintptr_t loc, uintptr_t sym_base_addr, const char *sym_name, uintptr_t load_bias)
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.
- Parameters:
rel – [in] Relocation data provided by elf
loc – [in] Address of operation to rewrite with relocation
sym_base_addr – [in] Symbol address
sym_name – [in] Symbol name
load_bias – [in] .text load address
- Return values:
0 – success
-ENOEXEC – invalid relocation
-
ssize_t llext_find_section(struct llext_loader *loader, const char *search_name)
Find an ELF section.
- Parameters:
loader – Extension loader data and context
search_name – Section name to search for
- Return values:
Section – offset or a negative error code
-
void arch_elf_relocate_local(struct llext_loader *loader, struct llext *ext, const elf_rela_t *rel, const elf_sym_t *sym, size_t got_offset)
Architecture specific function for updating addresses via relocation table.
- Parameters:
loader – [in] Extension loader data and context
ext – [in] Extension to call function in
rel – [in] Relocation data provided by elf
sym – [in] Corresponding symbol table entry
got_offset – [in] Offset within a relocation table
-
struct llext
- #include <llext.h>
Linkable loadable extension.
Public Members
-
char name[16]
Name of the llext.
-
void *mem[LLEXT_MEM_COUNT]
Lookup table of llext memory regions.
-
bool mem_on_heap[LLEXT_MEM_COUNT]
Is the memory for this section allocated on heap?
-
size_t mem_size[LLEXT_MEM_COUNT]
Size of each stored section.
-
size_t alloc_size
Total llext allocation size.
-
struct llext_symtable exp_tab
Exported symbols from the llext, may be linked against by other llext.
-
unsigned int use_count
Extension use counter, prevents unloading while in use.
-
char name[16]
-
struct llext_load_param
- #include <llext.h>
llext loader parameters
These are parameters, not saved in the permanent llext context, needed only for the loader
- group llext_symbols
Linkable loadable extension symbol.
Defines
-
EXPORT_SYMBOL(x)
Export a constant symbol to extensions.
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 referenced by extensions.
- Parameters:
x – Symbol to export to extensions
-
LL_EXTENSION_SYMBOL(x)
Exports a symbol from an extension to the base image.
This macro can be used in extensions to add a symbol (function or object) to the extension’s exported symbol table, so that it may be referenced by the base image.
- Parameters:
x – Extension symbol to export to the base image
-
struct llext_const_symbol
- #include <symbol.h>
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.
Note
When updating this structure, make sure to also update the ‘scripts/build/llext_prepare_exptab.py’ build script.
-
struct llext_symbol
- #include <symbol.h>
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_symtable
- #include <symbol.h>
A symbol table.
An array of symbols
Public Members
-
size_t sym_cnt
Number of symbols in the table.
-
struct llext_symbol *syms
Array of symbols.
-
size_t sym_cnt
-
EXPORT_SYMBOL(x)
- group llext_loader
Loader context for llext.
Functions
-
static inline int llext_read(struct llext_loader *l, void *buf, size_t len)
-
static inline int llext_seek(struct llext_loader *l, size_t pos)
-
static inline void *llext_peek(struct llext_loader *l, size_t pos)
-
struct llext_loader
- #include <loader.h>
Linkable loadable extension loader context.
Public Members
-
int (*read)(struct llext_loader *ldr, void *out, size_t len)
Read (copy) from the loader.
Copies len bytes into buf from the current position of the loader.
- Param ldr:
[in] Loader
- Param out:
[in] Output location
- Param len:
[in] Length to copy into the output location
- Retval 0:
Success
- Retval -errno:
Error reading (any errno)
-
int (*seek)(struct llext_loader *ldr, size_t pos)
Seek to a new absolute location.
Changes the location of the loader position to a new absolute given position.
- Param ldr:
[in] Loader
- Param pos:
[in] Position in stream to move loader
- Retval 0:
Success
- Retval -errno:
Error reading (any errno)
-
void *(*peek)(struct llext_loader *ldr, size_t pos)
Peek at an absolute location.
Return a pointer to the buffer at specified offset.
- Param ldr:
[in] Loader
- Param pos:
[in] Position to obtain a pointer to
- Retval pointer:
into the buffer
-
int (*read)(struct llext_loader *ldr, void *out, size_t len)
-
static inline int llext_read(struct llext_loader *l, void *buf, size_t len)
- group llext_buf_loader
LLEXT buffer loader.
Defines
-
LLEXT_BUF_LOADER(_buf, _buf_len)
Initialize an extension buf loader.
- Parameters:
_buf – Buffer containing an ELF binary
_buf_len – Buffer length in bytes
-
struct llext_buf_loader
- #include <buf_loader.h>
An extension loader from a provided buffer containing an ELF.
Public Members
-
struct llext_loader loader
Extension loader.
-
struct llext_loader loader
-
LLEXT_BUF_LOADER(_buf, _buf_len)