llext: fix handling of unimplemented syscalls
When building an LLEXT-enabled kernel, 62b19ef65c
added weak aliases
of all syscall implementation functions to a pointer to NULL, with the
assumption that LLEXT would check the required symbols at link time and
fail if any of them were found.
This check, however, is ineffective in the current implementation: the
actual address that is exported is the rather normal-looking location of
the variable containing the NULL pointer. This defeats the NULL symbol
validity checks in llext_link.c and causes the extension to crash at
runtime by jumping to a location containing a few zeroes in read-only
data memory.
This commit makes sure the alias target is actually placed at address 0
using the llext-sections.ld linker fragment, so that undefined syscall
implementations are exported as NULLs and as such properly flagged at
link time.
The test for this functionality is also updated to reflect the change.
Signed-off-by: Luca Burelli <l.burelli@arduino.cc>
This commit is contained in:
parent
e2e96acebf
commit
11c350e2e6
|
@ -1,5 +1,17 @@
|
|||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/*
|
||||
* Map the no_syscall_impl symbol in llext_export_syscalls.c to
|
||||
* absolute address 0 so other weak symbols are exported as NULL.
|
||||
* This section is used for mapping that symbol only and is not
|
||||
* to be included in the final binary.
|
||||
*/
|
||||
|
||||
SECTION_PROLOGUE(llext_no_syscall_impl, 0 (COPY), )
|
||||
{
|
||||
*(llext_no_syscall_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
* Special section used by LLEXT if CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID
|
||||
* is enabled. Declare this section to prevent it from being considered orphan.
|
||||
|
|
|
@ -159,11 +159,16 @@ syscall_tracer_void_template = """
|
|||
|
||||
|
||||
exported_template = """
|
||||
/* Export syscalls for extensions */
|
||||
static void * const no_handler = NULL;
|
||||
/*
|
||||
* This symbol is placed at address 0 by llext-sections.ld. Its value and
|
||||
* type is not important, we are only interested in its location
|
||||
*/
|
||||
static void * const no_syscall_impl Z_GENERIC_SECTION(llext_no_syscall_impl);
|
||||
|
||||
/* Weak references, if something is not found by the linker, it will be NULL
|
||||
* and simply fail during extension load
|
||||
/*
|
||||
* Weak references to all syscall implementations. Those not found by the
|
||||
* linker outside this file will be exported as NULL and simply fail when
|
||||
* an extension requiring them is loaded.
|
||||
*/
|
||||
%s
|
||||
|
||||
|
@ -495,7 +500,7 @@ def main():
|
|||
if args.syscall_export_llext:
|
||||
with open(args.syscall_export_llext, "w") as fp:
|
||||
# Export symbols for emitted syscalls
|
||||
weak_refs = "\n".join("extern __weak ALIAS_OF(no_handler) void * const %s;"
|
||||
weak_refs = "\n".join("extern __weak ALIAS_OF(no_syscall_impl) void * const %s;"
|
||||
% e for e in exported)
|
||||
exported_symbols = "\n".join("EXPORT_SYMBOL(%s);"
|
||||
% e for e in exported)
|
||||
|
|
|
@ -496,17 +496,16 @@ ZTEST(llext, test_printk_exported)
|
|||
}
|
||||
|
||||
/*
|
||||
* Ensure ext_syscall_fail is exported - as it is picked up by the syscall
|
||||
* build machinery - but points to NULL as it is not implemented.
|
||||
* The syscalls test above verifies that custom syscalls defined by extensions
|
||||
* are properly exported. Since `ext_syscalls.h` declares ext_syscall_fail, we
|
||||
* know it is picked up by the syscall build machinery, but the implementation
|
||||
* for it is missing. Make sure the exported symbol for it is NULL.
|
||||
*/
|
||||
ZTEST(llext, test_ext_syscall_fail)
|
||||
{
|
||||
const void * const esf_fn = LLEXT_FIND_BUILTIN_SYM(z_impl_ext_syscall_fail);
|
||||
|
||||
zassert_not_null(esf_fn, "est_fn should not be NULL");
|
||||
|
||||
zassert_is_null(*(uintptr_t **)esf_fn, NULL,
|
||||
"ext_syscall_fail should be NULL");
|
||||
zassert_is_null(esf_fn, "est_fn should be NULL");
|
||||
}
|
||||
|
||||
ZTEST_SUITE(llext, NULL, NULL, NULL, NULL, NULL);
|
||||
|
|
Loading…
Reference in New Issue