diff --git a/doc/services/llext/api.rst b/doc/services/llext/api.rst index b7c50ff12f3..b3d8d64152b 100644 --- a/doc/services/llext/api.rst +++ b/doc/services/llext/api.rst @@ -1,10 +1,8 @@ API Reference ************* -.. doxygengroup:: llext +.. doxygengroup:: llext_apis .. doxygengroup:: llext_symbols -.. doxygengroup:: llext_loader - -.. doxygengroup:: llext_buf_loader +.. doxygengroup:: llext_loader_apis diff --git a/doc/services/llext/build.rst b/doc/services/llext/build.rst index 79dd6a4adae..05c4c308ea3 100644 --- a/doc/services/llext/build.rst +++ b/doc/services/llext/build.rst @@ -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 +`_. 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_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( + + OUTPUT + SOURCES [...] + ) + +where: + +- ```` is the name of the final CMake target that will result in + the LLEXT binary being created; +- ```` is the name of the output file that will contain the + packaged extension; +- `` [...]`` 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 `. + +The following custom properties of ```` 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 + [PRE_BUILD | POST_BUILD | POST_PKG] + 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 ******************************* -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. +When building extensions as a standalone project, outside of the main Zephyr +build system, it's important to have access to the same set of generated +headers and compiler flags used by the main Zephyr application, since they have +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 -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: +This can be achieved by asking Zephyr to generate an Extension Development Kit +(EDK) from the build artifacts of the main Zephyr application, by running the +following command which uses the ``llext-edk`` target: .. code-block:: shell @@ -23,23 +155,46 @@ 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: +The EDK includes the convenience files ``cmake.cflags`` (for CMake-based +projects) and ``Makefile.cflags`` (for Make-based ones), which define a set of +variables that contain the compile flags needed by the project. The full list +of flags needed to build an extension is provided by ``LLEXT_CFLAGS``. Also +provided is a more granular set of flags that can be used in support of +different use cases, such as when building mocks for unit tests: -- ``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_INCLUDE_CFLAGS`` -.. _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 ------------------------- diff --git a/doc/services/llext/config.rst b/doc/services/llext/config.rst new file mode 100644 index 00000000000..3772c26a445 --- /dev/null +++ b/doc/services/llext/config.rst @@ -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 ` 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 + `_. + +.. _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 + `. + + .. 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`. diff --git a/doc/services/llext/index.rst b/doc/services/llext/index.rst index 1141e65d14f..f9112ae0469 100644 --- a/doc/services/llext/index.rst +++ b/doc/services/llext/index.rst @@ -3,17 +3,22 @@ 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:: :maxdepth: 1 + config build load api -The llext subsystem provides a toolbox for extending the functionality of an -application at runtime with linkable loadable code. +.. note:: -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. + The LLEXT subsystem requires architecture-specific support. It is currently + available only on ARM and Xtensa cores. diff --git a/doc/services/llext/load.rst b/doc/services/llext/load.rst index 58e9582bb25..0898cb3a40e 100644 --- a/doc/services/llext/load.rst +++ b/doc/services/llext/load.rst @@ -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` 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 ` 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.