llext: add support for exporting symbols from extensions
Extensions should be able to selectively export their global symbols. Add a LL_EXTENSION_SYMBOL() macro for that. Change the present .sym_tab to be a temporary symbol table of all global symbols in an extensions, used only during linking for internal purposes. Add a new .exp_tab symbol table to store symbols, exported by an extension permanently. Signed-off-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
This commit is contained in:
parent
86da7840bd
commit
b9bdae8c07
7 changed files with 80 additions and 8 deletions
|
@ -33,6 +33,7 @@ enum llext_mem {
|
||||||
LLEXT_MEM_DATA,
|
LLEXT_MEM_DATA,
|
||||||
LLEXT_MEM_RODATA,
|
LLEXT_MEM_RODATA,
|
||||||
LLEXT_MEM_BSS,
|
LLEXT_MEM_BSS,
|
||||||
|
LLEXT_MEM_EXPORT,
|
||||||
LLEXT_MEM_SYMTAB,
|
LLEXT_MEM_SYMTAB,
|
||||||
LLEXT_MEM_STRTAB,
|
LLEXT_MEM_STRTAB,
|
||||||
LLEXT_MEM_SHSTRTAB,
|
LLEXT_MEM_SHSTRTAB,
|
||||||
|
@ -60,9 +61,18 @@ struct llext {
|
||||||
/** Total size of the llext memory usage */
|
/** Total size of the llext memory usage */
|
||||||
size_t mem_size;
|
size_t mem_size;
|
||||||
|
|
||||||
/** Exported symbols from the llext, may be linked against by other llext */
|
/*
|
||||||
|
* These are all global symbols in the extension, all of them don't
|
||||||
|
* have to be exported to other extensions, but this table is needed for
|
||||||
|
* faster internal linking, e.g. if the extension is built out of
|
||||||
|
* several files, if any symbols are referenced between files, this
|
||||||
|
* table will be used to link them.
|
||||||
|
*/
|
||||||
struct llext_symtable sym_tab;
|
struct llext_symtable sym_tab;
|
||||||
|
|
||||||
|
/** Exported symbols from the llext, may be linked against by other llext */
|
||||||
|
struct llext_symtable exp_tab;
|
||||||
|
|
||||||
/** Extension use counter, prevents unloading while in use */
|
/** Extension use counter, prevents unloading while in use */
|
||||||
unsigned int use_count;
|
unsigned int use_count;
|
||||||
};
|
};
|
||||||
|
|
|
@ -35,6 +35,8 @@ enum llext_section {
|
||||||
LLEXT_SECT_REL_RODATA,
|
LLEXT_SECT_REL_RODATA,
|
||||||
LLEXT_SECT_REL_BSS,
|
LLEXT_SECT_REL_BSS,
|
||||||
|
|
||||||
|
LLEXT_SECT_EXPORT,
|
||||||
|
|
||||||
LLEXT_SECT_SYMTAB,
|
LLEXT_SECT_SYMTAB,
|
||||||
LLEXT_SECT_STRTAB,
|
LLEXT_SECT_STRTAB,
|
||||||
LLEXT_SECT_SHSTRTAB,
|
LLEXT_SECT_SHSTRTAB,
|
||||||
|
|
|
@ -78,6 +78,9 @@ struct llext_symtable {
|
||||||
.name = STRINGIFY(x), .addr = &x, \
|
.name = STRINGIFY(x), .addr = &x, \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define LL_EXTENSION_SYMBOL(x) struct llext_symbol __attribute__((section(".exported_sym"), used)) \
|
||||||
|
symbol_##x = {STRINGIFY(x), &x}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Export a system call to a table of symbols
|
* @brief Export a system call to a table of symbols
|
||||||
*
|
*
|
||||||
|
|
|
@ -75,7 +75,7 @@ struct llext *llext_by_name(const char *name)
|
||||||
const void * const llext_find_sym(const struct llext_symtable *sym_table, const char *sym_name)
|
const void * const llext_find_sym(const struct llext_symtable *sym_table, const char *sym_name)
|
||||||
{
|
{
|
||||||
if (sym_table == NULL) {
|
if (sym_table == NULL) {
|
||||||
/* Buildin symbol table */
|
/* Built-in symbol table */
|
||||||
STRUCT_SECTION_FOREACH(llext_const_symbol, sym) {
|
STRUCT_SECTION_FOREACH(llext_const_symbol, sym) {
|
||||||
if (strcmp(sym->name, sym_name) == 0) {
|
if (strcmp(sym->name, sym_name) == 0) {
|
||||||
return sym->addr;
|
return sym->addr;
|
||||||
|
@ -214,6 +214,9 @@ static int llext_map_sections(struct llext_loader *ldr, struct llext *ext)
|
||||||
} else if (strcmp(name, ".bss") == 0) {
|
} else if (strcmp(name, ".bss") == 0) {
|
||||||
sect_idx = LLEXT_SECT_BSS;
|
sect_idx = LLEXT_SECT_BSS;
|
||||||
mem_idx = LLEXT_MEM_BSS;
|
mem_idx = LLEXT_MEM_BSS;
|
||||||
|
} else if (strcmp(name, ".exported_sym") == 0) {
|
||||||
|
sect_idx = LLEXT_SECT_EXPORT;
|
||||||
|
mem_idx = LLEXT_MEM_EXPORT;
|
||||||
} else {
|
} else {
|
||||||
LOG_DBG("Not copied section %s", name);
|
LOG_DBG("Not copied section %s", name);
|
||||||
continue;
|
continue;
|
||||||
|
@ -240,6 +243,9 @@ static enum llext_section llext_sect_from_mem(enum llext_mem m)
|
||||||
case LLEXT_MEM_RODATA:
|
case LLEXT_MEM_RODATA:
|
||||||
s = LLEXT_SECT_RODATA;
|
s = LLEXT_SECT_RODATA;
|
||||||
break;
|
break;
|
||||||
|
case LLEXT_MEM_EXPORT:
|
||||||
|
s = LLEXT_SECT_EXPORT;
|
||||||
|
break;
|
||||||
case LLEXT_MEM_TEXT:
|
case LLEXT_MEM_TEXT:
|
||||||
s = LLEXT_SECT_TEXT;
|
s = LLEXT_SECT_TEXT;
|
||||||
break;
|
break;
|
||||||
|
@ -401,6 +407,37 @@ static int llext_allocate_symtab(struct llext_loader *ldr, struct llext *ext)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int llext_export_symbols(struct llext_loader *ldr, struct llext *ext)
|
||||||
|
{
|
||||||
|
elf_shdr_t *shdr = ldr->sects + LLEXT_SECT_EXPORT;
|
||||||
|
struct llext_symbol *sym;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
if (shdr->sh_size < sizeof(struct llext_symbol)) {
|
||||||
|
/* Not found, no symbols exported */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct llext_symtable *exp_tab = &ext->exp_tab;
|
||||||
|
|
||||||
|
exp_tab->sym_cnt = shdr->sh_size / sizeof(struct llext_symbol);
|
||||||
|
exp_tab->syms = k_heap_alloc(&llext_heap, exp_tab->sym_cnt * sizeof(struct llext_symbol),
|
||||||
|
K_NO_WAIT);
|
||||||
|
if (!exp_tab->syms) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0, sym = ext->mem[LLEXT_MEM_EXPORT];
|
||||||
|
i < exp_tab->sym_cnt;
|
||||||
|
i++, sym++) {
|
||||||
|
exp_tab->syms[i].name = sym->name;
|
||||||
|
exp_tab->syms[i].addr = sym->addr;
|
||||||
|
LOG_DBG("sym %p name %s in %p", sym->addr, sym->name, exp_tab->syms + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int llext_copy_symbols(struct llext_loader *ldr, struct llext *ext)
|
static int llext_copy_symbols(struct llext_loader *ldr, struct llext *ext)
|
||||||
{
|
{
|
||||||
size_t ent_size = ldr->sects[LLEXT_SECT_SYMTAB].sh_entsize;
|
size_t ent_size = ldr->sects[LLEXT_SECT_SYMTAB].sh_entsize;
|
||||||
|
@ -624,6 +661,8 @@ static int llext_link(struct llext_loader *ldr, struct llext *ext, bool do_local
|
||||||
} else if (strcmp(name, ".rel.data") == 0 ||
|
} else if (strcmp(name, ".rel.data") == 0 ||
|
||||||
strcmp(name, ".rela.data") == 0) {
|
strcmp(name, ".rela.data") == 0) {
|
||||||
loc = (uintptr_t)ext->mem[LLEXT_MEM_DATA];
|
loc = (uintptr_t)ext->mem[LLEXT_MEM_DATA];
|
||||||
|
} else if (strcmp(name, ".rel.exported_sym") == 0) {
|
||||||
|
loc = (uintptr_t)ext->mem[LLEXT_MEM_EXPORT];
|
||||||
} else if (strcmp(name, ".rela.plt") == 0 ||
|
} else if (strcmp(name, ".rela.plt") == 0 ||
|
||||||
strcmp(name, ".rela.dyn") == 0) {
|
strcmp(name, ".rela.dyn") == 0) {
|
||||||
llext_link_plt(ldr, ext, &shdr, do_local);
|
llext_link_plt(ldr, ext, &shdr, do_local);
|
||||||
|
@ -679,7 +718,8 @@ static int llext_link(struct llext_loader *ldr, struct llext *ext, bool do_local
|
||||||
name, rel.r_offset, shdr.sh_link);
|
name, rel.r_offset, shdr.sh_link);
|
||||||
return -ENODATA;
|
return -ENODATA;
|
||||||
}
|
}
|
||||||
} else if (ELF_ST_TYPE(sym.st_info) == STT_SECTION) {
|
} else if (ELF_ST_TYPE(sym.st_info) == STT_SECTION ||
|
||||||
|
ELF_ST_TYPE(sym.st_info) == STT_FUNC) {
|
||||||
/* Current relocation location holds an offset into the section */
|
/* Current relocation location holds an offset into the section */
|
||||||
link_addr = (uintptr_t)ext->mem[ldr->sect_map[sym.st_shndx]]
|
link_addr = (uintptr_t)ext->mem[ldr->sect_map[sym.st_shndx]]
|
||||||
+ sym.st_value
|
+ sym.st_value
|
||||||
|
@ -719,6 +759,7 @@ static int do_llext_load(struct llext_loader *ldr, struct llext *ext,
|
||||||
|
|
||||||
memset(ldr->sects, 0, sizeof(ldr->sects));
|
memset(ldr->sects, 0, sizeof(ldr->sects));
|
||||||
ldr->sect_cnt = 0;
|
ldr->sect_cnt = 0;
|
||||||
|
ext->sym_tab.sym_cnt = 0;
|
||||||
|
|
||||||
size_t sect_map_sz = ldr->hdr.e_shnum * sizeof(uint32_t);
|
size_t sect_map_sz = ldr->hdr.e_shnum * sizeof(uint32_t);
|
||||||
|
|
||||||
|
@ -788,6 +829,12 @@ static int do_llext_load(struct llext_loader *ldr, struct llext *ext,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = llext_export_symbols(ldr, ext);
|
||||||
|
if (ret != 0) {
|
||||||
|
LOG_ERR("Failed to export, ret %d", ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
k_heap_free(&llext_heap, ldr->sect_map);
|
k_heap_free(&llext_heap, ldr->sect_map);
|
||||||
|
|
||||||
|
@ -798,12 +845,16 @@ out:
|
||||||
k_heap_free(&llext_heap, ext->mem[mem_idx]);
|
k_heap_free(&llext_heap, ext->mem[mem_idx]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
k_heap_free(&llext_heap, ext->sym_tab.syms);
|
k_heap_free(&llext_heap, ext->exp_tab.syms);
|
||||||
} else {
|
} else {
|
||||||
LOG_DBG("loaded module, .text at %p, .rodata at %p", ext->mem[LLEXT_MEM_TEXT],
|
LOG_DBG("loaded module, .text at %p, .rodata at %p", ext->mem[LLEXT_MEM_TEXT],
|
||||||
ext->mem[LLEXT_MEM_RODATA]);
|
ext->mem[LLEXT_MEM_RODATA]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ext->sym_tab.sym_cnt = 0;
|
||||||
|
k_heap_free(&llext_heap, ext->sym_tab.syms);
|
||||||
|
ext->sym_tab.syms = NULL;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -912,7 +963,7 @@ int llext_unload(struct llext **ext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
k_heap_free(&llext_heap, tmp->sym_tab.syms);
|
k_heap_free(&llext_heap, tmp->exp_tab.syms);
|
||||||
k_heap_free(&llext_heap, tmp);
|
k_heap_free(&llext_heap, tmp);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -922,7 +973,7 @@ int llext_call_fn(struct llext *ext, const char *sym_name)
|
||||||
{
|
{
|
||||||
void (*fn)(void);
|
void (*fn)(void);
|
||||||
|
|
||||||
fn = llext_find_sym(&ext->sym_tab, sym_name);
|
fn = llext_find_sym(&ext->exp_tab, sym_name);
|
||||||
if (fn == NULL) {
|
if (fn == NULL) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ if(CONFIG_ARM)
|
||||||
|
|
||||||
add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/hello_world.llext
|
add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/hello_world.llext
|
||||||
COMMAND ${CMAKE_C_COMPILER} ${CMAKE_C_FLAGS} -c
|
COMMAND ${CMAKE_C_COMPILER} ${CMAKE_C_FLAGS} -c
|
||||||
|
-I ${PROJECT_SOURCE_DIR}/../../../../include
|
||||||
|
-imacros${PROJECT_BINARY_DIR}/../zephyr/include/generated/autoconf.h
|
||||||
-o ${PROJECT_BINARY_DIR}/hello_world.llext
|
-o ${PROJECT_BINARY_DIR}/hello_world.llext
|
||||||
${PROJECT_SOURCE_DIR}/hello_world.c
|
${PROJECT_SOURCE_DIR}/hello_world.c
|
||||||
)
|
)
|
||||||
|
@ -19,6 +21,8 @@ elseif(CONFIG_XTENSA)
|
||||||
|
|
||||||
add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/hello_world.llext
|
add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/hello_world.llext
|
||||||
COMMAND ${CMAKE_C_COMPILER} ${CMAKE_C_FLAGS}
|
COMMAND ${CMAKE_C_COMPILER} ${CMAKE_C_FLAGS}
|
||||||
|
-I ${PROJECT_SOURCE_DIR}/../../../../include
|
||||||
|
-imacros${PROJECT_BINARY_DIR}/../zephyr/include/generated/autoconf.h
|
||||||
-o ${PROJECT_BINARY_DIR}/hello_world.pre.llext
|
-o ${PROJECT_BINARY_DIR}/hello_world.pre.llext
|
||||||
${PROJECT_SOURCE_DIR}/hello_world.c
|
${PROJECT_SOURCE_DIR}/hello_world.c
|
||||||
COMMAND ${CROSS_COMPILE}strip -R .xt.*
|
COMMAND ${CROSS_COMPILE}strip -R .xt.*
|
||||||
|
|
|
@ -12,13 +12,15 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <zephyr/llext/symbol.h>
|
||||||
|
|
||||||
extern void printk(char *fmt, ...);
|
extern void printk(char *fmt, ...);
|
||||||
|
|
||||||
static const uint32_t number = 42;
|
static const uint32_t number = 42;
|
||||||
|
|
||||||
extern void hello_world(void)
|
void hello_world(void)
|
||||||
{
|
{
|
||||||
printk("hello world\n");
|
printk("hello world\n");
|
||||||
printk("A number is %lu\n", number);
|
printk("A number is %lu\n", number);
|
||||||
}
|
}
|
||||||
|
LL_EXTENSION_SYMBOL(hello_world);
|
||||||
|
|
|
@ -38,7 +38,7 @@ ZTEST(llext, test_llext_simple)
|
||||||
|
|
||||||
zassert_ok(res, "load should succeed");
|
zassert_ok(res, "load should succeed");
|
||||||
|
|
||||||
const void * const hello_world_fn = llext_find_sym(&ext->sym_tab, "hello_world");
|
const void * const hello_world_fn = llext_find_sym(&ext->exp_tab, "hello_world");
|
||||||
|
|
||||||
zassert_not_null(hello_world_fn, "hello_world should be an exported symbol");
|
zassert_not_null(hello_world_fn, "hello_world should be an exported symbol");
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue