Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/zephyr/llext/elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
*/
Expand Down
6 changes: 6 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a Kconfig, not a blocker but would be good to see a follow up for


/**
* @brief Structure describing a linkable loadable extension
*
Expand Down Expand Up @@ -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];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could use a doc string

};

/**
Expand Down
44 changes: 23 additions & 21 deletions include/zephyr/llext/symbol.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand All @@ -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); \
Expand All @@ -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

/**
* @}
*/
Expand Down
2 changes: 1 addition & 1 deletion samples/subsys/llext/edk/ext1/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ int start(void)

return 0;
}
LL_EXTENSION_SYMBOL(start);
EXPORT_SYMBOL(start);
2 changes: 1 addition & 1 deletion samples/subsys/llext/edk/ext2/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ int start(void)

return 0;
}
LL_EXTENSION_SYMBOL(start);
EXPORT_SYMBOL(start);
2 changes: 1 addition & 1 deletion samples/subsys/llext/edk/ext3/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,4 @@ int start(void)

return 0;
}
LL_EXTENSION_SYMBOL(start);
EXPORT_SYMBOL(start);
2 changes: 1 addition & 1 deletion samples/subsys/llext/edk/k-ext1/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,4 @@ int start(void)

return 0;
}
LL_EXTENSION_SYMBOL(start);
EXPORT_SYMBOL(start);
2 changes: 1 addition & 1 deletion samples/subsys/llext/modules/src/hello_world_ext.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
7 changes: 4 additions & 3 deletions subsys/llext/llext.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down
102 changes: 102 additions & 0 deletions subsys/llext/llext_link.c
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this sounds prone to race conditions - but the whole subsystem is, anyways, as struct llext lacks a lock... This is well outside PR scope though 😄

Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}

Comment on lines +392 to +401
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very minor nit - if you ever have to refactor anything: this bit of code is added in the last commit, which only deals with dependency tracking. Similar to what happens for Xtensa, it could be split between the commit that introduces the actual lookup and the one adding tracking.

if (link_addr == 0) {
LOG_ERR("Undefined symbol with no entry in "
"symbol table %s, offset %zd, link section %d",
Expand Down
6 changes: 4 additions & 2 deletions subsys/llext/llext_load.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
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_ */
1 change: 1 addition & 0 deletions subsys/logging/log_msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
2 changes: 1 addition & 1 deletion tests/misc/llext-edk/extension/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
2 changes: 1 addition & 1 deletion tests/subsys/llext/simple/src/find_section_ext.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ void test_entry(void)
/* unused */
}

LL_EXTENSION_SYMBOL(number);
EXPORT_SYMBOL(number);
2 changes: 1 addition & 1 deletion tests/subsys/llext/simple/src/hello_world_ext.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
2 changes: 1 addition & 1 deletion tests/subsys/llext/simple/src/logging_ext.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
2 changes: 1 addition & 1 deletion tests/subsys/llext/simple/src/movwmovt_ext.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
2 changes: 1 addition & 1 deletion tests/subsys/llext/simple/src/multi_file_ext1.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,4 @@ void test_entry(void)

run_id += 1;
}
LL_EXTENSION_SYMBOL(test_entry);
EXPORT_SYMBOL(test_entry);
2 changes: 1 addition & 1 deletion tests/subsys/llext/simple/src/object_ext.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,4 @@ void test_entry(void)

run_id += 1;
}
LL_EXTENSION_SYMBOL(test_entry);
EXPORT_SYMBOL(test_entry);
2 changes: 1 addition & 1 deletion tests/subsys/llext/simple/src/pre_located_ext.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ void test_entry(void)
{
/* This function is never called */
}
LL_EXTENSION_SYMBOL(test_entry);
EXPORT_SYMBOL(test_entry);
2 changes: 1 addition & 1 deletion tests/subsys/llext/simple/src/relative_jump_ext.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
2 changes: 1 addition & 1 deletion tests/subsys/llext/simple/src/syscalls_ext.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
2 changes: 1 addition & 1 deletion tests/subsys/llext/simple/src/threads_kernel_objects_ext.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);