diff --git a/include/zephyr/llext/elf.h b/include/zephyr/llext/elf.h index b0477416a4338..ca9f498a49ac0 100644 --- a/include/zephyr/llext/elf.h +++ b/include/zephyr/llext/elf.h @@ -212,6 +212,8 @@ struct elf64_shdr { #define SHF_ALLOC 0x2 /**< Section is present in memory */ #define SHF_EXECINSTR 0x4 /**< Section contains executable instructions */ +#define SHF_BASIC_TYPE_MASK (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR) + /** * @brief Symbol table entry(32-bit) */ diff --git a/include/zephyr/llext/llext.h b/include/zephyr/llext/llext.h index 19fbe7a0b55da..9256f1db8a65d 100644 --- a/include/zephyr/llext/llext.h +++ b/include/zephyr/llext/llext.h @@ -62,6 +62,9 @@ enum llext_mem { struct llext_loader; /** @endcond */ +/* Maximim number of dependency LLEXTs */ +#define LLEXT_MAX_DEPENDENCIES 8 + /** * @brief Structure describing a linkable loadable extension * @@ -111,6 +114,9 @@ struct llext { /** Extension use counter, prevents unloading while in use */ unsigned int use_count; + + /** Array of extensions, whose symbols this extension accesses */ + struct llext *dependency[LLEXT_MAX_DEPENDENCIES]; }; /** diff --git a/include/zephyr/llext/symbol.h b/include/zephyr/llext/symbol.h index 412924ddd4607..330bb19d9de48 100644 --- a/include/zephyr/llext/symbol.h +++ b/include/zephyr/llext/symbol.h @@ -87,6 +87,26 @@ struct llext_symtable { }; +/** + * @brief Exports a symbol from an extension to the base image + * + * This macro can be used in extensions to add a symbol (function or object) + * to the extension's exported symbol table, so that it may be referenced by + * the base image. + * + * @param x Extension symbol to export to the base image + */ +#if defined(CONFIG_LLEXT) && defined(LL_EXTENSION_BUILD) +#define LL_EXTENSION_SYMBOL(x) \ + static const struct llext_const_symbol \ + Z_GENERIC_SECTION(".exported_sym") __used \ + x ## _sym = { \ + .name = STRINGIFY(x), .addr = (const void *)&x, \ + } +#else +#define LL_EXTENSION_SYMBOL(x) +#endif + /** * @brief Export a constant symbol to extensions * @@ -96,7 +116,9 @@ struct llext_symtable { * * @param x Symbol to export to extensions */ -#if defined(CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID) +#if defined(LL_EXTENSION_BUILD) +#define EXPORT_SYMBOL(x) LL_EXTENSION_SYMBOL(x) +#elif defined(CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID) #define EXPORT_SYMBOL(x) \ static const char Z_GENERIC_SECTION("llext_exports_strtab") __used \ x ## _sym_name[] = STRINGIFY(x); \ @@ -112,26 +134,6 @@ struct llext_symtable { #define EXPORT_SYMBOL(x) #endif -/** - * @brief Exports a symbol from an extension to the base image - * - * This macro can be used in extensions to add a symbol (function or object) - * to the extension's exported symbol table, so that it may be referenced by - * the base image. - * - * @param x Extension symbol to export to the base image - */ -#if defined(CONFIG_LLEXT) && defined(LL_EXTENSION_BUILD) -#define LL_EXTENSION_SYMBOL(x) \ - static const struct llext_const_symbol \ - Z_GENERIC_SECTION(".exported_sym") __used \ - x ## _sym = { \ - .name = STRINGIFY(x), .addr = (const void *)&x, \ - } -#else -#define LL_EXTENSION_SYMBOL(x) -#endif - /** * @} */ diff --git a/samples/subsys/llext/edk/ext1/src/main.c b/samples/subsys/llext/edk/ext1/src/main.c index d7f9c6aa71b3f..a2d98a937845f 100644 --- a/samples/subsys/llext/edk/ext1/src/main.c +++ b/samples/subsys/llext/edk/ext1/src/main.c @@ -31,4 +31,4 @@ int start(void) return 0; } -LL_EXTENSION_SYMBOL(start); +EXPORT_SYMBOL(start); diff --git a/samples/subsys/llext/edk/ext2/src/main.c b/samples/subsys/llext/edk/ext2/src/main.c index 38a054a5ccb19..f6fd7822f4de1 100644 --- a/samples/subsys/llext/edk/ext2/src/main.c +++ b/samples/subsys/llext/edk/ext2/src/main.c @@ -23,4 +23,4 @@ int start(void) return 0; } -LL_EXTENSION_SYMBOL(start); +EXPORT_SYMBOL(start); diff --git a/samples/subsys/llext/edk/ext3/src/main.c b/samples/subsys/llext/edk/ext3/src/main.c index 19aff8352d967..fcaf40186c4a5 100644 --- a/samples/subsys/llext/edk/ext3/src/main.c +++ b/samples/subsys/llext/edk/ext3/src/main.c @@ -62,4 +62,4 @@ int start(void) return 0; } -LL_EXTENSION_SYMBOL(start); +EXPORT_SYMBOL(start); diff --git a/samples/subsys/llext/edk/k-ext1/src/main.c b/samples/subsys/llext/edk/k-ext1/src/main.c index 34dedc6372820..121cc2568a88f 100644 --- a/samples/subsys/llext/edk/k-ext1/src/main.c +++ b/samples/subsys/llext/edk/k-ext1/src/main.c @@ -61,4 +61,4 @@ int start(void) return 0; } -LL_EXTENSION_SYMBOL(start); +EXPORT_SYMBOL(start); diff --git a/samples/subsys/llext/modules/src/hello_world_ext.c b/samples/subsys/llext/modules/src/hello_world_ext.c index 41220b027255e..b7251d6917d04 100644 --- a/samples/subsys/llext/modules/src/hello_world_ext.c +++ b/samples/subsys/llext/modules/src/hello_world_ext.c @@ -24,4 +24,4 @@ void hello_world(void) printk("Hello, world, from an llext!\n"); #endif } -LL_EXTENSION_SYMBOL(hello_world); +EXPORT_SYMBOL(hello_world); diff --git a/subsys/llext/llext.c b/subsys/llext/llext.c index 831667f7c96c7..8469e316b5c34 100644 --- a/subsys/llext/llext.c +++ b/subsys/llext/llext.c @@ -82,14 +82,13 @@ struct llext *llext_by_name(const char *name) int llext_iterate(int (*fn)(struct llext *ext, void *arg), void *arg) { sys_snode_t *node; - unsigned int i; int ret = 0; k_mutex_lock(&llext_lock, K_FOREVER); - for (node = sys_slist_peek_head(&_llext_list), i = 0; + for (node = sys_slist_peek_head(&_llext_list); node; - node = sys_slist_peek_next(node), i++) { + node = sys_slist_peek_next(node)) { struct llext *ext = CONTAINER_OF(node, struct llext, _llext_list); ret = fn(ext, arg); @@ -198,6 +197,8 @@ int llext_unload(struct llext **ext) /* FIXME: protect the global list */ sys_slist_find_and_remove(&_llext_list, &tmp->_llext_list); + llext_dependency_remove_all(tmp); + *ext = NULL; k_mutex_unlock(&llext_lock); diff --git a/subsys/llext/llext_link.c b/subsys/llext/llext_link.c index 4ee19f57583bd..959db4db28a03 100644 --- a/subsys/llext/llext_link.c +++ b/subsys/llext/llext_link.c @@ -52,6 +52,86 @@ static size_t llext_file_offset(struct llext_loader *ldr, size_t offset) return offset; } +/* + * We increment use-count every time a new dependent is added, and have to + * decrement it again, when one is removed. Ideally we should be able to add + * arbitrary numbers of dependencies, but using lists for this doesn't work, + * because multiple extensions can have common dependencies. Dynamically + * allocating dependency entries would be too wasteful. In this initial + * implementation we use an array of dependencies, if at some point we run out + * of array entries, we'll implement re-allocation. + * We add dependencies incrementally as we discover them, but we only ever + * expect them to be removed all at once, when their user is removed. So the + * dependency array is always "dense" - it cannot have NULL entries between + * valid ones. + */ +static int llext_dependency_add(struct llext *ext, struct llext *dependency) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ext->dependency); i++) { + if (ext->dependency[i] == dependency) { + return 0; + } + + if (!ext->dependency[i]) { + ext->dependency[i] = dependency; + dependency->use_count++; + + return 0; + } + } + + return -ENOENT; +} + +void llext_dependency_remove_all(struct llext *ext) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ext->dependency) && ext->dependency[i]; i++) { + /* + * The use-count of dependencies is tightly bound to dependent's + * life cycle, so it shouldn't underrun. + */ + ext->dependency[i]->use_count--; + __ASSERT(ext->dependency[i]->use_count, "LLEXT dependency use-count underrun!"); + /* No need to NULL-ify the pointer - ext is freed after this */ + } +} + +struct llext_extension_sym { + struct llext *ext; + const char *sym; + const void *addr; +}; + +static int llext_find_extension_sym_iterate(struct llext *ext, void *arg) +{ + struct llext_extension_sym *se = arg; + const void *addr = llext_find_sym(&ext->exp_tab, se->sym); + + if (addr) { + se->addr = addr; + se->ext = ext; + return 1; + } + + return 0; +} + +static const void *llext_find_extension_sym(const char *sym_name, struct llext **ext) +{ + struct llext_extension_sym se = {.sym = sym_name}; + + llext_iterate(llext_find_extension_sym_iterate, &se); + if (ext) { + *ext = se.ext; + } + + return se.addr; +} + static void llext_link_plt(struct llext_loader *ldr, struct llext *ext, elf_shdr_t *shdr, bool do_local, elf_shdr_t *tgt) { @@ -141,13 +221,25 @@ static void llext_link_plt(struct llext_loader *ldr, struct llext *ext, switch (stb) { case STB_GLOBAL: + /* First try the global symbol table */ link_addr = llext_find_sym(NULL, SYM_NAME_OR_SLID(name, sym_tbl.st_value)); if (!link_addr) { + /* Next try internal tables */ link_addr = llext_find_sym(&ext->sym_tab, name); } + if (!link_addr) { + /* Finally try any loaded tables */ + struct llext *dep; + + link_addr = llext_find_extension_sym(name, &dep); + if (link_addr) { + llext_dependency_add(ext, dep); + } + } + if (!link_addr) { LOG_WRN("PLT: cannot find idx %u name %s", j, name); continue; @@ -297,6 +389,16 @@ int llext_link(struct llext_loader *ldr, struct llext *ext, bool do_local) link_addr = (uintptr_t)llext_find_sym(NULL, SYM_NAME_OR_SLID(name, sym.st_value)); + if (link_addr == 0) { + /* Try loaded tables */ + struct llext *dep; + + link_addr = (uintptr_t)llext_find_extension_sym(name, &dep); + if (link_addr) { + llext_dependency_add(ext, dep); + } + } + if (link_addr == 0) { LOG_ERR("Undefined symbol with no entry in " "symbol table %s, offset %zd, link section %d", diff --git a/subsys/llext/llext_load.c b/subsys/llext/llext_load.c index de3cdd6402b96..fcc26210ebcfc 100644 --- a/subsys/llext/llext_load.c +++ b/subsys/llext/llext_load.c @@ -253,8 +253,10 @@ static int llext_map_sections(struct llext_loader *ldr, struct llext *ext) memcpy(region, shdr, sizeof(*region)); } else { /* Make sure this section is compatible with the region */ - if (shdr->sh_flags != region->sh_flags) { - LOG_ERR("Unsupported section flags for %s (region %d)", + if ((shdr->sh_flags & SHF_BASIC_TYPE_MASK) != + (region->sh_flags & SHF_BASIC_TYPE_MASK)) { + LOG_ERR("Unsupported section flags %#x / %#x for %s (region %d)", + (uint32_t)shdr->sh_flags, (uint32_t)region->sh_flags, name, mem_idx); return -ENOEXEC; } diff --git a/subsys/llext/llext_priv.h b/subsys/llext/llext_priv.h index be11877395709..da2484dc3fe21 100644 --- a/subsys/llext/llext_priv.h +++ b/subsys/llext/llext_priv.h @@ -74,5 +74,6 @@ static inline const void *llext_loaded_sect_ptr(struct llext_loader *ldr, struct */ int llext_link(struct llext_loader *ldr, struct llext *ext, bool do_local); +void llext_dependency_remove_all(struct llext *ext); #endif /* ZEPHYR_SUBSYS_LLEXT_PRIV_H_ */ diff --git a/subsys/logging/log_msg.c b/subsys/logging/log_msg.c index c45eb2201daf1..e6534998c5071 100644 --- a/subsys/logging/log_msg.c +++ b/subsys/logging/log_msg.c @@ -397,6 +397,7 @@ void z_log_msg_runtime_vcreate(uint8_t domain_id, const void *source, z_log_msg_finalize(msg, source, desc, data); } } +EXPORT_SYMBOL(z_log_msg_runtime_vcreate); int16_t log_msg_get_source_id(struct log_msg *msg) { diff --git a/tests/misc/llext-edk/extension/src/main.c b/tests/misc/llext-edk/extension/src/main.c index 59038de2cdf05..235504f5f8c37 100644 --- a/tests/misc/llext-edk/extension/src/main.c +++ b/tests/misc/llext-edk/extension/src/main.c @@ -14,4 +14,4 @@ int start(int bar) printk("foo(%d) is %d\n", bar, foo(bar)); return 0; } -LL_EXTENSION_SYMBOL(start); +EXPORT_SYMBOL(start); diff --git a/tests/subsys/llext/simple/src/find_section_ext.c b/tests/subsys/llext/simple/src/find_section_ext.c index fcbe5c92a4d8b..5d947914ef402 100644 --- a/tests/subsys/llext/simple/src/find_section_ext.c +++ b/tests/subsys/llext/simple/src/find_section_ext.c @@ -22,4 +22,4 @@ void test_entry(void) /* unused */ } -LL_EXTENSION_SYMBOL(number); +EXPORT_SYMBOL(number); diff --git a/tests/subsys/llext/simple/src/hello_world_ext.c b/tests/subsys/llext/simple/src/hello_world_ext.c index 2a8bdf56c95eb..4cd2713cd9306 100644 --- a/tests/subsys/llext/simple/src/hello_world_ext.c +++ b/tests/subsys/llext/simple/src/hello_world_ext.c @@ -24,4 +24,4 @@ void test_entry(void) printk("A number is %u\n", number); zassert_equal(number, 42); } -LL_EXTENSION_SYMBOL(test_entry); +EXPORT_SYMBOL(test_entry); diff --git a/tests/subsys/llext/simple/src/logging_ext.c b/tests/subsys/llext/simple/src/logging_ext.c index 94298ce2f150d..75a6428ebcf81 100644 --- a/tests/subsys/llext/simple/src/logging_ext.c +++ b/tests/subsys/llext/simple/src/logging_ext.c @@ -23,4 +23,4 @@ void test_entry(void) LOG_INF("hello world"); LOG_INF("A number is %" PRIu32, number); } -LL_EXTENSION_SYMBOL(test_entry); +EXPORT_SYMBOL(test_entry); diff --git a/tests/subsys/llext/simple/src/movwmovt_ext.c b/tests/subsys/llext/simple/src/movwmovt_ext.c index 21633228778b1..aecaf029ddc97 100644 --- a/tests/subsys/llext/simple/src/movwmovt_ext.c +++ b/tests/subsys/llext/simple/src/movwmovt_ext.c @@ -32,4 +32,4 @@ void test_entry(void) __asm volatile ("blx r0"); zassert_equal(test_var, 1, "mov.w and mov.t test failed"); } -LL_EXTENSION_SYMBOL(test_entry); +EXPORT_SYMBOL(test_entry); diff --git a/tests/subsys/llext/simple/src/multi_file_ext1.c b/tests/subsys/llext/simple/src/multi_file_ext1.c index bdc8bccaaa128..8aa9092ee2801 100644 --- a/tests/subsys/llext/simple/src/multi_file_ext1.c +++ b/tests/subsys/llext/simple/src/multi_file_ext1.c @@ -50,4 +50,4 @@ void test_entry(void) run_id += 1; } -LL_EXTENSION_SYMBOL(test_entry); +EXPORT_SYMBOL(test_entry); diff --git a/tests/subsys/llext/simple/src/object_ext.c b/tests/subsys/llext/simple/src/object_ext.c index 7efe1a916d64e..1a4ea53158efd 100644 --- a/tests/subsys/llext/simple/src/object_ext.c +++ b/tests/subsys/llext/simple/src/object_ext.c @@ -45,4 +45,4 @@ void test_entry(void) run_id += 1; } -LL_EXTENSION_SYMBOL(test_entry); +EXPORT_SYMBOL(test_entry); diff --git a/tests/subsys/llext/simple/src/pre_located_ext.c b/tests/subsys/llext/simple/src/pre_located_ext.c index 1308c8d662fc6..99d4b77ca5772 100644 --- a/tests/subsys/llext/simple/src/pre_located_ext.c +++ b/tests/subsys/llext/simple/src/pre_located_ext.c @@ -16,4 +16,4 @@ void test_entry(void) { /* This function is never called */ } -LL_EXTENSION_SYMBOL(test_entry); +EXPORT_SYMBOL(test_entry); diff --git a/tests/subsys/llext/simple/src/relative_jump_ext.c b/tests/subsys/llext/simple/src/relative_jump_ext.c index e7f67469b834e..8153d84903ca8 100644 --- a/tests/subsys/llext/simple/src/relative_jump_ext.c +++ b/tests/subsys/llext/simple/src/relative_jump_ext.c @@ -62,4 +62,4 @@ void test_entry(void) printk("exit\n"); zassert_equal(test_var, 1, "relative jump test failed"); } -LL_EXTENSION_SYMBOL(test_entry); +EXPORT_SYMBOL(test_entry); diff --git a/tests/subsys/llext/simple/src/syscalls_ext.c b/tests/subsys/llext/simple/src/syscalls_ext.c index f8f073fb6f99f..d8e6449ee2ff3 100644 --- a/tests/subsys/llext/simple/src/syscalls_ext.c +++ b/tests/subsys/llext/simple/src/syscalls_ext.c @@ -23,4 +23,4 @@ void test_entry(void) input, input + 1, output); zassert_equal(output, input + 1); } -LL_EXTENSION_SYMBOL(test_entry); +EXPORT_SYMBOL(test_entry); diff --git a/tests/subsys/llext/simple/src/threads_kernel_objects_ext.c b/tests/subsys/llext/simple/src/threads_kernel_objects_ext.c index fd6b9673c9578..ab9763b912151 100644 --- a/tests/subsys/llext/simple/src/threads_kernel_objects_ext.c +++ b/tests/subsys/llext/simple/src/threads_kernel_objects_ext.c @@ -42,4 +42,4 @@ void test_entry(void) k_thread_join(&my_thread, K_FOREVER); printk("Test thread joined\n"); } -LL_EXTENSION_SYMBOL(test_entry); +EXPORT_SYMBOL(test_entry);