tests/llext: add init_fini test
Add a test to check the proper support of ELF init arrays, using the new llext_bootstrap function in place of llext_entry in the LLEXT test suite. Signed-off-by: Luca Burelli <l.burelli@arduino.cc>
This commit is contained in:
parent
92385a90d8
commit
dfef264bed
3 changed files with 115 additions and 12 deletions
|
@ -31,6 +31,11 @@ if (CONFIG_LLEXT_STORAGE_WRITABLE)
|
|||
list(APPEND ext_names find_section)
|
||||
endif()
|
||||
|
||||
if (NOT CONFIG_LLEXT_TYPE_ELF_SHAREDLIB)
|
||||
# ELF shared libraries do not support init sections
|
||||
list(APPEND ext_names init_fini)
|
||||
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)
|
||||
|
|
71
tests/subsys/llext/simple/src/init_fini_ext.c
Normal file
71
tests/subsys/llext/simple/src/init_fini_ext.c
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Arduino SA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*
|
||||
* This test checks for proper support of ELF init arrays. This processing is
|
||||
* performed by llext_bootstrap(), which gets the array of function pointers
|
||||
* from LLEXT via the llext_get_fn_table() syscall.
|
||||
*
|
||||
* Each function in this test shifts the number left by 4 bits and sets the
|
||||
* lower 4 bits to a specific value. The proper init sequence (preinit_fn_1,
|
||||
* preinit_fn_2, init_fn) would leave number set to 0x123; the termination
|
||||
* function will further shift the number to 0x1234. If a different result is
|
||||
* detected, then either not all routines were executed, or their order was not
|
||||
* correct.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <zephyr/llext/symbol.h>
|
||||
#include <zephyr/toolchain.h>
|
||||
#include <zephyr/sys/printk.h>
|
||||
#include <zephyr/ztest_assert.h>
|
||||
|
||||
static int number;
|
||||
EXPORT_SYMBOL(number);
|
||||
|
||||
static void preinit_fn_1(void)
|
||||
{
|
||||
number = 1;
|
||||
}
|
||||
|
||||
static void preinit_fn_2(void)
|
||||
{
|
||||
number <<= 4;
|
||||
number |= 2;
|
||||
}
|
||||
|
||||
static void init_fn(void)
|
||||
{
|
||||
number <<= 4;
|
||||
number |= 3;
|
||||
}
|
||||
|
||||
static void fini_fn(void)
|
||||
{
|
||||
number <<= 4;
|
||||
number |= 4;
|
||||
}
|
||||
|
||||
static const void *const preinit_fn_ptrs[] __used Z_GENERIC_SECTION(".preinit_array") = {
|
||||
preinit_fn_1,
|
||||
preinit_fn_2
|
||||
};
|
||||
static const void *const init_fn_ptrs[] __used Z_GENERIC_SECTION(".init_array") = {
|
||||
init_fn
|
||||
};
|
||||
static const void *const fini_fn_ptrs[] __used Z_GENERIC_SECTION(".fini_array") = {
|
||||
fini_fn
|
||||
};
|
||||
|
||||
void test_entry(void)
|
||||
{
|
||||
/* fini_fn() is not called yet, so we expect 0x123 here */
|
||||
const int expected = (((1 << 4) | 2) << 4) | 3;
|
||||
|
||||
zassert_equal(number, expected, "got 0x%x instead of 0x%x during test",
|
||||
number, expected);
|
||||
}
|
||||
EXPORT_SYMBOL(test_entry);
|
|
@ -67,16 +67,6 @@ struct llext_test {
|
|||
K_THREAD_STACK_DEFINE(llext_stack, 1024);
|
||||
struct k_thread llext_thread;
|
||||
|
||||
#ifdef CONFIG_USERSPACE
|
||||
void llext_entry(void *arg0, void *arg1, void *arg2)
|
||||
{
|
||||
void (*fn)(void) = arg0;
|
||||
|
||||
LOG_INF("calling fn %p from thread %p", fn, k_current_get());
|
||||
fn();
|
||||
}
|
||||
#endif /* CONFIG_USERSPACE */
|
||||
|
||||
|
||||
/* syscalls test */
|
||||
|
||||
|
@ -169,7 +159,8 @@ void load_call_unload(const struct llext_test *test_case)
|
|||
/* Should be runnable from newly created thread */
|
||||
k_thread_create(&llext_thread, llext_stack,
|
||||
K_THREAD_STACK_SIZEOF(llext_stack),
|
||||
&llext_entry, test_entry_fn, NULL, NULL,
|
||||
(k_thread_entry_t) &llext_bootstrap,
|
||||
ext, test_entry_fn, NULL,
|
||||
1, 0, K_FOREVER);
|
||||
|
||||
k_mem_domain_add_thread(&domain, &llext_thread);
|
||||
|
@ -192,7 +183,8 @@ void load_call_unload(const struct llext_test *test_case)
|
|||
if (!test_case->kernel_only) {
|
||||
k_thread_create(&llext_thread, llext_stack,
|
||||
K_THREAD_STACK_SIZEOF(llext_stack),
|
||||
&llext_entry, test_entry_fn, NULL, NULL,
|
||||
(k_thread_entry_t) &llext_bootstrap,
|
||||
ext, test_entry_fn, NULL,
|
||||
1, K_USER, K_FOREVER);
|
||||
|
||||
k_mem_domain_add_thread(&domain, &llext_thread);
|
||||
|
@ -210,12 +202,24 @@ void load_call_unload(const struct llext_test *test_case)
|
|||
}
|
||||
|
||||
#else /* CONFIG_USERSPACE */
|
||||
/* No userspace support: run the test only in supervisor mode, without
|
||||
* creating a new thread.
|
||||
*/
|
||||
if (test_case->test_setup) {
|
||||
test_case->test_setup(ext, NULL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LLEXT_TYPE_ELF_SHAREDLIB
|
||||
/* The ELF specification forbids shared libraries from defining init
|
||||
* entries, so calling llext_bootstrap here would be redundant. Use
|
||||
* this opportunity to test llext_call_fn, even though llext_bootstrap
|
||||
* would have behaved simlarly.
|
||||
*/
|
||||
zassert_ok(llext_call_fn(ext, "test_entry"),
|
||||
"test_entry call should succeed");
|
||||
#else /* !USERSPACE && !SHAREDLIB */
|
||||
llext_bootstrap(ext, test_entry_fn, NULL);
|
||||
#endif
|
||||
|
||||
if (test_case->test_cleanup) {
|
||||
test_case->test_cleanup(ext);
|
||||
|
@ -257,6 +261,29 @@ LLEXT_LOAD_UNLOAD(hello_world,
|
|||
.kernel_only = true
|
||||
)
|
||||
|
||||
#ifndef CONFIG_LLEXT_TYPE_ELF_SHAREDLIB
|
||||
static LLEXT_CONST uint8_t init_fini_ext[] ELF_ALIGN = {
|
||||
#include "init_fini.inc"
|
||||
};
|
||||
|
||||
static void init_fini_test_cleanup(struct llext *ext)
|
||||
{
|
||||
/* Make sure fini_fn() was called during teardown.
|
||||
* (see init_fini_ext.c for more details).
|
||||
*/
|
||||
const int *number = llext_find_sym(&ext->exp_tab, "number");
|
||||
const int expected = (((((1 << 4) | 2) << 4) | 3) << 4) | 4; /* 0x1234 */
|
||||
|
||||
zassert_not_null(number, "number should be an exported symbol");
|
||||
zassert_equal(*number, expected, "got 0x%x instead of 0x%x during cleanup",
|
||||
*number, expected);
|
||||
}
|
||||
|
||||
LLEXT_LOAD_UNLOAD(init_fini,
|
||||
.test_cleanup = init_fini_test_cleanup
|
||||
)
|
||||
#endif
|
||||
|
||||
static LLEXT_CONST uint8_t logging_ext[] ELF_ALIGN = {
|
||||
#include "logging.inc"
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue