Skip to content

Commit

Permalink
llext: add dependencies
Browse files Browse the repository at this point in the history
When an LLEXT object uses symbols of another such object, it depends
on it. Such dependencies have to be tracked to prevent their
accidental unloading. Ideally we should be able to track arbitrary
numbers of such dependencies, but this is a bit difficult. In this
first implementation we use a fixed-size array, currently consisting
of 8 entries.

Signed-off-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
  • Loading branch information
lyakh committed Aug 29, 2024
1 parent b2114ea commit 89d27f7
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 2 deletions.
5 changes: 5 additions & 0 deletions include/zephyr/llext/llext.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down Expand Up @@ -111,6 +114,8 @@ struct llext {

/** Extension use counter, prevents unloading while in use */
unsigned int use_count;

struct llext *dependency[LLEXT_MAX_DEPENDENCIES];
};

/**
Expand Down
2 changes: 2 additions & 0 deletions subsys/llext/llext.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
65 changes: 63 additions & 2 deletions subsys/llext/llext_link.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,56 @@ 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;
};
Expand All @@ -64,17 +113,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;
}
Expand Down Expand Up @@ -168,15 +221,23 @@ 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) {
Expand Down
1 change: 1 addition & 0 deletions subsys/llext/llext_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_ */

0 comments on commit 89d27f7

Please sign in to comment.