Skip to content

Commit ad53931

Browse files
committed
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 <[email protected]>
1 parent 3d4bc03 commit ad53931

File tree

3 files changed

+115
-12
lines changed

3 files changed

+115
-12
lines changed

tests/subsys/llext/simple/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ if (CONFIG_LLEXT_STORAGE_WRITABLE)
3131
list(APPEND ext_names find_section)
3232
endif()
3333

34+
if (NOT CONFIG_LLEXT_TYPE_ELF_SHAREDLIB)
35+
# ELF shared libraries do not support init sections
36+
list(APPEND ext_names init_fini)
37+
endif()
38+
3439
# generate extension targets foreach extension given by 'ext_names'
3540
foreach(ext_name ${ext_names})
3641
set(ext_src ${PROJECT_SOURCE_DIR}/src/${ext_name}_ext.c)
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright (c) 2024 Arduino SA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/*
8+
* This test checks for proper support of ELF init arrays. This processing is
9+
* performed by llext_bootstrap(), which gets the array of function pointers
10+
* from LLEXT via the llext_get_fn_table() syscall.
11+
*
12+
* Each function in this test shifts the number left by 4 bits and sets the
13+
* lower 4 bits to a specific value. The proper init sequence (preinit_fn_1,
14+
* preinit_fn_2, init_fn) would leave number set to 0x123; the termination
15+
* function will further shift the number to 0x1234. If a different result is
16+
* detected, then either not all routines were executed, or their order was not
17+
* correct.
18+
*/
19+
20+
#include <stdint.h>
21+
#include <zephyr/llext/symbol.h>
22+
#include <zephyr/toolchain.h>
23+
#include <zephyr/sys/printk.h>
24+
#include <zephyr/ztest_assert.h>
25+
26+
static int number;
27+
EXPORT_SYMBOL(number);
28+
29+
static void preinit_fn_1(void)
30+
{
31+
number = 1;
32+
}
33+
34+
static void preinit_fn_2(void)
35+
{
36+
number <<= 4;
37+
number |= 2;
38+
}
39+
40+
static void init_fn(void)
41+
{
42+
number <<= 4;
43+
number |= 3;
44+
}
45+
46+
static void fini_fn(void)
47+
{
48+
number <<= 4;
49+
number |= 4;
50+
}
51+
52+
static const void *const preinit_fn_ptrs[] __used Z_GENERIC_SECTION(".preinit_array") = {
53+
preinit_fn_1,
54+
preinit_fn_2
55+
};
56+
static const void *const init_fn_ptrs[] __used Z_GENERIC_SECTION(".init_array") = {
57+
init_fn
58+
};
59+
static const void *const fini_fn_ptrs[] __used Z_GENERIC_SECTION(".fini_array") = {
60+
fini_fn
61+
};
62+
63+
void test_entry(void)
64+
{
65+
/* fini_fn() is not called yet, so we expect 0x123 here */
66+
const int expected = (((1 << 4) | 2) << 4) | 3;
67+
68+
zassert_equal(number, expected, "got 0x%x instead of 0x%x during test",
69+
number, expected);
70+
}
71+
EXPORT_SYMBOL(test_entry);

tests/subsys/llext/simple/src/test_llext_simple.c

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,6 @@ struct llext_test {
6161
K_THREAD_STACK_DEFINE(llext_stack, 1024);
6262
struct k_thread llext_thread;
6363

64-
#ifdef CONFIG_USERSPACE
65-
void llext_entry(void *arg0, void *arg1, void *arg2)
66-
{
67-
void (*fn)(void) = arg0;
68-
69-
LOG_INF("calling fn %p from thread %p", fn, k_current_get());
70-
fn();
71-
}
72-
#endif /* CONFIG_USERSPACE */
73-
7464

7565
/* syscalls test */
7666

@@ -163,7 +153,8 @@ void load_call_unload(const struct llext_test *test_case)
163153
/* Should be runnable from newly created thread */
164154
k_thread_create(&llext_thread, llext_stack,
165155
K_THREAD_STACK_SIZEOF(llext_stack),
166-
&llext_entry, test_entry_fn, NULL, NULL,
156+
(k_thread_entry_t) &llext_bootstrap,
157+
ext, test_entry_fn, NULL,
167158
1, 0, K_FOREVER);
168159

169160
k_mem_domain_add_thread(&domain, &llext_thread);
@@ -186,7 +177,8 @@ void load_call_unload(const struct llext_test *test_case)
186177
if (!test_case->kernel_only) {
187178
k_thread_create(&llext_thread, llext_stack,
188179
K_THREAD_STACK_SIZEOF(llext_stack),
189-
&llext_entry, test_entry_fn, NULL, NULL,
180+
(k_thread_entry_t) &llext_bootstrap,
181+
ext, test_entry_fn, NULL,
190182
1, K_USER, K_FOREVER);
191183

192184
k_mem_domain_add_thread(&domain, &llext_thread);
@@ -204,12 +196,24 @@ void load_call_unload(const struct llext_test *test_case)
204196
}
205197

206198
#else /* CONFIG_USERSPACE */
199+
/* No userspace support: run the test only in supervisor mode, without
200+
* creating a new thread.
201+
*/
207202
if (test_case->test_setup) {
208203
test_case->test_setup(ext, NULL);
209204
}
210205

206+
#ifdef CONFIG_LLEXT_TYPE_ELF_SHAREDLIB
207+
/* The ELF specification forbids shared libraries from defining init
208+
* entries, so calling llext_bootstrap here would be redundant. Use
209+
* this opportunity to test llext_call_fn, even though llext_bootstrap
210+
* would have behaved simlarly.
211+
*/
211212
zassert_ok(llext_call_fn(ext, "test_entry"),
212213
"test_entry call should succeed");
214+
#else /* !USERSPACE && !SHAREDLIB */
215+
llext_bootstrap(ext, test_entry_fn, NULL);
216+
#endif
213217

214218
if (test_case->test_cleanup) {
215219
test_case->test_cleanup(ext);
@@ -251,6 +255,29 @@ LLEXT_LOAD_UNLOAD(hello_world,
251255
.kernel_only = true
252256
)
253257

258+
#ifndef CONFIG_LLEXT_TYPE_ELF_SHAREDLIB
259+
static LLEXT_CONST uint8_t init_fini_ext[] ELF_ALIGN = {
260+
#include "init_fini.inc"
261+
};
262+
263+
static void init_fini_test_cleanup(struct llext *ext)
264+
{
265+
/* Make sure fini_fn() was called during teardown.
266+
* (see init_fini_ext.c for more details).
267+
*/
268+
const int *number = llext_find_sym(&ext->exp_tab, "number");
269+
const int expected = (((((1 << 4) | 2) << 4) | 3) << 4) | 4; /* 0x1234 */
270+
271+
zassert_not_null(number, "number should be an exported symbol");
272+
zassert_equal(*number, expected, "got 0x%x instead of 0x%x during cleanup",
273+
*number, expected);
274+
}
275+
276+
LLEXT_LOAD_UNLOAD(init_fini,
277+
.test_cleanup = init_fini_test_cleanup
278+
)
279+
#endif
280+
254281
static LLEXT_CONST uint8_t logging_ext[] ELF_ALIGN = {
255282
#include "logging.inc"
256283
};

0 commit comments

Comments
 (0)