-
Notifications
You must be signed in to change notification settings - Fork 8.2k
llext: add support for init arrays - revised #77641
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -273,3 +273,66 @@ int llext_call_fn(struct llext *ext, const char *sym_name) | |
|
|
||
| return 0; | ||
| } | ||
|
|
||
| static int call_fn_table(struct llext *ext, bool is_init) | ||
| { | ||
| ssize_t ret; | ||
|
|
||
| ret = llext_get_fn_table(ext, is_init, NULL, 0); | ||
| if (ret < 0) { | ||
| LOG_ERR("Failed to get table size: %d", (int)ret); | ||
| return ret; | ||
| } | ||
|
|
||
| typedef void (*elf_void_fn_t)(void); | ||
|
|
||
| int fn_count = ret / sizeof(elf_void_fn_t); | ||
| elf_void_fn_t fn_table[fn_count]; | ||
|
|
||
| ret = llext_get_fn_table(ext, is_init, &fn_table, sizeof(fn_table)); | ||
| if (ret < 0) { | ||
| LOG_ERR("Failed to get function table: %d", (int)ret); | ||
| return ret; | ||
| } | ||
|
|
||
| for (int i = 0; i < fn_count; i++) { | ||
| LOG_DBG("calling %s function %p()", | ||
| is_init ? "bringup" : "teardown", fn_table[i]); | ||
| fn_table[i](); | ||
| } | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| inline int llext_bringup(struct llext *ext) | ||
| { | ||
| return call_fn_table(ext, true); | ||
| } | ||
|
|
||
| inline int llext_teardown(struct llext *ext) | ||
| { | ||
| return call_fn_table(ext, false); | ||
| } | ||
|
|
||
| void llext_bootstrap(struct llext *ext, llext_entry_fn_t entry_fn, void *user_data) | ||
| { | ||
| int ret; | ||
|
|
||
| /* Call initialization functions */ | ||
| ret = llext_bringup(ext); | ||
| if (ret < 0) { | ||
| LOG_ERR("Failed to call init functions: %d", ret); | ||
| return; | ||
| } | ||
|
|
||
| /* Start extension main function */ | ||
| LOG_DBG("calling entry function %p(%p)", entry_fn, user_data); | ||
| entry_fn(user_data); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you wrote "if desired" in the commit description. Should you check for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, commit message is a leftover from the previous iteration, when |
||
|
|
||
| /* Call de-initialization functions */ | ||
| ret = llext_teardown(ext); | ||
| if (ret < 0) { | ||
| LOG_ERR("Failed to call de-init functions: %d", ret); | ||
| return; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| /* | ||
| * Copyright (c) 2024 Arduino SA | ||
| * | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| #include <string.h> | ||
| #include <zephyr/llext/llext.h> | ||
| #include <zephyr/llext/loader.h> | ||
| #include <zephyr/internal/syscall_handler.h> | ||
| #include <zephyr/kernel.h> | ||
|
|
||
| #include <zephyr/logging/log.h> | ||
| LOG_MODULE_DECLARE(llext, CONFIG_LLEXT_LOG_LEVEL); | ||
|
|
||
| #include "llext_priv.h" | ||
|
|
||
| ssize_t z_impl_llext_get_fn_table(struct llext *ext, bool is_init, void *buf, size_t buf_size) | ||
| { | ||
| size_t table_size; | ||
|
|
||
| if (!ext) { | ||
| return -EINVAL; | ||
| } | ||
|
|
||
| if (is_init) { | ||
| table_size = ext->mem_size[LLEXT_MEM_PREINIT] + | ||
| ext->mem_size[LLEXT_MEM_INIT]; | ||
| } else { | ||
| table_size = ext->mem_size[LLEXT_MEM_FINI]; | ||
| } | ||
|
|
||
| if (buf) { | ||
| char *byte_ptr = buf; | ||
|
|
||
| if (buf_size < table_size) { | ||
| return -ENOMEM; | ||
| } | ||
|
|
||
| if (is_init) { | ||
| /* setup functions from preinit_array and init_array */ | ||
| memcpy(byte_ptr, | ||
| ext->mem[LLEXT_MEM_PREINIT], ext->mem_size[LLEXT_MEM_PREINIT]); | ||
| memcpy(byte_ptr + ext->mem_size[LLEXT_MEM_PREINIT], | ||
| ext->mem[LLEXT_MEM_INIT], ext->mem_size[LLEXT_MEM_INIT]); | ||
| } else { | ||
| /* cleanup functions from fini_array */ | ||
| memcpy(byte_ptr, | ||
| ext->mem[LLEXT_MEM_FINI], ext->mem_size[LLEXT_MEM_FINI]); | ||
| } | ||
|
|
||
| /* Sanity check: pointers in this table must map inside the | ||
| * text region of the extension. If this fails, something went | ||
| * wrong during the relocation process. | ||
| * Using "char *" for these simplifies pointer arithmetic. | ||
| */ | ||
| const char *text_start = ext->mem[LLEXT_MEM_TEXT]; | ||
| const char *text_end = text_start + ext->mem_size[LLEXT_MEM_TEXT]; | ||
| const char **fn_ptrs = buf; | ||
|
|
||
| for (int i = 0; i < table_size / sizeof(void *); i++) { | ||
| if (fn_ptrs[i] < text_start || fn_ptrs[i] >= text_end) { | ||
| LOG_ERR("init function %i (%p) outside text region", | ||
| i, fn_ptrs[i]); | ||
| return -EFAULT; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return table_size; | ||
| } | ||
|
|
||
| #ifdef CONFIG_USERSPACE | ||
|
|
||
| static int ext_is_valid(struct llext *ext, void *arg) | ||
| { | ||
| return ext == arg; | ||
| } | ||
|
|
||
| static inline ssize_t z_vrfy_llext_get_fn_table(struct llext *ext, bool is_init, | ||
| void *buf, size_t size) | ||
| { | ||
| /* Test that ext matches a loaded extension */ | ||
| K_OOPS(llext_iterate(ext_is_valid, ext) == 0); | ||
|
|
||
| if (buf) { | ||
| /* Test that buf is a valid user-accessible pointer */ | ||
| K_OOPS(K_SYSCALL_MEMORY_WRITE(buf, size)); | ||
| } | ||
|
|
||
| return z_impl_llext_get_fn_table(ext, is_init, buf, size); | ||
| } | ||
| #include <zephyr/syscalls/llext_get_fn_table_mrsh.c> | ||
|
|
||
| #endif /* CONFIG_USERSPACE */ |
Uh oh!
There was an error while loading. Please reload this page.