diff --git a/include/zephyr/llext/elf.h b/include/zephyr/llext/elf.h index b0477416a4338..2a2bfa9b7b270 100644 --- a/include/zephyr/llext/elf.h +++ b/include/zephyr/llext/elf.h @@ -206,6 +206,9 @@ struct elf64_shdr { #define SHT_NOBITS 0x8 /**< Program data with no file image */ #define SHT_REL 0x9 /**< Relocation entries without addends */ #define SHT_DYNSYM 0xB /**< Dynamic linking symbol table */ +#define SHT_INIT_ARRAY 0xe /**< Array of pointers to init functions */ +#define SHT_FINI_ARRAY 0xf /**< Array of pointers to termination functions */ +#define SHT_PREINIT_ARRAY 0x10 /**< Array of pointers to early init functions */ /** ELF section flags */ #define SHF_WRITE 0x1 /**< Section is writable */ diff --git a/include/zephyr/llext/llext.h b/include/zephyr/llext/llext.h index 19fbe7a0b55da..91ae2564f5dca 100644 --- a/include/zephyr/llext/llext.h +++ b/include/zephyr/llext/llext.h @@ -50,6 +50,9 @@ enum llext_mem { LLEXT_MEM_SYMTAB, /**< Symbol table */ LLEXT_MEM_STRTAB, /**< Symbol name strings */ LLEXT_MEM_SHSTRTAB, /**< Section name strings */ + LLEXT_MEM_PREINIT, /**< Array of early init functions */ + LLEXT_MEM_INIT, /**< Array of init functions */ + LLEXT_MEM_FINI, /**< Array of termination functions */ LLEXT_MEM_COUNT, /**< Number of regions managed by LLEXT */ }; diff --git a/subsys/llext/llext_load.c b/subsys/llext/llext_load.c index de3cdd6402b96..70a0b7ad90666 100644 --- a/subsys/llext/llext_load.c +++ b/subsys/llext/llext_load.c @@ -139,13 +139,15 @@ static int llext_load_elf_data(struct llext_loader *ldr, struct llext *ext) */ static int llext_find_tables(struct llext_loader *ldr) { - int table_cnt, i; + int i; memset(ldr->sects, 0, sizeof(ldr->sects)); /* Find symbol and string tables */ - for (i = 0, table_cnt = 0; i < ldr->sect_cnt && table_cnt < 3; ++i) { + for (i = 0; i < ldr->sect_cnt; ++i) { elf_shdr_t *shdr = ldr->sect_hdrs + i; + const char *sect_type_str; + enum llext_mem sect_mem_idx = LLEXT_MEM_COUNT; LOG_DBG("section %d at 0x%zx: name %d, type %d, flags 0x%zx, " "addr 0x%zx, size %zd, link %d, info %d", @@ -162,32 +164,49 @@ static int llext_find_tables(struct llext_loader *ldr) switch (shdr->sh_type) { case SHT_SYMTAB: case SHT_DYNSYM: - LOG_DBG("symtab at %d", i); - ldr->sects[LLEXT_MEM_SYMTAB] = *shdr; - ldr->sect_map[i].mem_idx = LLEXT_MEM_SYMTAB; - table_cnt++; + sect_type_str = "symtab"; + sect_mem_idx = LLEXT_MEM_SYMTAB; break; case SHT_STRTAB: if (ldr->hdr.e_shstrndx == i) { - LOG_DBG("shstrtab at %d", i); - ldr->sects[LLEXT_MEM_SHSTRTAB] = *shdr; - ldr->sect_map[i].mem_idx = LLEXT_MEM_SHSTRTAB; + sect_type_str = "shstrtab"; + sect_mem_idx = LLEXT_MEM_SHSTRTAB; } else { - LOG_DBG("strtab at %d", i); - ldr->sects[LLEXT_MEM_STRTAB] = *shdr; - ldr->sect_map[i].mem_idx = LLEXT_MEM_STRTAB; + sect_type_str = "strtab"; + sect_mem_idx = LLEXT_MEM_STRTAB; } - table_cnt++; break; - default: + case SHT_PREINIT_ARRAY: + sect_type_str = "preinit"; + sect_mem_idx = LLEXT_MEM_PREINIT; + break; + case SHT_INIT_ARRAY: + sect_type_str = "init"; + sect_mem_idx = LLEXT_MEM_INIT; break; + case SHT_FINI_ARRAY: + sect_type_str = "fini"; + sect_mem_idx = LLEXT_MEM_FINI; + break; + default: + /* not a special section */ + continue; + } + + if (ldr->sects[sect_mem_idx].sh_type != 0) { + LOG_ERR("Multiple %s sections found", sect_type_str); + return -ENOEXEC; } + + LOG_DBG("%s at %d", sect_type_str, i); + ldr->sects[sect_mem_idx] = *shdr; + ldr->sect_map[i].mem_idx = sect_mem_idx; } if (!ldr->sects[LLEXT_MEM_SHSTRTAB].sh_type || !ldr->sects[LLEXT_MEM_STRTAB].sh_type || !ldr->sects[LLEXT_MEM_SYMTAB].sh_type) { - LOG_ERR("Some sections are missing or present multiple times!"); + LOG_ERR("Some needed sections are missing"); return -ENOEXEC; } @@ -208,6 +227,12 @@ static int llext_map_sections(struct llext_loader *ldr, struct llext *ext) name = llext_string(ldr, ext, LLEXT_MEM_SHSTRTAB, shdr->sh_name); + if (ldr->sect_map[i].mem_idx != LLEXT_MEM_COUNT) { + LOG_DBG("section %d name %s already mapped to region %d", + i, name, ldr->sect_map[i].mem_idx); + continue; + } + /* Identify the section type by its flags */ enum llext_mem mem_idx; @@ -539,6 +564,43 @@ static int llext_copy_symbols(struct llext_loader *ldr, struct llext *ext, return 0; } +static int llext_call_array(struct llext_loader *ldr, struct llext *ext, enum llext_mem fn_array) +{ + uintptr_t *ptr; + size_t nents, i; + void (*entry_fn)(void); + + if (ldr->sects[fn_array].sh_size == 0) + return 0; + + if (ldr->sects[fn_array].sh_entsize == 0 || + ldr->sects[fn_array].sh_size % ldr->sects[fn_array].sh_entsize != 0) { + LOG_ERR("Invalid data in region %d", fn_array); + return -ENOEXEC; + } + + nents = ldr->sects[fn_array].sh_size / ldr->sects[fn_array].sh_entsize; + ptr = ext->mem[fn_array]; + for (i = 0; i < nents; ++i) { + entry_fn = (void *) *ptr++; + entry_fn(); + } + + return 0; +} + +static int llext_call_inits(struct llext_loader *ldr, struct llext *ext) +{ + int ret; + + ret = llext_call_array(ldr, ext, LLEXT_MEM_PREINIT); + if (ret != 0) { + return ret; + } + + return llext_call_array(ldr, ext, LLEXT_MEM_INIT); +} + /* * Load a valid ELF as an extension */ @@ -630,6 +692,13 @@ int do_llext_load(struct llext_loader *ldr, struct llext *ext, goto out; } + LOG_DBG("Calling init functions..."); + ret = llext_call_inits(ldr, ext); + if (ret != 0) { + LOG_ERR("Failed to init, ret %d", ret); + goto out; + } + out: /* * Free resources only used during loading. Note that this exploits