llext: add support for SLID-based linking
This commit introduces support for an alternate linking method in the LLEXT subsystem, called "SLID" (short for Symbol Link Identifier), enabled by the CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID Kconfig option. SLID-based linking uses a unique identifier (integer) to identify exported symbols, instead of using the symbol name as done currently. This approach provides several benefits: * linking is faster because the comparison operation to determine whether we found the correct symbol in the export table is now an integer compare, instead of a string compare * binary size is reduced as symbol names can be dropped from the binary * confidentiality is improved as a side-effect, as symbol names are no longer present in the binary Signed-off-by: Mathieu Choplain <mathieu.choplain@st.com>
This commit is contained in:
parent
05ad2565fa
commit
8aa6ae43ce
45 changed files with 981 additions and 7 deletions
|
@ -1888,6 +1888,21 @@ if(CONFIG_BUILD_OUTPUT_INFO_HEADER)
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (CONFIG_LLEXT AND CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID)
|
||||||
|
#slidgen must be the first post-build command to be executed
|
||||||
|
#on the Zephyr ELF to ensure that all other commands, such as
|
||||||
|
#binary file generation, are operating on a preparated ELF.
|
||||||
|
list(PREPEND
|
||||||
|
post_build_commands
|
||||||
|
COMMAND ${PYTHON_EXECUTABLE}
|
||||||
|
${ZEPHYR_BASE}/scripts/build/llext_prepare_exptab.py
|
||||||
|
--elf-file ${PROJECT_BINARY_DIR}/${KERNEL_ELF_NAME}
|
||||||
|
--slid-listing ${PROJECT_BINARY_DIR}/slid_listing.txt
|
||||||
|
-vvv
|
||||||
|
)
|
||||||
|
|
||||||
|
endif()
|
||||||
|
|
||||||
if(NOT CMAKE_C_COMPILER_ID STREQUAL "ARMClang")
|
if(NOT CMAKE_C_COMPILER_ID STREQUAL "ARMClang")
|
||||||
set(check_init_priorities_input
|
set(check_init_priorities_input
|
||||||
$<IF:$<TARGET_EXISTS:native_runner_executable>,${BYPRODUCT_KERNEL_EXE_NAME},${BYPRODUCT_KERNEL_ELF_NAME}>
|
$<IF:$<TARGET_EXISTS:native_runner_executable>,${BYPRODUCT_KERNEL_EXE_NAME},${BYPRODUCT_KERNEL_ELF_NAME}>
|
||||||
|
|
|
@ -254,6 +254,10 @@ SECTIONS
|
||||||
|
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
|
|
||||||
/DISCARD/ :
|
/DISCARD/ :
|
||||||
{
|
{
|
||||||
*(.plt)
|
*(.plt)
|
||||||
|
|
|
@ -31,6 +31,11 @@
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.20.0)
|
cmake_minimum_required(VERSION 3.20.0)
|
||||||
|
|
||||||
|
if (CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID)
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"The LLEXT EDK is not compatible with CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID.")
|
||||||
|
endif()
|
||||||
|
|
||||||
set(llext_edk ${PROJECT_BINARY_DIR}/${llext_edk_name})
|
set(llext_edk ${PROJECT_BINARY_DIR}/${llext_edk_name})
|
||||||
set(llext_edk_inc ${llext_edk}/include)
|
set(llext_edk_inc ${llext_edk}/include)
|
||||||
|
|
||||||
|
|
|
@ -5431,6 +5431,23 @@ function(add_llext_target target_name)
|
||||||
COMMAND_EXPAND_LISTS
|
COMMAND_EXPAND_LISTS
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# LLEXT ELF processing for importing via SLID
|
||||||
|
#
|
||||||
|
# This command must be executed as last step of the packaging process,
|
||||||
|
# to ensure that the ELF processed for binary generation contains SLIDs.
|
||||||
|
# If executed too early, it is possible that some tools executed to modify
|
||||||
|
# the ELF file (e.g., strip) undo the work performed here.
|
||||||
|
if (CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID)
|
||||||
|
set(slid_inject_cmd
|
||||||
|
${PYTHON_EXECUTABLE}
|
||||||
|
${ZEPHYR_BASE}/scripts/build/llext_inject_slids.py
|
||||||
|
--elf-file ${llext_pkg_output}
|
||||||
|
-vvv
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
set(slid_inject_cmd ${CMAKE_COMMAND} -E true)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Type-specific packaging of the built binary file into an .llext file
|
# Type-specific packaging of the built binary file into an .llext file
|
||||||
if(CONFIG_LLEXT_TYPE_ELF_OBJECT)
|
if(CONFIG_LLEXT_TYPE_ELF_OBJECT)
|
||||||
|
|
||||||
|
@ -5438,6 +5455,7 @@ function(add_llext_target target_name)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${llext_pkg_output}
|
OUTPUT ${llext_pkg_output}
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy ${llext_pkg_input} ${llext_pkg_output}
|
COMMAND ${CMAKE_COMMAND} -E copy ${llext_pkg_input} ${llext_pkg_output}
|
||||||
|
COMMAND ${slid_inject_cmd}
|
||||||
DEPENDS ${llext_proc_target} ${llext_pkg_input}
|
DEPENDS ${llext_proc_target} ${llext_pkg_input}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -5453,6 +5471,7 @@ function(add_llext_target target_name)
|
||||||
$<TARGET_PROPERTY:bintools,elfconvert_flag_infile>${llext_pkg_input}
|
$<TARGET_PROPERTY:bintools,elfconvert_flag_infile>${llext_pkg_input}
|
||||||
$<TARGET_PROPERTY:bintools,elfconvert_flag_outfile>${llext_pkg_output}
|
$<TARGET_PROPERTY:bintools,elfconvert_flag_outfile>${llext_pkg_output}
|
||||||
$<TARGET_PROPERTY:bintools,elfconvert_flag_final>
|
$<TARGET_PROPERTY:bintools,elfconvert_flag_final>
|
||||||
|
COMMAND ${slid_inject_cmd}
|
||||||
DEPENDS ${llext_proc_target} ${llext_pkg_input}
|
DEPENDS ${llext_proc_target} ${llext_pkg_input}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -5467,6 +5486,7 @@ function(add_llext_target target_name)
|
||||||
$<TARGET_PROPERTY:bintools,strip_flag_infile>${llext_pkg_input}
|
$<TARGET_PROPERTY:bintools,strip_flag_infile>${llext_pkg_input}
|
||||||
$<TARGET_PROPERTY:bintools,strip_flag_outfile>${llext_pkg_output}
|
$<TARGET_PROPERTY:bintools,strip_flag_outfile>${llext_pkg_output}
|
||||||
$<TARGET_PROPERTY:bintools,strip_flag_final>
|
$<TARGET_PROPERTY:bintools,strip_flag_final>
|
||||||
|
COMMAND ${slid_inject_cmd}
|
||||||
DEPENDS ${llext_proc_target} ${llext_pkg_input}
|
DEPENDS ${llext_proc_target} ${llext_pkg_input}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -81,6 +81,10 @@ SECTIONS {
|
||||||
|
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
|
|
||||||
GROUP_START(ROMABLE_REGION)
|
GROUP_START(ROMABLE_REGION)
|
||||||
|
|
||||||
SECTION_PROLOGUE(_TEXT_SECTION_NAME,,ALIGN(1024)) {
|
SECTION_PROLOGUE(_TEXT_SECTION_NAME,,ALIGN(1024)) {
|
||||||
|
|
|
@ -98,6 +98,10 @@ SECTIONS
|
||||||
{
|
{
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* .plt and .iplt are here according to 'arm-zephyr-elf-ld --verbose',
|
* .plt and .iplt are here according to 'arm-zephyr-elf-ld --verbose',
|
||||||
* before text section.
|
* before text section.
|
||||||
|
|
|
@ -113,6 +113,10 @@ SECTIONS
|
||||||
|
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* .plt and .iplt are here according to 'arm-zephyr-elf-ld --verbose',
|
* .plt and .iplt are here according to 'arm-zephyr-elf-ld --verbose',
|
||||||
* before text section.
|
* before text section.
|
||||||
|
|
|
@ -77,6 +77,10 @@ SECTIONS
|
||||||
|
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* .plt and .iplt are here according to 'arm-zephyr-elf-ld --verbose',
|
* .plt and .iplt are here according to 'arm-zephyr-elf-ld --verbose',
|
||||||
* before text section.
|
* before text section.
|
||||||
|
|
|
@ -46,6 +46,10 @@ SECTIONS
|
||||||
|
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
|
|
||||||
SECTION_PROLOGUE(_VECTOR_SECTION_NAME,,)
|
SECTION_PROLOGUE(_VECTOR_SECTION_NAME,,)
|
||||||
{
|
{
|
||||||
. = ALIGN(0x1000);
|
. = ALIGN(0x1000);
|
||||||
|
|
|
@ -85,6 +85,10 @@ SECTIONS
|
||||||
|
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* .plt and .iplt are here according to
|
* .plt and .iplt are here according to
|
||||||
* 'nios2-zephyr-elf-ld --verbose', before text section.
|
* 'nios2-zephyr-elf-ld --verbose', before text section.
|
||||||
|
|
|
@ -21,6 +21,10 @@
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
|
|
||||||
SECTION_PROLOGUE(rom_start,,)
|
SECTION_PROLOGUE(rom_start,,)
|
||||||
{
|
{
|
||||||
/* Located in generated directory. This file is populated by the
|
/* Located in generated directory. This file is populated by the
|
||||||
|
|
|
@ -122,6 +122,10 @@ SECTIONS
|
||||||
|
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The .plt and .iplt are here according to
|
* The .plt and .iplt are here according to
|
||||||
* 'riscv32-zephyr-elf-ld --verbose', before text section.
|
* 'riscv32-zephyr-elf-ld --verbose', before text section.
|
||||||
|
|
|
@ -23,6 +23,10 @@ SECTIONS
|
||||||
|
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
|
|
||||||
__rom_region_start = .;
|
__rom_region_start = .;
|
||||||
|
|
||||||
SECTION_PROLOGUE(_TEXT_SECTION_NAME,,)
|
SECTION_PROLOGUE(_TEXT_SECTION_NAME,,)
|
||||||
|
|
|
@ -77,6 +77,10 @@ SECTIONS
|
||||||
|
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
|
|
||||||
/DISCARD/ :
|
/DISCARD/ :
|
||||||
{
|
{
|
||||||
*(.plt)
|
*(.plt)
|
||||||
|
|
|
@ -236,4 +236,7 @@ SECTIONS
|
||||||
.shstrtab 0 : { *(.shstrtab) }
|
.shstrtab 0 : { *(.shstrtab) }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
19
include/zephyr/linker/llext-sections.ld
Normal file
19
include/zephyr/linker/llext-sections.ld
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
/* SPDX-License-Identifier: Apache-2.0 */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Special section used by LLEXT if CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID
|
||||||
|
* is enabled. Declare this section to prevent it from being considered orphan.
|
||||||
|
*
|
||||||
|
* This section is used to temporarily save the exported symbols' names in the
|
||||||
|
* Zephyr ELF for post-processing, but it is not included in the final binary.
|
||||||
|
*
|
||||||
|
* NOTE: This section MUST start at address 0, as the post-processing scripts
|
||||||
|
* assume that the address of any data in this section (i.e., symbol names) is
|
||||||
|
* strictly equivalent to the offset inside the section.
|
||||||
|
*/
|
||||||
|
#ifdef CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID
|
||||||
|
SECTION_PROLOGUE(llext_exports_strtab, 0 (COPY), )
|
||||||
|
{
|
||||||
|
KEEP(*(llext_exports_strtab))
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -10,6 +10,7 @@
|
||||||
#include <zephyr/sys/iterable_sections.h>
|
#include <zephyr/sys/iterable_sections.h>
|
||||||
#include <zephyr/toolchain.h>
|
#include <zephyr/toolchain.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -27,14 +28,27 @@ extern "C" {
|
||||||
* Symbols may be named function or global objects that have been exported
|
* Symbols may be named function or global objects that have been exported
|
||||||
* for linking. These constant symbols are useful in the base image
|
* for linking. These constant symbols are useful in the base image
|
||||||
* as they may be placed in ROM.
|
* 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_const_symbol {
|
struct llext_const_symbol {
|
||||||
/** Name of symbol */
|
/** At build time, we always write to 'name'.
|
||||||
const char *const name;
|
* At runtime, which field is used depends
|
||||||
|
* on CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID.
|
||||||
|
*/
|
||||||
|
union {
|
||||||
|
/** Name of symbol */
|
||||||
|
const char *const name;
|
||||||
|
|
||||||
|
/** Symbol Link Identifier */
|
||||||
|
const uintptr_t slid;
|
||||||
|
};
|
||||||
|
|
||||||
/** Address of symbol */
|
/** Address of symbol */
|
||||||
const void *const addr;
|
const void *const addr;
|
||||||
};
|
};
|
||||||
|
BUILD_ASSERT(sizeof(struct llext_const_symbol) == 2 * sizeof(uintptr_t));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Symbols are named memory addresses
|
* @brief Symbols are named memory addresses
|
||||||
|
@ -75,10 +89,19 @@ struct llext_symtable {
|
||||||
*
|
*
|
||||||
* @param x Symbol to export to extensions
|
* @param x Symbol to export to extensions
|
||||||
*/
|
*/
|
||||||
|
#ifdef CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID
|
||||||
|
#define EXPORT_SYMBOL(x) \
|
||||||
|
static const char Z_GENERIC_SECTION("llext_exports_strtab") __used \
|
||||||
|
x ## _sym_name[] = STRINGIFY(x); \
|
||||||
|
static const STRUCT_SECTION_ITERABLE(llext_const_symbol, x ## _sym) = { \
|
||||||
|
.name = x ## _sym_name, .addr = (const void *)&x, \
|
||||||
|
}
|
||||||
|
#else
|
||||||
#define EXPORT_SYMBOL(x) \
|
#define EXPORT_SYMBOL(x) \
|
||||||
static const STRUCT_SECTION_ITERABLE(llext_const_symbol, x ## _sym) = { \
|
static const STRUCT_SECTION_ITERABLE(llext_const_symbol, x ## _sym) = { \
|
||||||
.name = STRINGIFY(x), .addr = (const void *)&x, \
|
.name = STRINGIFY(x), .addr = (const void *)&x, \
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Exports a symbol from an extension to the base image
|
* @brief Exports a symbol from an extension to the base image
|
||||||
|
|
196
scripts/build/llext_inject_slids.py
Executable file
196
scripts/build/llext_inject_slids.py
Executable file
|
@ -0,0 +1,196 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# Copyright (c) 2024 STMicroelectronics
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
"""Injects SLIDs in LLEXT ELFs' symbol tables.
|
||||||
|
|
||||||
|
When Kconfig option CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID is enabled,
|
||||||
|
all imports from the Zephyr kernel & application are resolved using
|
||||||
|
SLIDs instead of symbol names. This script stores the SLID of all
|
||||||
|
imported symbols in their associated entry in the ELF symbol table
|
||||||
|
to allow the LLEXT subsystem to link it properly at runtime.
|
||||||
|
|
||||||
|
Note that this script is idempotent in theory. However, to prevent
|
||||||
|
any catastrophic problem, the script will abort if the 'st_value'
|
||||||
|
field of the `ElfX_Sym` structure is found to be non-zero, which is
|
||||||
|
the case after one invocation. For this reason, in practice, the script
|
||||||
|
cannot actually be executed twice on the same ELF file.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from elftools.elf.elffile import ELFFile
|
||||||
|
from elftools.elf.sections import SymbolTableSection
|
||||||
|
|
||||||
|
import llext_slidlib
|
||||||
|
|
||||||
|
class LLEXTSymtabPreparator():
|
||||||
|
def __init__(self, elf_path, log):
|
||||||
|
self.log = log
|
||||||
|
self.elf_path = elf_path
|
||||||
|
self.elf_fd = open(elf_path, "rb+")
|
||||||
|
self.elf = ELFFile(self.elf_fd)
|
||||||
|
|
||||||
|
def _find_symtab(self):
|
||||||
|
supported_symtab_sections = [
|
||||||
|
".symtab",
|
||||||
|
".dynsym",
|
||||||
|
]
|
||||||
|
|
||||||
|
symtab = None
|
||||||
|
for section_name in supported_symtab_sections:
|
||||||
|
symtab = self.elf.get_section_by_name(section_name)
|
||||||
|
if not isinstance(symtab, SymbolTableSection):
|
||||||
|
self.log.debug(f"section {section_name} not found.")
|
||||||
|
else:
|
||||||
|
self.log.info(f"processing '{section_name}' symbol table...")
|
||||||
|
self.log.debug(f"(symbol table is at file offset 0x{symtab['sh_offset']:X})")
|
||||||
|
break
|
||||||
|
return symtab
|
||||||
|
|
||||||
|
def _find_imports_in_symtab(self, symtab):
|
||||||
|
i = 0
|
||||||
|
imports = []
|
||||||
|
for sym in symtab.iter_symbols():
|
||||||
|
#Check if symbol is an import
|
||||||
|
if sym.entry['st_info']['type'] == 'STT_NOTYPE' and \
|
||||||
|
sym.entry['st_info']['bind'] == 'STB_GLOBAL' and \
|
||||||
|
sym.entry['st_shndx'] == 'SHN_UNDEF':
|
||||||
|
|
||||||
|
self.log.debug(f"found imported symbol '{sym.name}' at index {i}")
|
||||||
|
imports.append((i, sym))
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
return imports
|
||||||
|
|
||||||
|
def _prepare_inner(self):
|
||||||
|
#1) Locate the symbol table
|
||||||
|
symtab = self._find_symtab()
|
||||||
|
if symtab is None:
|
||||||
|
self.log.error("no symbol table found in file")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
#2) Find imported symbols in symbol table
|
||||||
|
imports = self._find_imports_in_symtab(symtab)
|
||||||
|
self.log.info(f"LLEXT has {len(imports)} import(s)")
|
||||||
|
|
||||||
|
#3) Write SLIDs in each symbol's 'st_value' field
|
||||||
|
def make_stvalue_reader_writer():
|
||||||
|
byteorder = "little" if self.elf.little_endian else "big"
|
||||||
|
if self.elf.elfclass == 32:
|
||||||
|
sizeof_Elf_Sym = 0x10 #sizeof(Elf32_Sym)
|
||||||
|
offsetof_st_value = 0x4 #offsetof(Elf32_Sym, st_value)
|
||||||
|
sizeof_st_value = 0x4 #sizeof(Elf32_Sym.st_value)
|
||||||
|
else:
|
||||||
|
sizeof_Elf_Sym = 0x18
|
||||||
|
offsetof_st_value = 0x8
|
||||||
|
sizeof_st_value = 0x8
|
||||||
|
|
||||||
|
def seek(symidx):
|
||||||
|
self.elf_fd.seek(
|
||||||
|
symtab['sh_offset'] +
|
||||||
|
symidx * sizeof_Elf_Sym +
|
||||||
|
offsetof_st_value)
|
||||||
|
|
||||||
|
def reader(symbol_index):
|
||||||
|
seek(symbol_index)
|
||||||
|
return int.from_bytes(self.elf_fd.read(sizeof_st_value), byteorder)
|
||||||
|
|
||||||
|
def writer(symbol_index, st_value):
|
||||||
|
seek(symbol_index)
|
||||||
|
self.elf_fd.write(int.to_bytes(st_value, sizeof_st_value, byteorder))
|
||||||
|
|
||||||
|
return reader, writer
|
||||||
|
|
||||||
|
rd_st_val, wr_st_val = make_stvalue_reader_writer()
|
||||||
|
slid_size = self.elf.elfclass // 8
|
||||||
|
|
||||||
|
for (index, symbol) in imports:
|
||||||
|
slid = llext_slidlib.generate_slid(symbol.name, slid_size)
|
||||||
|
slid_as_str = llext_slidlib.format_slid(slid, slid_size)
|
||||||
|
msg = f"{symbol.name} -> {slid_as_str}"
|
||||||
|
|
||||||
|
self.log.info(msg)
|
||||||
|
|
||||||
|
# Make sure we're not overwriting something actually important
|
||||||
|
original_st_value = rd_st_val(index)
|
||||||
|
if original_st_value != 0:
|
||||||
|
self.log.error(f"unexpected non-zero st_value for symbol {symbol.name}")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
wr_st_val(index, slid)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def prepare_llext(self):
|
||||||
|
res = self._prepare_inner()
|
||||||
|
self.elf_fd.close()
|
||||||
|
return res
|
||||||
|
|
||||||
|
# Disable duplicate code warning for the code that follows,
|
||||||
|
# as it is expected for these functions to be similar.
|
||||||
|
# pylint: disable=duplicate-code
|
||||||
|
def _parse_args(argv):
|
||||||
|
"""Parse the command line arguments."""
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description=__doc__,
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
allow_abbrev=False)
|
||||||
|
|
||||||
|
parser.add_argument("-f", "--elf-file", required=True,
|
||||||
|
help="LLEXT ELF file to process")
|
||||||
|
parser.add_argument("-o", "--output-file",
|
||||||
|
help=("Additional output file where processed ELF "
|
||||||
|
"will be copied"))
|
||||||
|
parser.add_argument("-sl", "--slid-listing",
|
||||||
|
help="write the SLID listing to a file")
|
||||||
|
parser.add_argument("-v", "--verbose", action="count",
|
||||||
|
help=("enable verbose output, can be used multiple times "
|
||||||
|
"to increase verbosity level"))
|
||||||
|
parser.add_argument("--always-succeed", action="store_true",
|
||||||
|
help="always exit with a return code of 0, used for testing")
|
||||||
|
|
||||||
|
return parser.parse_args(argv)
|
||||||
|
|
||||||
|
def _init_log(verbose):
|
||||||
|
"""Initialize a logger object."""
|
||||||
|
log = logging.getLogger(__file__)
|
||||||
|
|
||||||
|
console = logging.StreamHandler()
|
||||||
|
console.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
|
||||||
|
log.addHandler(console)
|
||||||
|
|
||||||
|
if verbose and verbose > 1:
|
||||||
|
log.setLevel(logging.DEBUG)
|
||||||
|
elif verbose and verbose > 0:
|
||||||
|
log.setLevel(logging.INFO)
|
||||||
|
else:
|
||||||
|
log.setLevel(logging.WARNING)
|
||||||
|
|
||||||
|
return log
|
||||||
|
|
||||||
|
def main(argv=None):
|
||||||
|
args = _parse_args(argv)
|
||||||
|
|
||||||
|
log = _init_log(args.verbose)
|
||||||
|
|
||||||
|
log.info(f"inject_slids_in_llext: {args.elf_file}")
|
||||||
|
|
||||||
|
preparator = LLEXTSymtabPreparator(args.elf_file, log)
|
||||||
|
|
||||||
|
res = preparator.prepare_llext()
|
||||||
|
|
||||||
|
if args.always_succeed:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if res == 0 and args.output_file:
|
||||||
|
shutil.copy(args.elf_file, args.output_file)
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main(sys.argv[1:]))
|
377
scripts/build/llext_prepare_exptab.py
Executable file
377
scripts/build/llext_prepare_exptab.py
Executable file
|
@ -0,0 +1,377 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# Copyright (c) 2024 STMicroelectronics
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
"""
|
||||||
|
Script to prepare the LLEXT exports table of a Zephyr ELF
|
||||||
|
|
||||||
|
This script performs compile-time processing of the LLEXT exports
|
||||||
|
table for usage at runtime by the LLEXT subsystem code. The table
|
||||||
|
is a special section filled with 'llext_const_symbol' structures
|
||||||
|
generated by the EXPORT_SYMBOL macro.
|
||||||
|
|
||||||
|
Currently, the preparatory work consists mostly of sorting the
|
||||||
|
exports table to allow usage of binary search algorithms at runtime.
|
||||||
|
If CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID option is enabled, SLIDs
|
||||||
|
of all exported functions are also injected in the export table by
|
||||||
|
this script. (In this case, the preparation process is destructive)
|
||||||
|
"""
|
||||||
|
|
||||||
|
import llext_slidlib
|
||||||
|
|
||||||
|
from elftools.elf.elffile import ELFFile
|
||||||
|
from elftools.elf.sections import Section
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
import pathlib
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
|
||||||
|
#!!!!! WARNING !!!!!
|
||||||
|
#
|
||||||
|
#These constants MUST be kept in sync with the linker scripts
|
||||||
|
#and the EXPORT_SYMBOL macro located in 'subsys/llext/llext.h'.
|
||||||
|
#Otherwise, the LLEXT subsystem will be broken!
|
||||||
|
#
|
||||||
|
#!!!!! WARNING !!!!!
|
||||||
|
|
||||||
|
LLEXT_EXPORT_TABLE_SECTION_NAME = "llext_const_symbol_area"
|
||||||
|
LLEXT_EXPORT_NAMES_SECTION_NAME = "llext_exports_strtab"
|
||||||
|
|
||||||
|
def _llext_const_symbol_struct(ptr_size: int, endianness: str):
|
||||||
|
"""
|
||||||
|
ptr_size -- Platform pointer size in bytes
|
||||||
|
endianness -- Platform endianness ('little'/'big')
|
||||||
|
"""
|
||||||
|
endspec = "<" if endianness == 'little' else ">"
|
||||||
|
if ptr_size == 4:
|
||||||
|
ptrspec = "I"
|
||||||
|
elif ptr_size == 8:
|
||||||
|
ptrspec = "Q"
|
||||||
|
|
||||||
|
# struct llext_const_symbol
|
||||||
|
# contains just two pointers.
|
||||||
|
lcs_spec = endspec + 2 * ptrspec
|
||||||
|
return struct.Struct(lcs_spec)
|
||||||
|
|
||||||
|
#ELF Shdr flag applied to the export table section, to indicate
|
||||||
|
#the section has already been prepared by this script. This is
|
||||||
|
#mostly a security measure to prevent the script from running
|
||||||
|
#twice on the same ELF file, which can result in catastrophic
|
||||||
|
#failures if SLID-based linking is enabled (in this case, the
|
||||||
|
#preparation process is destructive).
|
||||||
|
#
|
||||||
|
#This flag is part of the SHF_MASKOS mask, of which all bits
|
||||||
|
#are "reserved for operating system-specific semantics".
|
||||||
|
#See: https://refspecs.linuxbase.org/elf/gabi4+/ch4.sheader.html
|
||||||
|
SHF_LLEXT_PREPARATION_DONE = 0x08000000
|
||||||
|
|
||||||
|
class SectionDescriptor():
|
||||||
|
"""ELF Section descriptor
|
||||||
|
|
||||||
|
This is a wrapper class around pyelftools' "Section" object.
|
||||||
|
"""
|
||||||
|
def __init__(self, elffile, section_name):
|
||||||
|
self.name = section_name
|
||||||
|
self.section = elffile.get_section_by_name(section_name)
|
||||||
|
if not isinstance(self.section, Section):
|
||||||
|
raise KeyError(f"section {section_name} not found")
|
||||||
|
|
||||||
|
self.shdr_index = elffile.get_section_index(section_name)
|
||||||
|
self.shdr_offset = elffile['e_shoff'] + \
|
||||||
|
self.shdr_index * elffile['e_shentsize']
|
||||||
|
self.size = self.section['sh_size']
|
||||||
|
self.flags = self.section['sh_flags']
|
||||||
|
self.offset = self.section['sh_offset']
|
||||||
|
|
||||||
|
class LLEXTExptabManipulator():
|
||||||
|
"""Class used to wrap the LLEXT export table manipulation."""
|
||||||
|
def __init__(self, elf_fd, exptab_file_offset, lcs_struct, exports_count):
|
||||||
|
self.fd = elf_fd
|
||||||
|
self.exports_count = exports_count
|
||||||
|
self.base_offset = exptab_file_offset
|
||||||
|
self.lcs_struct = lcs_struct
|
||||||
|
|
||||||
|
def _seek_to_sym(self, index):
|
||||||
|
self.fd.seek(self.base_offset + index * self.lcs_struct.size)
|
||||||
|
|
||||||
|
def __getitem__(self, index):
|
||||||
|
if not isinstance(index, int):
|
||||||
|
raise TypeError(f"invalid type {type(index)} for index")
|
||||||
|
|
||||||
|
if index >= self.exports_count:
|
||||||
|
raise IndexError(f"index {index} is out of bounds (max {self.exports_count})")
|
||||||
|
|
||||||
|
self._seek_to_sym(index)
|
||||||
|
return self.lcs_struct.unpack(self.fd.read(self.lcs_struct.size))
|
||||||
|
|
||||||
|
def __setitem__(self, index, item):
|
||||||
|
if not isinstance(index, int):
|
||||||
|
raise TypeError(f"invalid type {type(index)} for index")
|
||||||
|
|
||||||
|
if index >= self.exports_count:
|
||||||
|
raise IndexError(f"index {index} is out of bounds (max {self.exports_count})")
|
||||||
|
|
||||||
|
(addr_or_slid, sym_addr) = item
|
||||||
|
|
||||||
|
self._seek_to_sym(index)
|
||||||
|
self.fd.write(self.lcs_struct.pack(addr_or_slid, sym_addr))
|
||||||
|
|
||||||
|
class ZephyrElfExptabPreparator():
|
||||||
|
"""Prepares the LLEXT export table of a Zephyr ELF.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
elf_path: path to the Zephyr ELF to prepare
|
||||||
|
log: a logging.Logger object
|
||||||
|
slid_listing_path: path to the file where SLID listing should be saved
|
||||||
|
"""
|
||||||
|
def __init__(self, elf_path: str, log: logging.Logger, slid_listing_path: str | None):
|
||||||
|
self.elf_path = elf_path
|
||||||
|
self.elf_fd = open(self.elf_path, 'rb+')
|
||||||
|
self.elf = ELFFile(self.elf_fd)
|
||||||
|
self.log = log
|
||||||
|
|
||||||
|
# Lazy-open the SLID listing file to ensure it is only created when necessary
|
||||||
|
self.slid_listing_path = slid_listing_path
|
||||||
|
self.slid_listing_fd = None
|
||||||
|
|
||||||
|
def _prepare_exptab_for_slid_linking(self):
|
||||||
|
"""
|
||||||
|
IMPLEMENTATION NOTES:
|
||||||
|
In the linker script, we declare the export names table
|
||||||
|
as starting at address 0. Thanks to this, all "pointers"
|
||||||
|
to that section are equal to the offset inside the section.
|
||||||
|
Also note that symbol names are always NUL-terminated.
|
||||||
|
|
||||||
|
The export table is sorted by SLID in ASCENDING order.
|
||||||
|
"""
|
||||||
|
def read_symbol_name(name_ptr):
|
||||||
|
raw_name = b''
|
||||||
|
self.elf_fd.seek(self.expstrtab_section.offset + name_ptr)
|
||||||
|
|
||||||
|
c = self.elf_fd.read(1)
|
||||||
|
while c != b'\0':
|
||||||
|
raw_name += c
|
||||||
|
c = self.elf_fd.read(1)
|
||||||
|
|
||||||
|
return raw_name.decode("utf-8")
|
||||||
|
|
||||||
|
#1) Load the export table
|
||||||
|
exports_list = []
|
||||||
|
for (name_ptr, export_address) in self.exptab_manipulator:
|
||||||
|
export_name = read_symbol_name(name_ptr)
|
||||||
|
exports_list.append((export_name, export_address))
|
||||||
|
|
||||||
|
#2) Generate the SLID for all exports
|
||||||
|
collided = False
|
||||||
|
sorted_exptab = dict()
|
||||||
|
for export_name, export_addr in exports_list:
|
||||||
|
slid = llext_slidlib.generate_slid(export_name, self.ptrsize)
|
||||||
|
|
||||||
|
collision = sorted_exptab.get(slid)
|
||||||
|
if collision:
|
||||||
|
#Don't abort immediately on collision: if there are others, we want to log them all.
|
||||||
|
self.log.error(f"SLID collision: {export_name} and {collision[0]} have the same SLID 0x{slid:X}")
|
||||||
|
collided = True
|
||||||
|
else:
|
||||||
|
sorted_exptab[slid] = (export_name, export_addr)
|
||||||
|
|
||||||
|
if collided:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
#3) Sort the export table (order specified above)
|
||||||
|
sorted_exptab = dict(sorted(sorted_exptab.items()))
|
||||||
|
|
||||||
|
#4) Write the updated export table to ELF, and dump
|
||||||
|
#to SLID listing if requested by caller
|
||||||
|
if self.slid_listing_path:
|
||||||
|
self.slid_listing_fd = open(self.slid_listing_path, "w")
|
||||||
|
|
||||||
|
def slidlist_write(msg):
|
||||||
|
if self.slid_listing_fd:
|
||||||
|
self.slid_listing_fd.write(msg + "\n")
|
||||||
|
|
||||||
|
slidlist_write(f"/* SLID listing generated by {__file__} */")
|
||||||
|
slidlist_write("//")
|
||||||
|
slidlist_write("// This file contains the 'SLID -> name' mapping for all")
|
||||||
|
slidlist_write("// symbols exported to LLEXT by this Zephyr executable.")
|
||||||
|
slidlist_write("")
|
||||||
|
|
||||||
|
self.log.info("SLID -> export name mapping:")
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
for (slid, name_and_symaddr) in sorted_exptab.items():
|
||||||
|
slid_as_str = llext_slidlib.format_slid(slid, self.ptrsize)
|
||||||
|
msg = f"{slid_as_str} -> {name_and_symaddr[0]}"
|
||||||
|
self.log.info(msg)
|
||||||
|
slidlist_write(msg)
|
||||||
|
|
||||||
|
self.exptab_manipulator[i] = (slid, name_and_symaddr[1])
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
if self.slid_listing_fd:
|
||||||
|
self.slid_listing_fd.close()
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def _prepare_exptab_for_str_linking(self):
|
||||||
|
#TODO: sort the export table by symbol
|
||||||
|
# name to allow binary search too
|
||||||
|
#
|
||||||
|
# Plan of action:
|
||||||
|
# 1) Locate in which section the names are located
|
||||||
|
# 2) Load the export table and resolve names
|
||||||
|
# 3) Sort the exports by name
|
||||||
|
# WARN: THIS MUST USE THE SAME SORTING RULES
|
||||||
|
# AS LLEXT CODE OR DICHOTOMIC SEARCH WILL BREAK
|
||||||
|
# Using a custom sorting function might be required.
|
||||||
|
# 4) Write back the updated export table
|
||||||
|
#
|
||||||
|
# N.B.: reusing part of the code in _prepare_elf_for_slid_linking
|
||||||
|
# might be possible and desireable.
|
||||||
|
#
|
||||||
|
# As of writing, this function will never be called as this script
|
||||||
|
# is only called if CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID is enabled,
|
||||||
|
# which makes _prepare_exptab_for_slid_linking be called instead.
|
||||||
|
#
|
||||||
|
self.log.warn(f"_prepare_exptab_for_str_linking: do nothing")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def _set_prep_done_shdr_flag(self):
|
||||||
|
#Offset and size of the 'sh_flags' member of
|
||||||
|
#the Elf_Shdr structure. The offset does not
|
||||||
|
#change between ELF32 and ELF64. Size in both
|
||||||
|
#is equal to pointer size (4 bytes for ELF32,
|
||||||
|
#8 bytes for ELF64).
|
||||||
|
SHF_OFFSET = 8
|
||||||
|
SHF_SIZE = self.ptrsize
|
||||||
|
|
||||||
|
off = self.exptab_section.shdr_offset + SHF_OFFSET
|
||||||
|
|
||||||
|
#Read existing sh_flags, set the PREPARATION_DONE flag
|
||||||
|
#and write back the new value.
|
||||||
|
self.elf_fd.seek(off)
|
||||||
|
sh_flags = int.from_bytes(self.elf_fd.read(SHF_SIZE), self.endianness)
|
||||||
|
|
||||||
|
sh_flags |= SHF_LLEXT_PREPARATION_DONE
|
||||||
|
|
||||||
|
self.elf_fd.seek(off)
|
||||||
|
self.elf_fd.write(int.to_bytes(sh_flags, self.ptrsize, self.endianness))
|
||||||
|
|
||||||
|
def _prepare_inner(self):
|
||||||
|
# Locate the export table section
|
||||||
|
try:
|
||||||
|
self.exptab_section = SectionDescriptor(
|
||||||
|
self.elf, LLEXT_EXPORT_TABLE_SECTION_NAME)
|
||||||
|
except KeyError as e:
|
||||||
|
self.log.error(e.args[0])
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# Abort if the ELF has already been processed
|
||||||
|
if (self.exptab_section.flags & SHF_LLEXT_PREPARATION_DONE) != 0:
|
||||||
|
self.log.warning("exptab section flagged with LLEXT_PREPARATION_DONE "
|
||||||
|
"- not preparing again")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# Get the struct.Struct for export table entry
|
||||||
|
self.ptrsize = self.elf.elfclass // 8
|
||||||
|
self.endianness = 'little' if self.elf.little_endian else 'big'
|
||||||
|
self.lcs_struct = _llext_const_symbol_struct(self.ptrsize, self.endianness)
|
||||||
|
|
||||||
|
# Verify that the export table size is coherent
|
||||||
|
if (self.exptab_section.size % self.lcs_struct.size) != 0:
|
||||||
|
self.log.error(f"export table size (0x{self.exptab_section.size:X}) "
|
||||||
|
f"not aligned to 'llext_const_symbol' size (0x{self.lcs_struct.size:X})")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# Create the export table manipulator
|
||||||
|
num_exports = self.exptab_section.size // self.lcs_struct.size
|
||||||
|
self.exptab_manipulator = LLEXTExptabManipulator(
|
||||||
|
self.elf_fd, self.exptab_section.offset, self.lcs_struct, num_exports)
|
||||||
|
|
||||||
|
# Attempt to locate the export names section
|
||||||
|
try:
|
||||||
|
self.expstrtab_section = SectionDescriptor(
|
||||||
|
self.elf, LLEXT_EXPORT_NAMES_SECTION_NAME)
|
||||||
|
except KeyError:
|
||||||
|
self.expstrtab_section = None
|
||||||
|
|
||||||
|
self.log.debug(f"exports table section at file offset 0x{self.exptab_section.offset:X}")
|
||||||
|
if self.expstrtab_section:
|
||||||
|
self.log.debug(f"exports strtab section at file offset 0x{self.expstrtab_section.offset:X}")
|
||||||
|
else:
|
||||||
|
self.log.debug("no exports strtab section in ELF")
|
||||||
|
self.log.info(f"{num_exports} symbols are exported to LLEXTs by this ELF")
|
||||||
|
|
||||||
|
# Perform the export table preparation
|
||||||
|
if self.expstrtab_section:
|
||||||
|
res = self._prepare_exptab_for_slid_linking()
|
||||||
|
else:
|
||||||
|
res = self._prepare_exptab_for_str_linking()
|
||||||
|
|
||||||
|
if res == 0: # Add the "prepared" flag to export table section
|
||||||
|
self._set_prep_done_shdr_flag()
|
||||||
|
|
||||||
|
def prepare_elf(self):
|
||||||
|
res = self._prepare_inner()
|
||||||
|
self.elf_fd.close()
|
||||||
|
return res
|
||||||
|
|
||||||
|
# pylint: disable=duplicate-code
|
||||||
|
def _parse_args(argv):
|
||||||
|
"""Parse the command line arguments."""
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description=__doc__,
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
allow_abbrev=False)
|
||||||
|
|
||||||
|
parser.add_argument("-f", "--elf-file", default=pathlib.Path("build", "zephyr", "zephyr.elf"),
|
||||||
|
help="ELF file to process")
|
||||||
|
parser.add_argument("-sl", "--slid-listing",
|
||||||
|
help=("write the SLID listing to a file (only useful"
|
||||||
|
"when CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID is enabled) "))
|
||||||
|
parser.add_argument("-v", "--verbose", action="count",
|
||||||
|
help=("enable verbose output, can be used multiple times "
|
||||||
|
"to increase verbosity level"))
|
||||||
|
parser.add_argument("--always-succeed", action="store_true",
|
||||||
|
help="always exit with a return code of 0, used for testing")
|
||||||
|
|
||||||
|
return parser.parse_args(argv)
|
||||||
|
|
||||||
|
def _init_log(verbose):
|
||||||
|
"""Initialize a logger object."""
|
||||||
|
log = logging.getLogger(__file__)
|
||||||
|
|
||||||
|
console = logging.StreamHandler()
|
||||||
|
console.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
|
||||||
|
log.addHandler(console)
|
||||||
|
|
||||||
|
if verbose and verbose > 1:
|
||||||
|
log.setLevel(logging.DEBUG)
|
||||||
|
elif verbose and verbose > 0:
|
||||||
|
log.setLevel(logging.INFO)
|
||||||
|
else:
|
||||||
|
log.setLevel(logging.WARNING)
|
||||||
|
|
||||||
|
return log
|
||||||
|
|
||||||
|
def main(argv=None):
|
||||||
|
args = _parse_args(argv)
|
||||||
|
|
||||||
|
log = _init_log(args.verbose)
|
||||||
|
|
||||||
|
log.info(f"prepare_llext_exptab: {args.elf_file}")
|
||||||
|
|
||||||
|
preparator = ZephyrElfExptabPreparator(args.elf_file, log, args.slid_listing)
|
||||||
|
|
||||||
|
res = preparator.prepare_elf()
|
||||||
|
|
||||||
|
if args.always_succeed:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main(sys.argv[1:]))
|
65
scripts/build/llext_slidlib.py
Executable file
65
scripts/build/llext_slidlib.py
Executable file
|
@ -0,0 +1,65 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# Copyright (c) 2024 STMicroelectronics
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
"""
|
||||||
|
This file implements the Symbol Link Identifer (SLID)
|
||||||
|
generation code, for use by the LLEXT subsystem.
|
||||||
|
SLID-based linking is enabled by the Kconfig
|
||||||
|
option 'CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID'.
|
||||||
|
|
||||||
|
When executed as a script, this file can be used as
|
||||||
|
an interactive prompt to calculate the SLID of arbitrary
|
||||||
|
symbols, which can be useful for debugging purposes.
|
||||||
|
|
||||||
|
IMPLEMENTATION NOTES:
|
||||||
|
Currently, SLIDs are generated by taking the first
|
||||||
|
[pointer size] bytes of the symbol name's SHA-256
|
||||||
|
hash, taken in big-endian order.
|
||||||
|
|
||||||
|
This ordering provides one advantage: the 32-bit
|
||||||
|
SLID for an export is present in the top 32 bits of
|
||||||
|
the 64-bit SLID for the same export.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from hashlib import sha256
|
||||||
|
|
||||||
|
def generate_slid(symbol_name: str, slid_size: int) -> int:
|
||||||
|
"""
|
||||||
|
Generates the Symbol Link Identifier (SLID) for a symbol.
|
||||||
|
|
||||||
|
symbol_name: Name of the symbol for which to generate a SLID
|
||||||
|
slid_side: Size of the SLID in bytes (4/8)
|
||||||
|
"""
|
||||||
|
if slid_size not in (4, 8):
|
||||||
|
raise AssertionError(f"Invalid SLID size {slid_size}")
|
||||||
|
|
||||||
|
m = sha256()
|
||||||
|
m.update(symbol_name.encode("utf-8"))
|
||||||
|
hash = m.digest()
|
||||||
|
return int.from_bytes(hash[0:slid_size], byteorder='big', signed=False)
|
||||||
|
|
||||||
|
def format_slid(slid: int, slid_size: int) -> str:
|
||||||
|
if slid_size == 4:
|
||||||
|
fmt = f"0x{slid:08X}"
|
||||||
|
elif slid_size == 8:
|
||||||
|
fmt = f"0x{slid:016X}"
|
||||||
|
return fmt
|
||||||
|
|
||||||
|
def repl():
|
||||||
|
while True:
|
||||||
|
sym_name = input("Symbol name? ")
|
||||||
|
slid32 = generate_slid(sym_name, 4)
|
||||||
|
slid64 = generate_slid(sym_name, 8)
|
||||||
|
print(f" 32-bit SLID for '{sym_name}': {format_slid(slid32, 4)}")
|
||||||
|
print(f" 64-bit SLID for '{sym_name}': {format_slid(slid64, 8)}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("LLEXT SLID calculation REPL")
|
||||||
|
print("Press ^C to exit.")
|
||||||
|
try:
|
||||||
|
repl()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print()
|
|
@ -92,6 +92,10 @@ SECTIONS
|
||||||
|
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The .plt and .iplt are here according to
|
* The .plt and .iplt are here according to
|
||||||
* 'riscv32-zephyr-elf-ld --verbose', before text section.
|
* 'riscv32-zephyr-elf-ld --verbose', before text section.
|
||||||
|
|
|
@ -113,6 +113,10 @@ SECTIONS
|
||||||
|
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_GEN_ISR_TABLES
|
#ifdef CONFIG_GEN_ISR_TABLES
|
||||||
#include <zephyr/linker/intlist.ld>
|
#include <zephyr/linker/intlist.ld>
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -177,6 +177,10 @@ SECTIONS
|
||||||
|
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_GEN_ISR_TABLES
|
#ifdef CONFIG_GEN_ISR_TABLES
|
||||||
#include <zephyr/linker/intlist.ld>
|
#include <zephyr/linker/intlist.ld>
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -152,6 +152,10 @@ SECTIONS
|
||||||
|
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
|
|
||||||
/* --- RTC BEGIN --- */
|
/* --- RTC BEGIN --- */
|
||||||
|
|
||||||
/* RTC fast memory holds RTC wake stub code,
|
/* RTC fast memory holds RTC wake stub code,
|
||||||
|
|
|
@ -55,6 +55,10 @@ SECTIONS
|
||||||
{
|
{
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
|
|
||||||
_image_iram_start = LOADADDR(.iram0.vectors);
|
_image_iram_start = LOADADDR(.iram0.vectors);
|
||||||
_image_iram_size = LOADADDR(_TEXT_SECTION_NAME) + SIZEOF(_TEXT_SECTION_NAME) - _image_iram_start;
|
_image_iram_size = LOADADDR(_TEXT_SECTION_NAME) + SIZEOF(_TEXT_SECTION_NAME) - _image_iram_start;
|
||||||
_image_iram_vaddr = ADDR(.iram0.vectors);
|
_image_iram_vaddr = ADDR(.iram0.vectors);
|
||||||
|
|
|
@ -129,6 +129,10 @@ SECTIONS
|
||||||
|
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
|
|
||||||
/* --- START OF RTC --- */
|
/* --- START OF RTC --- */
|
||||||
|
|
||||||
.rtc.text :
|
.rtc.text :
|
||||||
|
|
|
@ -136,6 +136,10 @@ SECTIONS
|
||||||
/* Virtual non-loadable sections */
|
/* Virtual non-loadable sections */
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
|
|
||||||
/* --- START OF RTC --- */
|
/* --- START OF RTC --- */
|
||||||
|
|
||||||
/* RTC fast memory holds RTC wake stub code,
|
/* RTC fast memory holds RTC wake stub code,
|
||||||
|
|
|
@ -135,6 +135,10 @@ SECTIONS
|
||||||
/* Virtual non-loadable sections */
|
/* Virtual non-loadable sections */
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
|
|
||||||
/* --- START OF RTC --- */
|
/* --- START OF RTC --- */
|
||||||
|
|
||||||
/* RTC fast memory holds RTC wake stub code */
|
/* RTC fast memory holds RTC wake stub code */
|
||||||
|
|
|
@ -38,6 +38,10 @@ SECTIONS
|
||||||
{
|
{
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Send .iram0 code to iram */
|
/* Send .iram0 code to iram */
|
||||||
.iram0.vectors : ALIGN(4)
|
.iram0.vectors : ALIGN(4)
|
||||||
{
|
{
|
||||||
|
|
|
@ -104,6 +104,10 @@ SECTIONS
|
||||||
|
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* .plt and .iplt are here according to 'arm-zephyr-elf-ld --verbose',
|
* .plt and .iplt are here according to 'arm-zephyr-elf-ld --verbose',
|
||||||
* before text section.
|
* before text section.
|
||||||
|
|
|
@ -490,4 +490,8 @@ SECTIONS {
|
||||||
#ifdef CONFIG_GEN_ISR_TABLES
|
#ifdef CONFIG_GEN_ISR_TABLES
|
||||||
#include <zephyr/linker/intlist.ld>
|
#include <zephyr/linker/intlist.ld>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -503,4 +503,8 @@ SECTIONS {
|
||||||
#ifdef CONFIG_GEN_ISR_TABLES
|
#ifdef CONFIG_GEN_ISR_TABLES
|
||||||
#include <zephyr/linker/intlist.ld>
|
#include <zephyr/linker/intlist.ld>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,6 +91,10 @@ SECTIONS
|
||||||
|
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The .plt and .iplt are here according to
|
* The .plt and .iplt are here according to
|
||||||
* 'riscv32-zephyr-elf-ld --verbose', before text section.
|
* 'riscv32-zephyr-elf-ld --verbose', before text section.
|
||||||
|
|
|
@ -173,6 +173,10 @@ SECTIONS
|
||||||
{
|
{
|
||||||
|
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
.ResetVector.text : ALIGN(4)
|
.ResetVector.text : ALIGN(4)
|
||||||
{
|
{
|
||||||
_ResetVector_text_start = ABSOLUTE(.);
|
_ResetVector_text_start = ABSOLUTE(.);
|
||||||
|
|
|
@ -179,6 +179,10 @@ SECTIONS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
.ResetVector.text : ALIGN(4)
|
.ResetVector.text : ALIGN(4)
|
||||||
{
|
{
|
||||||
_ResetVector_text_start = ABSOLUTE(.);
|
_ResetVector_text_start = ABSOLUTE(.);
|
||||||
|
|
|
@ -173,6 +173,10 @@ SECTIONS
|
||||||
{
|
{
|
||||||
|
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
.ResetVector.text : ALIGN(4)
|
.ResetVector.text : ALIGN(4)
|
||||||
{
|
{
|
||||||
_ResetVector_text_start = ABSOLUTE(.);
|
_ResetVector_text_start = ABSOLUTE(.);
|
||||||
|
|
|
@ -173,6 +173,10 @@ SECTIONS
|
||||||
{
|
{
|
||||||
|
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
.ResetVector.text : ALIGN(4)
|
.ResetVector.text : ALIGN(4)
|
||||||
{
|
{
|
||||||
_ResetVector_text_start = ABSOLUTE(.);
|
_ResetVector_text_start = ABSOLUTE(.);
|
||||||
|
|
|
@ -71,6 +71,10 @@ SECTIONS
|
||||||
|
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* .plt and .iplt are here according to 'arm-zephyr-elf-ld --verbose',
|
* .plt and .iplt are here according to 'arm-zephyr-elf-ld --verbose',
|
||||||
* before text section.
|
* before text section.
|
||||||
|
|
|
@ -134,6 +134,10 @@ SECTIONS
|
||||||
{
|
{
|
||||||
|
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
.ResetVector.text : ALIGN(4)
|
.ResetVector.text : ALIGN(4)
|
||||||
{
|
{
|
||||||
_ResetVector_text_start = ABSOLUTE(.);
|
_ResetVector_text_start = ABSOLUTE(.);
|
||||||
|
|
|
@ -87,6 +87,10 @@ SECTIONS
|
||||||
|
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
|
|
||||||
SECTION_PROLOGUE(.plt,,)
|
SECTION_PROLOGUE(.plt,,)
|
||||||
{
|
{
|
||||||
*(.plt)
|
*(.plt)
|
||||||
|
|
|
@ -13,6 +13,11 @@ SECTIONS
|
||||||
{
|
{
|
||||||
|
|
||||||
#include <zephyr/linker/rel-sections.ld>
|
#include <zephyr/linker/rel-sections.ld>
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT
|
||||||
|
#include <zephyr/linker/llext-sections.ld>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_OPENAMP_RSC_TABLE
|
#ifdef CONFIG_OPENAMP_RSC_TABLE
|
||||||
|
|
||||||
SECTION_PROLOGUE(.resource_table,, SUBALIGN(4))
|
SECTION_PROLOGUE(.resource_table,, SUBALIGN(4))
|
||||||
|
|
|
@ -66,6 +66,21 @@ config LLEXT_STORAGE_WRITABLE
|
||||||
Select if LLEXT storage is writable, i.e. if extensions are stored in
|
Select if LLEXT storage is writable, i.e. if extensions are stored in
|
||||||
RAM and can be modified in place
|
RAM and can be modified in place
|
||||||
|
|
||||||
|
config LLEXT_EXPORT_BUILTINS_BY_SLID
|
||||||
|
bool "Export built-in symbols to llexts via SLIDs"
|
||||||
|
help
|
||||||
|
When enabled, symbols exported from the Zephyr kernel
|
||||||
|
or application (via EXPORT_SYMBOL) are linked to LLEXTs
|
||||||
|
via Symbol Link Identifiers (SLIDs) instead of name.
|
||||||
|
|
||||||
|
Enabling this option provides a huge size reduction,
|
||||||
|
makes the linking process faster and provides more
|
||||||
|
confidentiality, as exported symbol names are dropped
|
||||||
|
from the binary. However, it can make LLEXT debugging
|
||||||
|
harder and prevents usage of 'llext_find_sym' to look
|
||||||
|
up symbols from the built-in table by name. It also
|
||||||
|
requires the LLEXTs to be post-processed after build.
|
||||||
|
|
||||||
module = LLEXT
|
module = LLEXT
|
||||||
module-str = llext
|
module-str = llext
|
||||||
source "subsys/logging/Kconfig.template.log_config"
|
source "subsys/logging/Kconfig.template.log_config"
|
||||||
|
|
|
@ -24,6 +24,12 @@ LOG_MODULE_REGISTER(llext, CONFIG_LLEXT_LOG_LEVEL);
|
||||||
#define LLEXT_PAGE_SIZE 32
|
#define LLEXT_PAGE_SIZE 32
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID
|
||||||
|
#define SYM_NAME_OR_SLID(name, slid) ((const char *)slid)
|
||||||
|
#else
|
||||||
|
#define SYM_NAME_OR_SLID(name, slid) name
|
||||||
|
#endif
|
||||||
|
|
||||||
K_HEAP_DEFINE(llext_heap, CONFIG_LLEXT_HEAP_SIZE * 1024);
|
K_HEAP_DEFINE(llext_heap, CONFIG_LLEXT_HEAP_SIZE * 1024);
|
||||||
|
|
||||||
static const char ELF_MAGIC[] = {0x7f, 'E', 'L', 'F'};
|
static const char ELF_MAGIC[] = {0x7f, 'E', 'L', 'F'};
|
||||||
|
@ -118,11 +124,27 @@ const void *llext_find_sym(const struct llext_symtable *sym_table, const char *s
|
||||||
{
|
{
|
||||||
if (sym_table == NULL) {
|
if (sym_table == NULL) {
|
||||||
/* Built-in symbol table */
|
/* Built-in symbol table */
|
||||||
|
#ifdef CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID
|
||||||
|
/* 'sym_name' is actually a SLID to search for */
|
||||||
|
uintptr_t slid = (uintptr_t)sym_name;
|
||||||
|
|
||||||
|
/* TODO: perform a binary search instead of linear.
|
||||||
|
* Note that - as of writing - the llext_const_symbol_area
|
||||||
|
* section is sorted in ascending SLID order.
|
||||||
|
* (see scripts/build/llext_prepare_exptab.py)
|
||||||
|
*/
|
||||||
|
STRUCT_SECTION_FOREACH(llext_const_symbol, sym) {
|
||||||
|
if (slid == sym->slid) {
|
||||||
|
return sym->addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
STRUCT_SECTION_FOREACH(llext_const_symbol, sym) {
|
STRUCT_SECTION_FOREACH(llext_const_symbol, sym) {
|
||||||
if (strcmp(sym->name, sym_name) == 0) {
|
if (strcmp(sym->name, sym_name) == 0) {
|
||||||
return sym->addr;
|
return sym->addr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
/* find symbols in module */
|
/* find symbols in module */
|
||||||
for (size_t i = 0; i < sym_table->sym_cnt; i++) {
|
for (size_t i = 0; i < sym_table->sym_cnt; i++) {
|
||||||
|
@ -728,7 +750,8 @@ static void llext_link_plt(struct llext_loader *ldr, struct llext *ext,
|
||||||
|
|
||||||
switch (stb) {
|
switch (stb) {
|
||||||
case STB_GLOBAL:
|
case STB_GLOBAL:
|
||||||
link_addr = llext_find_sym(NULL, name);
|
link_addr = llext_find_sym(NULL,
|
||||||
|
SYM_NAME_OR_SLID(name, sym_tbl.st_value));
|
||||||
|
|
||||||
if (!link_addr)
|
if (!link_addr)
|
||||||
link_addr = llext_find_sym(&ext->sym_tab, name);
|
link_addr = llext_find_sym(&ext->sym_tab, name);
|
||||||
|
@ -864,7 +887,8 @@ static int llext_link(struct llext_loader *ldr, struct llext *ext, bool do_local
|
||||||
link_addr = 0;
|
link_addr = 0;
|
||||||
} else if (sym.st_shndx == SHN_UNDEF) {
|
} else if (sym.st_shndx == SHN_UNDEF) {
|
||||||
/* If symbol is undefined, then we need to look it up */
|
/* If symbol is undefined, then we need to look it up */
|
||||||
link_addr = (uintptr_t)llext_find_sym(NULL, name);
|
link_addr = (uintptr_t)llext_find_sym(NULL,
|
||||||
|
SYM_NAME_OR_SLID(name, sym.st_value));
|
||||||
|
|
||||||
if (link_addr == 0) {
|
if (link_addr == 0) {
|
||||||
LOG_ERR("Undefined symbol with no entry in "
|
LOG_ERR("Undefined symbol with no entry in "
|
||||||
|
|
|
@ -24,6 +24,20 @@ LOG_MODULE_REGISTER(test_llext_simple);
|
||||||
#define LLEXT_CONST const
|
#define LLEXT_CONST const
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID
|
||||||
|
#define LLEXT_FIND_BUILTIN_SYM(symbol_name) llext_find_sym(NULL, symbol_name ## _SLID)
|
||||||
|
|
||||||
|
#ifdef CONFIG_64BIT
|
||||||
|
#define printk_SLID ((const char *)0x87B3105268827052ull)
|
||||||
|
#define z_impl_ext_syscall_fail_SLID ((const char *)0xD58BC0E7C64CD965ull)
|
||||||
|
#else
|
||||||
|
#define printk_SLID ((const char *)0x87B31052ull)
|
||||||
|
#define z_impl_ext_syscall_fail_SLID ((const char *)0xD58BC0E7ull)
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define LLEXT_FIND_BUILTIN_SYM(symbol_name) llext_find_sym(NULL, # symbol_name)
|
||||||
|
#endif
|
||||||
|
|
||||||
struct llext_test {
|
struct llext_test {
|
||||||
const char *name;
|
const char *name;
|
||||||
bool try_userspace;
|
bool try_userspace;
|
||||||
|
@ -271,7 +285,7 @@ ZTEST(llext, test_pre_located)
|
||||||
*/
|
*/
|
||||||
ZTEST(llext, test_printk_exported)
|
ZTEST(llext, test_printk_exported)
|
||||||
{
|
{
|
||||||
const void * const printk_fn = llext_find_sym(NULL, "printk");
|
const void * const printk_fn = LLEXT_FIND_BUILTIN_SYM(printk);
|
||||||
|
|
||||||
zassert_equal(printk_fn, printk, "printk should be an exported symbol");
|
zassert_equal(printk_fn, printk, "printk should be an exported symbol");
|
||||||
}
|
}
|
||||||
|
@ -282,8 +296,9 @@ ZTEST(llext, test_printk_exported)
|
||||||
*/
|
*/
|
||||||
ZTEST(llext, test_ext_syscall_fail)
|
ZTEST(llext, test_ext_syscall_fail)
|
||||||
{
|
{
|
||||||
const void * const esf_fn = llext_find_sym(NULL,
|
const void * const esf_fn = LLEXT_FIND_BUILTIN_SYM(z_impl_ext_syscall_fail);
|
||||||
"z_impl_ext_syscall_fail");
|
|
||||||
|
zassert_not_null(esf_fn, "est_fn should not be NULL");
|
||||||
|
|
||||||
zassert_is_null(*(uintptr_t **)esf_fn, NULL,
|
zassert_is_null(*(uintptr_t **)esf_fn, NULL,
|
||||||
"ext_syscall_fail should be NULL");
|
"ext_syscall_fail should be NULL");
|
||||||
|
|
|
@ -80,3 +80,71 @@ tests:
|
||||||
- CONFIG_USERSPACE=y
|
- CONFIG_USERSPACE=y
|
||||||
- CONFIG_MODULES=y
|
- CONFIG_MODULES=y
|
||||||
- CONFIG_LLEXT_TEST_HELLO=m
|
- CONFIG_LLEXT_TEST_HELLO=m
|
||||||
|
llext.simple.readonly_slid_linking:
|
||||||
|
arch_exclude: xtensa # for now
|
||||||
|
filter: not CONFIG_MPU and not CONFIG_MMU and not CONFIG_SOC_SERIES_S32ZE
|
||||||
|
extra_configs:
|
||||||
|
- arch:arm:CONFIG_ARM_MPU=n
|
||||||
|
- CONFIG_LLEXT_STORAGE_WRITABLE=n
|
||||||
|
- CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID=y
|
||||||
|
llext.simple.readonly_mpu_slid_linking:
|
||||||
|
min_ram: 128
|
||||||
|
arch_exclude: xtensa # for now
|
||||||
|
filter: CONFIG_ARCH_HAS_USERSPACE
|
||||||
|
extra_configs:
|
||||||
|
- CONFIG_USERSPACE=y
|
||||||
|
- CONFIG_LLEXT_STORAGE_WRITABLE=n
|
||||||
|
- CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID=y
|
||||||
|
llext.simple.writable_slid_linking:
|
||||||
|
filter: not CONFIG_MPU and not CONFIG_MMU and not CONFIG_SOC_SERIES_S32ZE
|
||||||
|
extra_configs:
|
||||||
|
- arch:arm:CONFIG_ARM_MPU=n
|
||||||
|
- CONFIG_LLEXT_STORAGE_WRITABLE=y
|
||||||
|
llext.simple.modules_enabled_writable_slid_linking:
|
||||||
|
filter: not CONFIG_MPU and not CONFIG_MMU
|
||||||
|
platform_key:
|
||||||
|
- simulation
|
||||||
|
- arch
|
||||||
|
platform_exclude:
|
||||||
|
- qemu_cortex_a9 # MMU
|
||||||
|
extra_configs:
|
||||||
|
- arch:arm:CONFIG_ARM_MPU=n
|
||||||
|
- CONFIG_MODULES=y
|
||||||
|
- CONFIG_LLEXT_STORAGE_WRITABLE=y
|
||||||
|
- CONFIG_LLEXT_TEST_HELLO=m
|
||||||
|
- CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID=y
|
||||||
|
llext.simple.modules_enabled_writable_relocatable_slid_linking:
|
||||||
|
arch_exclude: arm arm64
|
||||||
|
filter: not CONFIG_MPU and not CONFIG_MMU
|
||||||
|
integration_platforms:
|
||||||
|
- qemu_xtensa
|
||||||
|
extra_configs:
|
||||||
|
- CONFIG_MODULES=y
|
||||||
|
- CONFIG_LLEXT_STORAGE_WRITABLE=y
|
||||||
|
- CONFIG_LLEXT_TYPE_ELF_RELOCATABLE=y
|
||||||
|
- CONFIG_LLEXT_TEST_HELLO=m
|
||||||
|
- CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID=y
|
||||||
|
llext.simple.modules_enabled_readonly_slid_linking:
|
||||||
|
filter: not CONFIG_MPU and not CONFIG_MMU
|
||||||
|
arch_exclude: xtensa # for now
|
||||||
|
platform_key:
|
||||||
|
- simulation
|
||||||
|
- arch
|
||||||
|
platform_exclude:
|
||||||
|
- qemu_cortex_a9 # MMU
|
||||||
|
extra_configs:
|
||||||
|
- arch:arm:CONFIG_ARM_MPU=n
|
||||||
|
- CONFIG_MODULES=y
|
||||||
|
- CONFIG_LLEXT_TEST_HELLO=m
|
||||||
|
- CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID=y
|
||||||
|
llext.simple.modules_enabled_readonly_mpu_slid_linking:
|
||||||
|
filter: CONFIG_ARCH_HAS_USERSPACE
|
||||||
|
arch_exclude: xtensa # for now
|
||||||
|
platform_key:
|
||||||
|
- simulation
|
||||||
|
- arch
|
||||||
|
extra_configs:
|
||||||
|
- CONFIG_USERSPACE=y
|
||||||
|
- CONFIG_MODULES=y
|
||||||
|
- CONFIG_LLEXT_TEST_HELLO=m
|
||||||
|
- CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID=y
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue