diff --git a/include/zephyr/llext/llext.h b/include/zephyr/llext/llext.h index 19fbe7a0b55dabf..0341d78d20bf05c 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,8 @@ struct llext { /** Extension use counter, prevents unloading while in use */ unsigned int use_count; + + struct llext *dependency[LLEXT_MAX_DEPENDENCIES]; }; /** diff --git a/subsys/llext/llext.c b/subsys/llext/llext.c index c61b5006947baca..8469e316b5c3440 100644 --- a/subsys/llext/llext.c +++ b/subsys/llext/llext.c @@ -197,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 6c5373d4916753a..9351e4f868f242f 100644 --- a/subsys/llext/llext_link.c +++ b/subsys/llext/llext_link.c @@ -52,7 +52,55 @@ static size_t llext_file_offset(struct llext_loader *ldr, size_t offset) return offset; } +/* + * We increment use-cound 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; }; @@ -64,17 +112,21 @@ static int llext_find_extension_sym_iterate(struct llext *ext, void *arg) if (addr) { se->addr = addr; + se->ext = ext; return 1; } return 0; } -static const void *llext_find_extension_sym(const char *sym_name) +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; } @@ -168,15 +220,22 @@ 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) { - link_addr = llext_find_extension_sym(name); + /* 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) { diff --git a/subsys/llext/llext_priv.h b/subsys/llext/llext_priv.h index be11877395709ad..da2484dc3fe211a 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_ */