From dfef264bed9d65504e88d0718fb72b7298b3dd43 Mon Sep 17 00:00:00 2001 From: Luca Burelli Date: Thu, 22 Aug 2024 15:55:42 +0200 Subject: [PATCH] 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 --- tests/subsys/llext/simple/CMakeLists.txt | 5 ++ tests/subsys/llext/simple/src/init_fini_ext.c | 71 +++++++++++++++++++ .../llext/simple/src/test_llext_simple.c | 51 +++++++++---- 3 files changed, 115 insertions(+), 12 deletions(-) create mode 100644 tests/subsys/llext/simple/src/init_fini_ext.c diff --git a/tests/subsys/llext/simple/CMakeLists.txt b/tests/subsys/llext/simple/CMakeLists.txt index 8db40dfb10e..b38f66c9a3a 100644 --- a/tests/subsys/llext/simple/CMakeLists.txt +++ b/tests/subsys/llext/simple/CMakeLists.txt @@ -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) diff --git a/tests/subsys/llext/simple/src/init_fini_ext.c b/tests/subsys/llext/simple/src/init_fini_ext.c new file mode 100644 index 00000000000..13cd4e741d7 --- /dev/null +++ b/tests/subsys/llext/simple/src/init_fini_ext.c @@ -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 +#include +#include +#include +#include + +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); diff --git a/tests/subsys/llext/simple/src/test_llext_simple.c b/tests/subsys/llext/simple/src/test_llext_simple.c index 4867e568591..3455806bed0 100644 --- a/tests/subsys/llext/simple/src/test_llext_simple.c +++ b/tests/subsys/llext/simple/src/test_llext_simple.c @@ -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" };