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)
|
list(APPEND ext_names find_section)
|
||||||
endif()
|
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'
|
# generate extension targets foreach extension given by 'ext_names'
|
||||||
foreach(ext_name ${ext_names})
|
foreach(ext_name ${ext_names})
|
||||||
set(ext_src ${PROJECT_SOURCE_DIR}/src/${ext_name}_ext.c)
|
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);
|
K_THREAD_STACK_DEFINE(llext_stack, 1024);
|
||||||
struct k_thread llext_thread;
|
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 */
|
/* syscalls test */
|
||||||
|
|
||||||
|
@ -169,7 +159,8 @@ void load_call_unload(const struct llext_test *test_case)
|
||||||
/* Should be runnable from newly created thread */
|
/* Should be runnable from newly created thread */
|
||||||
k_thread_create(&llext_thread, llext_stack,
|
k_thread_create(&llext_thread, llext_stack,
|
||||||
K_THREAD_STACK_SIZEOF(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);
|
1, 0, K_FOREVER);
|
||||||
|
|
||||||
k_mem_domain_add_thread(&domain, &llext_thread);
|
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) {
|
if (!test_case->kernel_only) {
|
||||||
k_thread_create(&llext_thread, llext_stack,
|
k_thread_create(&llext_thread, llext_stack,
|
||||||
K_THREAD_STACK_SIZEOF(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);
|
1, K_USER, K_FOREVER);
|
||||||
|
|
||||||
k_mem_domain_add_thread(&domain, &llext_thread);
|
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 */
|
#else /* CONFIG_USERSPACE */
|
||||||
|
/* No userspace support: run the test only in supervisor mode, without
|
||||||
|
* creating a new thread.
|
||||||
|
*/
|
||||||
if (test_case->test_setup) {
|
if (test_case->test_setup) {
|
||||||
test_case->test_setup(ext, NULL);
|
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"),
|
zassert_ok(llext_call_fn(ext, "test_entry"),
|
||||||
"test_entry call should succeed");
|
"test_entry call should succeed");
|
||||||
|
#else /* !USERSPACE && !SHAREDLIB */
|
||||||
|
llext_bootstrap(ext, test_entry_fn, NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (test_case->test_cleanup) {
|
if (test_case->test_cleanup) {
|
||||||
test_case->test_cleanup(ext);
|
test_case->test_cleanup(ext);
|
||||||
|
@ -257,6 +261,29 @@ LLEXT_LOAD_UNLOAD(hello_world,
|
||||||
.kernel_only = true
|
.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 = {
|
static LLEXT_CONST uint8_t logging_ext[] ELF_ALIGN = {
|
||||||
#include "logging.inc"
|
#include "logging.inc"
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue