From 62b19ef65c0be6186ed9f07343c8242abf651e6c Mon Sep 17 00:00:00 2001 From: Ederson de Souza Date: Thu, 21 Mar 2024 15:51:15 -0700 Subject: [PATCH] syscalls: llext: Export z_impl symbols so they are available to kernel commit 67bb6db3f8e5 ("syscall: Export all emitted syscalls, enabling them for extensions") exports all emitted syscalls, however, it does that only for the `z_mrsh` symbols, effectively only available for userspace. If an extension running at kernel level tries to use a syscall, it will fail to load. This patch fixes that by exposing the `z_impl` symbols instead. However, this is not as straightforward as the `z_mrsh` ones. As, in their signatures, they can basically contain any type, it's not just a matter of emitting `EXPORT_SYMBOL(z_impl_)`, as the compiler will complain about the undefined types. Here, there are a few approaches. One of them is to have the `EXPORT_SYMBOL` being generated on the same files where the syscall is implemented - injecting it there would allow it to access all known symbols. But changing a lot of files is undesirable, and it was one of the nice points of first patch. Another one would be to reconstruct - or simply use the absolute path - for the includes where the syscalls are defined. Reconstruct the paths seems fragile and I'm not sure using absolute paths is portable. Finally, the approach used in this patch is to declare, on a different generated file, all `z_impl_` symbols as `void *` - after all, only the address (and the name) to the function is relevant to EXPORT_SYMBOL. By living in an compilation unit that doesn't include any header which would expose any of the syscalls, there shouldn't be any conflicts. And to account for the possibility that a syscall is not compiled - due being configured out via Kconfig - all those symbols are also weak aliases to a pointer to NULL. This file is then included in `llext_export.c` (which should naturally not include any conflicting header). Signed-off-by: Ederson de Souza --- CMakeLists.txt | 1 + scripts/build/gen_syscalls.py | 35 ++++++++++++++++++++++++++++------- subsys/llext/llext_export.c | 2 ++ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d8f2801d011633..2d802277a68ffe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -759,6 +759,7 @@ add_custom_command(OUTPUT include/generated/syscall_dispatch.c ${syscall_list_h} --json-file ${syscalls_json} # Read this file --base-output include/generated/syscalls # Write to this dir --syscall-dispatch include/generated/syscall_dispatch.c # Write this file + --syscall-export-llext include/generated/syscall_export_llext.c --syscall-list ${syscall_list_h} $<$:--gen-mrsh-files> ${SYSCALL_LONG_REGISTERS_ARG} diff --git a/scripts/build/gen_syscalls.py b/scripts/build/gen_syscalls.py index 3741707f8033ff..8755f3c649aacc 100755 --- a/scripts/build/gen_syscalls.py +++ b/scripts/build/gen_syscalls.py @@ -62,8 +62,6 @@ const _k_syscall_handler_t _k_syscall_table[K_SYSCALL_LIMIT] = { \t%s }; -/* Export syscalls for extensions */ -%s """ list_template = """/* auto-generated by gen_syscalls.py, don't edit */ @@ -159,6 +157,20 @@ #endif """ + +exported_template = """ +/* Export syscalls for extensions */ +static void * const no_handler = NULL; + +/* Weak references, if something is not found by the linker, it will be NULL + * and simply fail during extension load + */ +%s + +/* Exported symbols */ +%s +""" + typename_regex = re.compile(r'(.*?)([A-Za-z0-9_]+)$') @@ -410,6 +422,8 @@ def parse_args(): help="Indicates we are on system with 64-bit registers") parser.add_argument("--gen-mrsh-files", action="store_true", help="Generate marshalling files (*_mrsh.c)") + parser.add_argument("-e", "--syscall-export-llext", + help="output C system call export for extensions") args = parser.parse_args() @@ -431,6 +445,7 @@ def main(): table_entries = [] handlers = [] emit_list = [] + exported = [] for match_group, fn, to_emit in syscalls: handler, inv, mrsh, sys_id, entry = analyze_fn(match_group, fn) @@ -445,6 +460,7 @@ def main(): ids_emit.append(sys_id) table_entries.append(entry) emit_list.append(handler) + exported.append(handler.replace("z_mrsh_", "z_impl_")) else: ids_not_emit.append(sys_id) @@ -464,12 +480,17 @@ def main(): weak_defines += "\n".join(["extern uintptr_t %s(uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5, uintptr_t arg6, void *ssf);" % s for s in noweak]) - # Export symbols for emitted syscalls - exported_symbols = "\n".join("EXPORT_SYMBOL(%s);" % e for e in emit_list) - fp.write(table_template % (weak_defines, - ",\n\t".join(table_entries), - exported_symbols)) + ",\n\t".join(table_entries))) + + 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;" + % e for e in exported) + exported_symbols = "\n".join("EXPORT_SYMBOL(%s);" + % e for e in exported) + fp.write(exported_template % (weak_refs, exported_symbols)) # Listing header emitted to stdout ids_emit.sort() diff --git a/subsys/llext/llext_export.c b/subsys/llext/llext_export.c index 0ec7fe4ac0a0a6..e5b5aa441ab00b 100644 --- a/subsys/llext/llext_export.c +++ b/subsys/llext/llext_export.c @@ -15,3 +15,5 @@ EXPORT_SYMBOL(strncmp); EXPORT_SYMBOL(memcmp); EXPORT_SYMBOL(memcpy); EXPORT_SYMBOL(memset); + +#include