Skip to content

Commit c110441

Browse files
committed
llext: add dependencies
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 <[email protected]>
1 parent b2114ea commit c110441

File tree

4 files changed

+82
-2
lines changed

4 files changed

+82
-2
lines changed

include/zephyr/llext/llext.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ enum llext_mem {
6262
struct llext_loader;
6363
/** @endcond */
6464

65+
/* Maximim number of dependency LLEXTs */
66+
#define LLEXT_MAX_DEPENDENCIES 8
67+
6568
/**
6669
* @brief Structure describing a linkable loadable extension
6770
*
@@ -111,6 +114,9 @@ struct llext {
111114

112115
/** Extension use counter, prevents unloading while in use */
113116
unsigned int use_count;
117+
118+
/** Array of extensions, whose symbols this extension accesses */
119+
struct llext *dependency[LLEXT_MAX_DEPENDENCIES];
114120
};
115121

116122
/**

subsys/llext/llext.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@ int llext_unload(struct llext **ext)
197197
/* FIXME: protect the global list */
198198
sys_slist_find_and_remove(&_llext_list, &tmp->_llext_list);
199199

200+
llext_dependency_remove_all(tmp);
201+
200202
*ext = NULL;
201203
k_mutex_unlock(&llext_lock);
202204

subsys/llext/llext_link.c

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,56 @@ static size_t llext_file_offset(struct llext_loader *ldr, size_t offset)
5252
return offset;
5353
}
5454

55+
/*
56+
* We increment use-count every time a new dependent is added, and have to
57+
* decrement it again, when one is removed. Ideally we should be able to add
58+
* arbitrary numbers of dependencies, but using lists for this doesn't work,
59+
* because multiple extensions can have common dependencies. Dynamically
60+
* allocating dependency entries would be too wasteful. In this initial
61+
* implementation we use an array of dependencies, if at some point we run out
62+
* of array entries, we'll implement re-allocation.
63+
* We add dependencies incrementally as we discover them, but we only ever
64+
* expect them to be removed all at once, when their user is removed. So the
65+
* dependency array is always "dense" - it cannot have NULL entries between
66+
* valid ones.
67+
*/
68+
static int llext_dependency_add(struct llext *ext, struct llext *dependency)
69+
{
70+
unsigned int i;
71+
72+
for (i = 0; i < ARRAY_SIZE(ext->dependency); i++) {
73+
if (ext->dependency[i] == dependency) {
74+
return 0;
75+
}
76+
77+
if (!ext->dependency[i]) {
78+
ext->dependency[i] = dependency;
79+
dependency->use_count++;
80+
81+
return 0;
82+
}
83+
}
84+
85+
return -ENOENT;
86+
}
87+
88+
void llext_dependency_remove_all(struct llext *ext)
89+
{
90+
unsigned int i;
91+
92+
for (i = 0; i < ARRAY_SIZE(ext->dependency) && ext->dependency[i]; i++) {
93+
/*
94+
* The use-count of dependencies is tightly bound to dependent's
95+
* life cycle, so it shouldn't underrun.
96+
*/
97+
ext->dependency[i]->use_count--;
98+
__ASSERT(ext->dependency[i]->use_count, "LLEXT dependency use-count underrun!");
99+
/* No need to NULL-ify the pointer - ext is freed after this */
100+
}
101+
}
102+
55103
struct llext_extension_sym {
104+
struct llext *ext;
56105
const char *sym;
57106
const void *addr;
58107
};
@@ -64,17 +113,21 @@ static int llext_find_extension_sym_iterate(struct llext *ext, void *arg)
64113

65114
if (addr) {
66115
se->addr = addr;
116+
se->ext = ext;
67117
return 1;
68118
}
69119

70120
return 0;
71121
}
72122

73-
static const void *llext_find_extension_sym(const char *sym_name)
123+
static const void *llext_find_extension_sym(const char *sym_name, struct llext **ext)
74124
{
75125
struct llext_extension_sym se = {.sym = sym_name};
76126

77127
llext_iterate(llext_find_extension_sym_iterate, &se);
128+
if (ext) {
129+
*ext = se.ext;
130+
}
78131

79132
return se.addr;
80133
}
@@ -168,15 +221,23 @@ static void llext_link_plt(struct llext_loader *ldr, struct llext *ext,
168221

169222
switch (stb) {
170223
case STB_GLOBAL:
224+
/* First try the global symbol table */
171225
link_addr = llext_find_sym(NULL,
172226
SYM_NAME_OR_SLID(name, sym_tbl.st_value));
173227

174228
if (!link_addr) {
229+
/* Next try internal tables */
175230
link_addr = llext_find_sym(&ext->sym_tab, name);
176231
}
177232

178233
if (!link_addr) {
179-
link_addr = llext_find_extension_sym(name);
234+
/* Finally try any loaded tables */
235+
struct llext *dep;
236+
237+
link_addr = llext_find_extension_sym(name, &dep);
238+
if (link_addr) {
239+
llext_dependency_add(ext, dep);
240+
}
180241
}
181242

182243
if (!link_addr) {
@@ -328,6 +389,16 @@ int llext_link(struct llext_loader *ldr, struct llext *ext, bool do_local)
328389
link_addr = (uintptr_t)llext_find_sym(NULL,
329390
SYM_NAME_OR_SLID(name, sym.st_value));
330391

392+
if (link_addr == 0) {
393+
/* Try loaded tables */
394+
struct llext *dep;
395+
396+
link_addr = (uintptr_t)llext_find_extension_sym(name, &dep);
397+
if (link_addr) {
398+
llext_dependency_add(ext, dep);
399+
}
400+
}
401+
331402
if (link_addr == 0) {
332403
LOG_ERR("Undefined symbol with no entry in "
333404
"symbol table %s, offset %zd, link section %d",

subsys/llext/llext_priv.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,5 +74,6 @@ static inline const void *llext_loaded_sect_ptr(struct llext_loader *ldr, struct
7474
*/
7575

7676
int llext_link(struct llext_loader *ldr, struct llext *ext, bool do_local);
77+
void llext_dependency_remove_all(struct llext *ext);
7778

7879
#endif /* ZEPHYR_SUBSYS_LLEXT_PRIV_H_ */

0 commit comments

Comments
 (0)