llext: add a test for the pre_located feature

This test checks that the pre_located feature works as expected. It
creates a new extension that is manually relocated to a specific address
via the add_llext_command() CMake function invoking a custom linker
command. The test then loads the extension setting the pre_located
option and checks that the symbol is resolved properly.

Signed-off-by: Luca Burelli <l.burelli@arduino.cc>
This commit is contained in:
Luca Burelli 2024-05-29 17:40:27 +02:00 committed by Anas Nashif
commit 435b72dc18
3 changed files with 62 additions and 9 deletions

View file

@ -23,6 +23,10 @@ if(CONFIG_ARM)
endif()
endif()
if (CONFIG_LLEXT_TYPE_ELF_RELOCATABLE AND CONFIG_XTENSA)
list(APPEND ext_names pre_located)
endif()
# generate extension targets foreach extension given by 'ext_names'
foreach(ext_name ${ext_names})
set(ext_src ${PROJECT_SOURCE_DIR}/src/${ext_name}_ext.c)
@ -45,12 +49,15 @@ if(NOT CONFIG_LLEXT_TYPE_ELF_OBJECT)
)
endif()
# Add a dummy custom processing command to test add_llext_command
get_target_property(proc_in_file hello_world_ext lib_output)
get_target_property(proc_out_file hello_world_ext pkg_input)
add_llext_command(
TARGET hello_world_ext
POST_BUILD
COMMAND echo "dummy patching ${proc_in_file} to create ${proc_out_file}"
COMMAND ${CMAKE_COMMAND} -E copy ${proc_in_file} ${proc_out_file}
)
if (CONFIG_LLEXT_TYPE_ELF_RELOCATABLE AND CONFIG_XTENSA)
# Manually fix the pre_located extension's text address at a multiple of 4
get_target_property(pre_located_target pre_located_ext lib_target)
get_target_property(pre_located_file pre_located_ext pkg_input)
add_llext_command(
TARGET pre_located_ext
POST_BUILD
COMMAND ${CMAKE_C_COMPILER}
-Wl,-r -Wl,-Ttext=0xbada110c -nostdlib -nodefaultlibs -nostartfiles
$<TARGET_OBJECTS:${pre_located_target}> -o ${pre_located_file}
)
endif()

View file

@ -0,0 +1,19 @@
/*
* Copyright (c) 2024 Arduino SA.
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* This file contains a simple test extension that defines an entry point
* in the .text section. The entry point is never called, but the symbol
* address is fixed via a linker call and then checked by the test.
*/
#include <zephyr/llext/symbol.h>
void test_entry(void)
{
/* This function is never called */
}
LL_EXTENSION_SYMBOL(test_entry);

View file

@ -237,6 +237,33 @@ static LLEXT_CONST uint8_t multi_file_ext[] __aligned(4) = {
LLEXT_LOAD_UNLOAD(multi_file, true, NULL)
#endif
#if defined(CONFIG_LLEXT_TYPE_ELF_RELOCATABLE) && defined(CONFIG_XTENSA)
static LLEXT_CONST uint8_t pre_located_ext[] __aligned(4) = {
#include "pre_located.inc"
};
ZTEST(llext, test_pre_located)
{
struct llext_buf_loader buf_loader =
LLEXT_BUF_LOADER(pre_located_ext, ARRAY_SIZE(pre_located_ext));
struct llext_loader *loader = &buf_loader.loader;
struct llext_load_param ldr_parm = LLEXT_LOAD_PARAM_DEFAULT;
struct llext *ext = NULL;
const void *test_entry_fn;
int res;
/* load the extension trying to respect the addresses in the ELF */
ldr_parm.pre_located = true;
res = llext_load(loader, "pre_located", &ext, &ldr_parm);
zassert_ok(res, "load should succeed");
/* check the function address is the expected one */
test_entry_fn = llext_find_sym(&ext->exp_tab, "test_entry");
zassert_equal(test_entry_fn, (void *)0xbada110c, "test_entry should be at 0xbada110c");
llext_unload(&ext);
}
#endif
/*
* Ensure that EXPORT_SYMBOL does indeed provide a symbol and a valid address