Configuration
The following Kconfig options are available for the LLEXT subsystem:
Heap size
The LLEXT subsystem needs a heap to be allocated for extension related data. The following option controls this allocation, when allocating a static heap.
Size of the LLEXT heap in kilobytes.
For boards using the Harvard architecture, the LLEXT heap is split into two: one heap in instruction memory and another in data memory. The following options control these allocations.
Size of the LLEXT heap in instruction memory in kilobytes.
Size of the LLEXT heap in data memory in kilobytes.
Alternatively the application can configure a dynamic heap using the following option.
Some applications require loading extensions into the memory which does not exist during the boot time and cannot be allocated statically. Make the application responsible for LLEXT heap allocation. Do not allocate LLEXT heap statically.
Application must call
llext_heap_init()in order to assign a buffer to be used as the LLEXT heap, otherwise LLEXT modules will not load. When the application does not need LLEXT functionality any more, it should callllext_heap_uninit()which releases control of the buffer back to the application.
Note
When user mode is enabled, the heap size must be large enough to allow the extension sections to be allocated with the alignment required by the architecture.
Note
On Harvard architectures, applications must call
llext_heap_init_harvard().
The backing data structure for the LLEXT heap is by default a
k_heap, but it can be changed to sys_mem_blocks_t for
non-metadata (i.e., extension regions) by selecting
CONFIG_LLEXT_HEAP_MEMBLK for
CONFIG_LLEXT_HEAP_MANAGEMENT.
Select the memory management API used to store extension regions in LLEXT heap memory. This choice does not affect LLEXT metadata, which is always managed with
k_heap.
Use
sys_mem_blocks_tAPI to manage LLEXT heap memory for extension regions. A minimum of one block will be allocated per region. Block size must be selected with care to ensure proper alignment for extension regions.
Note
CONFIG_LLEXT_HEAP_MEMBLK does not support
CONFIG_LLEXT_HEAP_DYNAMIC.
The heap will be split into two, with the size of each sub-heap controlled by the following options.
Heap size in kilobytes available for LLEXT extension sections. Must be a multiple of
CONFIG_LLEXT_HEAP_MEMBLK_BLOCK_SIZE. Replaced byCONFIG_LLEXT_INSTR_HEAP_SIZEandCONFIG_LLEXT_DATA_HEAP_SIZEifCONFIG_HARVARDis selected.
CONFIG_LLEXT_METADATA_HEAP_SIZE
Heap size in kilobytes available for LLEXT metadata.
Another option controls block size.
CONFIG_LLEXT_HEAP_MEMBLK_BLOCK_SIZE
Block size in bytes for LLEXT
sys_mem_blocks_theap(s). Must be equal to or a multiple ofLLEXT_PAGE_SIZE. The block size must also be equal to or a multiple of the largest alignment needed for any extension region. IfCONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENTis selected and regions are large, an unreasonably large block size may be needed to satisfy alignment requirements.
Heap placement
The LLEXT heap(s) have custom sections. Non-Harvard heap sections
(.llext_heap, or .llext_metadata_heap and .llext_ext_heap if
CONFIG_LLEXT_HEAP_MEMBLK is selected) are placed alongside
.noinit sections in the file
include/zephyr/linker/common-noinit.ld. If none of your linker scripts
include this file, you will need to place the non-Harvard LLEXT heap sections
manually. One way to do this is by including snippets-noinit.ld in
your linker script after your .noinit sections.
/* Located in generated directory. This file is populated by the
* zephyr_linker_sources() CMake function.
*/
#include <snippets-noinit.ld>
Add the file as a linker source in your board, SoC, or architecture
CMakeFiles.txt.
zephyr_linker_sources(NOINIT snippets-noinit.ld)
Then create a file in the same directory as your CMakeFiles.txt named
noinit.ld.
#ifdef CONFIG_LLEXT
*(.llext_heap)
*(.llext_ext_heap)
*(.llext_metadata_heap)
#endif /* CONFIG_LLEXT */
For ARC, the Harvard instruction and data heap sections (.llext_instr_heap
and .llext_data_heap) are placed in instruction and data memory at the
architecture level. If you are using a non-ARC board with a Harvard
architecture, you will need to manually place .llext_instr_heap and
.llext_data_heap.
Warning
LLEXT will be unable to load extensions if the instruction memory
.llext_instr_heap is placed in is not writable at the time the
extensions are loaded and linked.
Placements can also be specified, or default placements overridden, by providing a custom linker script.
Path to the linker script to be used instead of the one defined by the board.
The linker script must be based on a version provided by Zephyr since the kernel can expect a certain layout/certain regions.
This is useful when an application needs to add sections into the linker script and avoid having to change the script provided by Zephyr.
ELF object type
The LLEXT subsystem supports loading different types of extensions; the type can be set by choosing among the following Kconfig options:
Build and expect relocatable files as binary object type for the LLEXT subsystem. A single compiler invocation is used to generate the object file.
CONFIG_LLEXT_TYPE_ELF_RELOCATABLE
Build and expect relocatable (partially linked) files as the binary object type for the LLEXT subsystem. These object files are generated by the linker by combining multiple object files into a single one.
CONFIG_LLEXT_TYPE_ELF_SHAREDLIB
Build and expect shared libraries as binary object type for the LLEXT subsystem. The standard linking process is used to generate the shared library from multiple object files.
Note
This is not currently supported on ARM architectures.
Minimize allocations
The LLEXT subsystem loading mechanism, by default, uses a seek/read abstraction and copies all data into allocated memory; this is done to allow the extension to be loaded from any storage medium. Sometimes, however, data is already in a buffer in RAM and copying it is not necessary. The following option allows the LLEXT subsystem to optimize memory footprint in this case.
Allow the extension to be loaded by directly referencing section data into the ELF buffer. To be effective, this requires the use of an ELF loader that supports the
peekfunctionality, such as thellext_buf_loader.Warning
The application must ensure that the buffer used to load the extension remains allocated until the extension is unloaded.
Note
This will directly modify the contents of the buffer during the link phase. Once the extension is unloaded, the buffer must be reloaded before it can be used again in a call to
llext_load().Note
This is currently required by the Xtensa architecture. Further information on this topic is available on GitHub issue #75341.
Symbol Groups
All LLEXT symbols belong to a group, with the inclusion of each group in the
exported symbol table controlled by a corresponding Kconfig symbol. Exporting
a symbol as part of a group is done with the EXPORT_GROUP_SYMBOL
and EXPORT_GROUP_SYMBOL_NAMED macros. For example the following
exports the symbol memcpy as part of the LIBC group:
EXPORT_GROUP_SYMBOL(LIBC, memcpy);
Group names are arbitrary, but they must be all uppercase. For each group used in C code, there MUST be a corresponding Kconfig symbol of the form:
config LLEXT_EXPORT_SYMBOL_GROUP_{GROUP_NAME}
bool "Export all symbols from the {GROUP_NAME} group"
The default group for symbols (those declared with EXPORT_SYMBOL
or EXPORT_SYMBOL_NAMED) is the UNASSIGNED group. As per the
above rules, the inclusion of this group is controlled by
CONFIG_LLEXT_EXPORT_SYMBOL_GROUP_UNASSIGNED.
The groups currently defined by Zephyr are:
Group Name |
Kconfig Symbol |
Description |
|---|---|---|
|
Symbols without an explicit group |
|
|
Zephyr kernel system calls |
|
|
C standard library functions ( |
|
|
Devicetree devices |
Using SLID for symbol lookups
When an extension is loaded, the LLEXT subsystem must find the address of all the symbols residing in the main application that the extension references. To this end, the main binary contains a LLEXT-dedicated symbol table, filled with one symbol-name-to-address mapping entry for each symbol exported by the main application to extensions. This table can then be searched into by the LLEXT linker at extension load time. This process is pretty slow due to the nature of string comparisons, and the size consumed by the table can become significant as the number of exported symbols increases.
CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID
Perform an extra processing step on the Zephyr binary and on all extensions being built, converting every string in the symbol tables to a pointer-sized hash called Symbol Link Identifier (SLID), which is stored in the binary.
This speeds up the symbol lookup process by allowing usage of integer-based comparisons rather than string-based ones. Another benefit of SLID-based linking is that storing symbol names in the binary is no longer necessary, which provides a significant decrease in symbol table size.
Note
This option is not currently compatible with the LLEXT EDK.
Note
Using a different value for this option in the main binary and in extensions is not supported. For example, if the main application is built with
CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID=y, it is forbidden to load an extension that was compiled withCONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID=n.
EDK configuration
Options influencing the generation and behavior of the LLEXT EDK are described in LLEXT EDK Kconfig options.