blob: 78fc897b87d4395cc63e8ed4cef2b1f4ddf3f129 [file] [log] [blame]
Loading extensions
##################
Once an extension is built and the ELF file is available, it can be loaded into
the Zephyr application using the LLEXT API, which provides a way to load the
extension into memory, access its symbols and call its functions.
Loading an extension
====================
An extension may be loaded using any implementation of a :c:struct:`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 :c:func:`llext_load` function. An implementation over a buffer
containing an ELF in addressable memory in memory is available as
:c:struct:`llext_buf_loader`.
The extensions are loaded with a call to the :c:func:`llext_load` function,
passing in the extension name and the configured loader. Once that completes
successfully, the extension is loaded into memory and is ready to be used.
.. note::
When :ref:`User Mode <usermode_api>` is enabled, the extension will not be
included in any user memory domain. To allow access from user mode, the
:c:func:`llext_add_domain` function must be called.
Initializing and cleaning up the extension
==========================================
The extension may define a number of initialization functions that must be
called after loading but before any function in it can be used; this is typical
in languages such as C++ that provide the concept of object constructors. The
same is true for cleanup functions that must be called before unloading the
extension.
LLEXT supports calling the functions listed in the ``.preinit_array`` and
``.init_array`` sections of the ELF file with the :c:func:`llext_bringup`
function, and the functions listed in the ``.fini_array`` section with the
:c:func:`llext_teardown` function. These APIs are compatible with
:ref:`User Mode <usermode_api>`, and thus can be called from either kernel or
user context.
.. important::
The code run by these functions is fully determined by the contents of the
ELF file. This may have security implications if its origin is untrusted.
If the extension requires a dedicated thread, the :c:func:`llext_bootstrap`
function can be used to minimize boilerplate code. This function has a
signature that is compatible with the :c:func:`k_thread_create` API, and will
call :c:func:`llext_bringup`, then a user-specified function in the same
context, and finally :c:func:`llext_teardown` before returning.
Accessing code and data
=======================
To interact with the newly loaded extension, the host application must use the
:c:func:`llext_find_sym` function to get the address of the exported symbol.
The returned ``void *`` can then be cast to the appropriate type and used.
A wrapper for calling a function with no arguments is provided in
:c:func:`llext_call_fn`.
Cleaning up after use
=====================
The :c:func:`llext_unload` function must be called to free the memory used by
the extension once it is no longer required. After this call completes, all
pointers to symbols in the extension that were obtained will be invalid.
Troubleshooting
###############
This feature is being actively developed and as such it is possible that some
issues may arise. Since linking does modify the binary code, in case of errors
the results are difficult to predict. Some common issues may be:
* Results from :c:func:`llext_find_sym` point to an invalid address;
* Constants and variables defined in the extension do not have the expected
values;
* Calling a function defined in an extension results in a hard fault, or memory
in the main application is corrupted after returning from it.
If any of this happens, the following tips may help understand the issue:
* Make sure :kconfig:option:`CONFIG_LLEXT_LOG_LEVEL` is set to ``DEBUG``, then
obtain a log of the :c:func:`llext_load` invocation.
* If possible, disable memory protection (MMU/MPU) and see if this results in
different behavior.
* Try to simplify the extension to the minimum possible code that reproduces
the issue.
* Use a debugger to inspect the memory and registers to try to understand what
is happening.
.. note::
When using GDB, the ``add_symbol_file`` command may be used to load the
debugging information and symbols from the ELF file. Make sure to specify
the proper offset (usually the start of the ``.text`` section, reported
as ``region 0`` in the debug logs.)
If the issue persists, please open an issue in the GitHub repository, including
all the above information.