From b9bdae8c07a3be0ac365bd7ec2ed0b58cd4f7adc Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 9 Nov 2023 14:28:47 +0100 Subject: [PATCH] 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 --- include/zephyr/llext/llext.h | 12 +++- include/zephyr/llext/loader.h | 2 + include/zephyr/llext/symbol.h | 3 + subsys/llext/llext.c | 61 +++++++++++++++++-- tests/subsys/llext/hello_world/CMakeLists.txt | 4 ++ tests/subsys/llext/hello_world/hello_world.c | 4 +- tests/subsys/llext/src/test_llext_simple.c | 2 +- 7 files changed, 80 insertions(+), 8 deletions(-) diff --git a/include/zephyr/llext/llext.h b/include/zephyr/llext/llext.h index 5dc5b6b0b72..c097ec2b329 100644 --- a/include/zephyr/llext/llext.h +++ b/include/zephyr/llext/llext.h @@ -33,6 +33,7 @@ enum llext_mem { LLEXT_MEM_DATA, LLEXT_MEM_RODATA, LLEXT_MEM_BSS, + LLEXT_MEM_EXPORT, LLEXT_MEM_SYMTAB, LLEXT_MEM_STRTAB, LLEXT_MEM_SHSTRTAB, @@ -60,9 +61,18 @@ struct llext { /** Total size of the llext memory usage */ 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; + /** 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 */ unsigned int use_count; }; diff --git a/include/zephyr/llext/loader.h b/include/zephyr/llext/loader.h index 3102f17cf1a..bef29524471 100644 --- a/include/zephyr/llext/loader.h +++ b/include/zephyr/llext/loader.h @@ -35,6 +35,8 @@ enum llext_section { LLEXT_SECT_REL_RODATA, LLEXT_SECT_REL_BSS, + LLEXT_SECT_EXPORT, + LLEXT_SECT_SYMTAB, LLEXT_SECT_STRTAB, LLEXT_SECT_SHSTRTAB, diff --git a/include/zephyr/llext/symbol.h b/include/zephyr/llext/symbol.h index 84e43d22b5b..2c6505ee12f 100644 --- a/include/zephyr/llext/symbol.h +++ b/include/zephyr/llext/symbol.h @@ -78,6 +78,9 @@ struct llext_symtable { .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 * diff --git a/subsys/llext/llext.c b/subsys/llext/llext.c index 05878b5c72f..281f5d1047f 100644 --- a/subsys/llext/llext.c +++ b/subsys/llext/llext.c @@ -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) { if (sym_table == NULL) { - /* Buildin symbol table */ + /* Built-in symbol table */ STRUCT_SECTION_FOREACH(llext_const_symbol, sym) { if (strcmp(sym->name, sym_name) == 0) { 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) { sect_idx = LLEXT_SECT_BSS; mem_idx = LLEXT_MEM_BSS; + } else if (strcmp(name, ".exported_sym") == 0) { + sect_idx = LLEXT_SECT_EXPORT; + mem_idx = LLEXT_MEM_EXPORT; } else { LOG_DBG("Not copied section %s", name); continue; @@ -240,6 +243,9 @@ static enum llext_section llext_sect_from_mem(enum llext_mem m) case LLEXT_MEM_RODATA: s = LLEXT_SECT_RODATA; break; + case LLEXT_MEM_EXPORT: + s = LLEXT_SECT_EXPORT; + break; case LLEXT_MEM_TEXT: s = LLEXT_SECT_TEXT; break; @@ -401,6 +407,37 @@ static int llext_allocate_symtab(struct llext_loader *ldr, struct llext *ext) 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) { 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 || strcmp(name, ".rela.data") == 0) { 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 || strcmp(name, ".rela.dyn") == 0) { 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); 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 */ link_addr = (uintptr_t)ext->mem[ldr->sect_map[sym.st_shndx]] + 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)); ldr->sect_cnt = 0; + ext->sym_tab.sym_cnt = 0; 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; } + ret = llext_export_symbols(ldr, ext); + if (ret != 0) { + LOG_ERR("Failed to export, ret %d", ret); + goto out; + } + out: 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->sym_tab.syms); + k_heap_free(&llext_heap, ext->exp_tab.syms); } else { LOG_DBG("loaded module, .text at %p, .rodata at %p", ext->mem[LLEXT_MEM_TEXT], 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; } @@ -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); return 0; @@ -922,7 +973,7 @@ int llext_call_fn(struct llext *ext, const char *sym_name) { void (*fn)(void); - fn = llext_find_sym(&ext->sym_tab, sym_name); + fn = llext_find_sym(&ext->exp_tab, sym_name); if (fn == NULL) { return -EINVAL; } diff --git a/tests/subsys/llext/hello_world/CMakeLists.txt b/tests/subsys/llext/hello_world/CMakeLists.txt index 6519075e2f4..833d8c56ccd 100644 --- a/tests/subsys/llext/hello_world/CMakeLists.txt +++ b/tests/subsys/llext/hello_world/CMakeLists.txt @@ -11,6 +11,8 @@ if(CONFIG_ARM) add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/hello_world.llext 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 ${PROJECT_SOURCE_DIR}/hello_world.c ) @@ -19,6 +21,8 @@ elseif(CONFIG_XTENSA) add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/hello_world.llext 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 ${PROJECT_SOURCE_DIR}/hello_world.c COMMAND ${CROSS_COMPILE}strip -R .xt.* diff --git a/tests/subsys/llext/hello_world/hello_world.c b/tests/subsys/llext/hello_world/hello_world.c index 93d55ed5422..7ed688d2461 100644 --- a/tests/subsys/llext/hello_world/hello_world.c +++ b/tests/subsys/llext/hello_world/hello_world.c @@ -12,13 +12,15 @@ */ #include +#include extern void printk(char *fmt, ...); static const uint32_t number = 42; -extern void hello_world(void) +void hello_world(void) { printk("hello world\n"); printk("A number is %lu\n", number); } +LL_EXTENSION_SYMBOL(hello_world); diff --git a/tests/subsys/llext/src/test_llext_simple.c b/tests/subsys/llext/src/test_llext_simple.c index dde8e674d11..28a51a092b2 100644 --- a/tests/subsys/llext/src/test_llext_simple.c +++ b/tests/subsys/llext/src/test_llext_simple.c @@ -38,7 +38,7 @@ ZTEST(llext, test_llext_simple) 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");