doc: llext: improve documentation
This patch improves the documentation for the llext subsystem, adding information onhow to configure the llext subsystem, how to build extensions using the Zephyr build system, and how to load and use these in a Zephyr application. Signed-off-by: Luca Burelli <l.burelli@arduino.cc>
This commit is contained in:
parent
c444fdb581
commit
7ba86312e7
5 changed files with 398 additions and 33 deletions
|
@ -1,10 +1,8 @@
|
||||||
API Reference
|
API Reference
|
||||||
*************
|
*************
|
||||||
|
|
||||||
.. doxygengroup:: llext
|
.. doxygengroup:: llext_apis
|
||||||
|
|
||||||
.. doxygengroup:: llext_symbols
|
.. doxygengroup:: llext_symbols
|
||||||
|
|
||||||
.. doxygengroup:: llext_loader
|
.. doxygengroup:: llext_loader_apis
|
||||||
|
|
||||||
.. doxygengroup:: llext_buf_loader
|
|
||||||
|
|
|
@ -1,15 +1,147 @@
|
||||||
|
Building extensions
|
||||||
|
###################
|
||||||
|
|
||||||
|
The LLEXT subsystem allows for the creation of extensions that can be loaded
|
||||||
|
into a running Zephyr application. When building these extensions, it's very
|
||||||
|
often useful to have access to the headers and compiler flags used by the main
|
||||||
|
Zephyr application.
|
||||||
|
|
||||||
|
The easiest path to achieve this is to build the extension as part of the
|
||||||
|
Zephyr application, using the `native Zephyr CMake features
|
||||||
|
<llext_build_native_>`_. This will result in a single build providing both the
|
||||||
|
main Zephyr application and the extension(s), which will all automatically be
|
||||||
|
built with the same parameters.
|
||||||
|
|
||||||
|
In some cases, involving the full Zephyr build system may not be feasible or
|
||||||
|
convenient; maybe the extension is built using a different compiler suite or as
|
||||||
|
part of a different project altogether. In this case, the extension developer
|
||||||
|
needs to export the headers and compiler flags used by the main Zephyr
|
||||||
|
application. This can be done using the `LLEXT Extension Development Kit
|
||||||
|
<llext_build_edk_>`_.
|
||||||
|
|
||||||
|
.. _llext_build_native:
|
||||||
|
|
||||||
|
Using the Zephyr CMake features
|
||||||
|
*******************************
|
||||||
|
|
||||||
|
The Zephyr build system provides a set of features that can be used to build
|
||||||
|
extensions as part of the Zephyr application. This is the simplest way to build
|
||||||
|
extensions, as it requires minimal additions to an application build system.
|
||||||
|
|
||||||
|
Building the extension
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
An extension can be defined in the app's ``CMakeLists.txt`` by invoking the
|
||||||
|
``add_llext_target`` function, providing the target name, the output and the
|
||||||
|
source files. Usage is similar to the standard ``add_custom_target`` CMake
|
||||||
|
function:
|
||||||
|
|
||||||
|
.. code-block:: cmake
|
||||||
|
|
||||||
|
add_llext_target(
|
||||||
|
<target_name>
|
||||||
|
OUTPUT <ext_file.llext>
|
||||||
|
SOURCES <src1> [<src2>...]
|
||||||
|
)
|
||||||
|
|
||||||
|
where:
|
||||||
|
|
||||||
|
- ``<target_name>`` is the name of the final CMake target that will result in
|
||||||
|
the LLEXT binary being created;
|
||||||
|
- ``<ext_file.llext>`` is the name of the output file that will contain the
|
||||||
|
packaged extension;
|
||||||
|
- ``<src1> [<src2>...]`` is the list of source files that will be compiled to
|
||||||
|
create the extension.
|
||||||
|
|
||||||
|
The exact steps of the extension building process depend on the currently
|
||||||
|
selected :ref:`ELF object format <llext_kconfig_type>`.
|
||||||
|
|
||||||
|
The following custom properties of ``<target_name>`` are defined and can be
|
||||||
|
retrieved using the ``get_target_property()`` CMake function:
|
||||||
|
|
||||||
|
``lib_target``
|
||||||
|
|
||||||
|
Target name for the source compilation and/or link step.
|
||||||
|
|
||||||
|
``lib_output``
|
||||||
|
|
||||||
|
The binary file resulting from compilation and/or linking steps.
|
||||||
|
|
||||||
|
``pkg_input``
|
||||||
|
|
||||||
|
The file to be used as input for the packaging step.
|
||||||
|
|
||||||
|
``pkg_output``
|
||||||
|
|
||||||
|
The final extension file name.
|
||||||
|
|
||||||
|
Tweaking the build process
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
The following CMake functions can be used to modify the build system behavior
|
||||||
|
during the extension build process to a fine degree. Each of the below
|
||||||
|
functions takes the LLEXT target name as its first argument; it is otherwise
|
||||||
|
functionally equivalent to the common Zephyr ``target_*`` version.
|
||||||
|
|
||||||
|
* ``llext_compile_definitions``
|
||||||
|
* ``llext_compile_features``
|
||||||
|
* ``llext_compile_options``
|
||||||
|
* ``llext_include_directories``
|
||||||
|
* ``llext_link_options``
|
||||||
|
|
||||||
|
Custom build steps
|
||||||
|
------------------
|
||||||
|
|
||||||
|
The ``add_llext_command`` CMake function can be used to add custom build steps
|
||||||
|
that will be executed during the extension build process. The command will be
|
||||||
|
run at the specified build step and can refer to the properties of the target
|
||||||
|
for build-specific details.
|
||||||
|
|
||||||
|
The function signature is:
|
||||||
|
|
||||||
|
.. code-block:: cmake
|
||||||
|
|
||||||
|
add_llext_command(
|
||||||
|
TARGET <target_name>
|
||||||
|
[PRE_BUILD | POST_BUILD | POST_PKG]
|
||||||
|
COMMAND <command> [args...]
|
||||||
|
)
|
||||||
|
|
||||||
|
The different build steps are:
|
||||||
|
|
||||||
|
``PRE_BUILD``
|
||||||
|
|
||||||
|
Before the extension code is linked, if the architecture uses dynamic
|
||||||
|
libraries. This step can access `lib_target` and its own properties.
|
||||||
|
|
||||||
|
``POST_BUILD``
|
||||||
|
|
||||||
|
After the extension code is built, but before packaging it in an ``.llext``
|
||||||
|
file. This step is expected to create a `pkg_input` file by reading the
|
||||||
|
contents of `lib_output`.
|
||||||
|
|
||||||
|
``POST_PKG``
|
||||||
|
|
||||||
|
After the extension output file has been created. The command can operate
|
||||||
|
on the final llext file `pkg_output`.
|
||||||
|
|
||||||
|
Anything else after ``COMMAND`` will be passed to ``add_custom_command()`` as-is
|
||||||
|
(including multiple commands and other options).
|
||||||
|
|
||||||
|
.. _llext_build_edk:
|
||||||
|
|
||||||
LLEXT Extension Development Kit
|
LLEXT Extension Development Kit
|
||||||
*******************************
|
*******************************
|
||||||
|
|
||||||
To build the extensions to be loaded by the llext subsystem, it's necessary to
|
When building extensions as a standalone project, outside of the main Zephyr
|
||||||
have access to the headers and compiler flags used by the main Zephyr
|
build system, it's important to have access to the same set of generated
|
||||||
application. While building an extension as part of the Zephyr application
|
headers and compiler flags used by the main Zephyr application, since they have
|
||||||
is straightforward, it can be more complex as a standalone project.
|
a direct impact on how Zephyr headers are interpreted and the extension is
|
||||||
|
compiled in general.
|
||||||
|
|
||||||
The LLEXT Extension Development Kit (EDK) provides a set of headers and compile
|
This can be achieved by asking Zephyr to generate an Extension Development Kit
|
||||||
flags that can be used to build extensions as standalone projects. The EDK
|
(EDK) from the build artifacts of the main Zephyr application, by running the
|
||||||
can be generated by the Zephyr build system by running the following command
|
following command which uses the ``llext-edk`` target:
|
||||||
which uses the ``llext-edk`` target to generate the EDK:
|
|
||||||
|
|
||||||
.. code-block:: shell
|
.. code-block:: shell
|
||||||
|
|
||||||
|
@ -23,23 +155,46 @@ and use the compile flags in their build system to build the extension.
|
||||||
Compile flags
|
Compile flags
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
Convenience files ``cmake.cflags`` (for CMake based projects) and
|
The EDK includes the convenience files ``cmake.cflags`` (for CMake-based
|
||||||
``Makefile.cflags`` are provided by the EDK containing the needed compile flags.
|
projects) and ``Makefile.cflags`` (for Make-based ones), which define a set of
|
||||||
The main flag is ``LLEXT_CFLAGS`` which contains the flags needed to build an
|
variables that contain the compile flags needed by the project. The full list
|
||||||
extension. Also provided is a granular set of flags that can be used in support
|
of flags needed to build an extension is provided by ``LLEXT_CFLAGS``. Also
|
||||||
of different use cases, such as when building mocks for unit tests. The provided
|
provided is a more granular set of flags that can be used in support of
|
||||||
flags are:
|
different use cases, such as when building mocks for unit tests:
|
||||||
|
|
||||||
- ``LLEXT_CFLAGS``: Flags needed to build an extension.
|
``LLEXT_INCLUDE_CFLAGS``
|
||||||
- ``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_kconf_edk:
|
Compiler flags to add directories containing non-autogenerated headers
|
||||||
|
to the compiler's include search paths.
|
||||||
|
|
||||||
|
``LLEXT_GENERATED_INCLUDE_CFLAGS``
|
||||||
|
|
||||||
|
Compiler flags to add directories containing autogenerated headers to
|
||||||
|
the compiler's include search paths.
|
||||||
|
|
||||||
|
``LLEXT_ALL_INCLUDE_CFLAGS``
|
||||||
|
|
||||||
|
Compiler flags to add all directories containing headers used in the
|
||||||
|
build to the compiler's include search paths. This is a combination of
|
||||||
|
``LLEXT_INCLUDE_CFLAGS`` and ``LLEXT_GENERATED_INCLUDE_CFLAGS``.
|
||||||
|
|
||||||
|
``LLEXT_GENERATED_IMACROS_CFLAGS``
|
||||||
|
|
||||||
|
Compiler flags for autogenerated headers that must be included in the
|
||||||
|
build via ``-imacros``.
|
||||||
|
|
||||||
|
``LLEXT_BASE_CFLAGS``
|
||||||
|
|
||||||
|
Other compiler flags that control code generation for the target CPU.
|
||||||
|
None of these flags are included in the above lists.
|
||||||
|
|
||||||
|
``LLEXT_CFLAGS``
|
||||||
|
|
||||||
|
All flags required to build an extension. This is a combination of
|
||||||
|
``LLEXT_ALL_INCLUDE_CFLAGS``, ``LLEXT_GENERATED_IMACROS_CFLAGS`` and
|
||||||
|
``LLEXT_BASE_CFLAGS``.
|
||||||
|
|
||||||
|
.. _llext_kconfig_edk:
|
||||||
|
|
||||||
LLEXT EDK Kconfig options
|
LLEXT EDK Kconfig options
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
133
doc/services/llext/config.rst
Normal file
133
doc/services/llext/config.rst
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
Configuration
|
||||||
|
#############
|
||||||
|
|
||||||
|
The following Kconfig options are available for the LLEXT subsystem:
|
||||||
|
|
||||||
|
.. _llext_kconfig_heap:
|
||||||
|
|
||||||
|
Heap size
|
||||||
|
----------
|
||||||
|
|
||||||
|
The LLEXT subsystem needs a static heap to be allocated for extension related
|
||||||
|
data. The following option controls this allocation.
|
||||||
|
|
||||||
|
:kconfig:option:`CONFIG_LLEXT_HEAP_SIZE`
|
||||||
|
|
||||||
|
Size of the LLEXT heap in kilobytes.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
When :ref:`user mode <usermode_api>` is enabled, the heap size must be
|
||||||
|
large enough to allow the extension sections to be allocated with the
|
||||||
|
alignment required by the architecture.
|
||||||
|
|
||||||
|
.. _llext_kconfig_type:
|
||||||
|
|
||||||
|
ELF object type
|
||||||
|
---------------
|
||||||
|
|
||||||
|
The LLEXT subsystem supports loading different types of extensions; the type
|
||||||
|
can be set by choosing among the following Kconfig options:
|
||||||
|
|
||||||
|
:kconfig:option:`CONFIG_LLEXT_TYPE_ELF_OBJECT`
|
||||||
|
|
||||||
|
Build and expect relocatable files as binary object type for the LLEXT
|
||||||
|
subsystem. A single compiler invocation is used to generate the object
|
||||||
|
file.
|
||||||
|
|
||||||
|
:kconfig:option:`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.
|
||||||
|
|
||||||
|
:kconfig:option:`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.
|
||||||
|
|
||||||
|
.. _llext_kconfig_storage:
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
:kconfig:option:`CONFIG_LLEXT_STORAGE_WRITABLE`
|
||||||
|
|
||||||
|
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 ``peek`` functionality, such as the
|
||||||
|
:c:struct:`llext_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 :c:func:`llext_load`.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This is currently required by the Xtensa architecture. Further
|
||||||
|
information on this topic is available on GitHub issue `#75341
|
||||||
|
<https://github.com/zephyrproject-rtos/zephyr/issues/75341>`_.
|
||||||
|
|
||||||
|
.. _llext_kconfig_slid:
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
:kconfig:option:`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 :ref:`LLEXT EDK
|
||||||
|
<llext_build_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 with
|
||||||
|
``CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID=n``.
|
||||||
|
|
||||||
|
EDK configuration
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Options influencing the generation and behavior of the LLEXT EDK are described
|
||||||
|
in :ref:`llext_kconfig_edk`.
|
|
@ -3,17 +3,22 @@
|
||||||
Linkable Loadable Extensions (LLEXT)
|
Linkable Loadable Extensions (LLEXT)
|
||||||
####################################
|
####################################
|
||||||
|
|
||||||
|
The LLEXT subsystem provides a toolbox for extending the functionality of an
|
||||||
|
application at runtime with linkable loadable code.
|
||||||
|
|
||||||
|
Extensions are precompiled executables in ELF format that can be verified,
|
||||||
|
loaded, and linked with the main Zephyr binary. Extensions can be manipulated
|
||||||
|
and introspected to some degree, as well as unloaded when no longer needed.
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
|
config
|
||||||
build
|
build
|
||||||
load
|
load
|
||||||
api
|
api
|
||||||
|
|
||||||
The llext subsystem provides a toolbox for extending the functionality of an
|
.. note::
|
||||||
application at runtime with linkable loadable code.
|
|
||||||
|
|
||||||
Extensions can be loaded from precompiled ELF formatted data which is
|
The LLEXT subsystem requires architecture-specific support. It is currently
|
||||||
verified, loaded, and linked with other extensions. Extensions can be
|
available only on ARM and Xtensa cores.
|
||||||
manipulated and introspected to some degree, as well as unloaded when no longer
|
|
||||||
needed.
|
|
||||||
|
|
|
@ -1,6 +1,80 @@
|
||||||
|
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`
|
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
|
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)
|
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
|
needed by the :c:func:`llext_load` function. An implementation over a buffer
|
||||||
containing an ELF in addressable memory in memory is available as
|
containing an ELF in addressable memory in memory is available as
|
||||||
:c:struct:`llext_buf_loader`.
|
: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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue