diff --git a/app/llext_relocatable.conf b/app/llext_relocatable.conf index 76b5339e1bb0..ce8dafe3a6aa 100644 --- a/app/llext_relocatable.conf +++ b/app/llext_relocatable.conf @@ -1 +1,2 @@ CONFIG_LLEXT_TYPE_ELF_RELOCATABLE=y +CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID=y diff --git a/scripts/llext_link_helper.py b/scripts/llext_link_helper.py index f10777c9918f..333c863d1d21 100755 --- a/scripts/llext_link_helper.py +++ b/scripts/llext_link_helper.py @@ -77,6 +77,8 @@ def main(): command = [args.command] + is_relocatable = '-r' in args.params + executable = [] writable = [] readonly = [] @@ -111,7 +113,8 @@ def main(): text_found = True text_addr = max_alignment(text_addr, 0x1000, s_alignment) text_size = s_size - command.append(f'-Wl,-Ttext=0x{text_addr:x}') + if not is_relocatable: + command.append(f'-Wl,-Ttext=0x{text_addr:x}') else: executable.append(section) @@ -164,7 +167,8 @@ def main(): dram_addr = align_up(dram_addr, s_alignment) - command.append(f'-Wl,--section-start={s_name}=0x{dram_addr:x}') + if not is_relocatable: + command.append(f'-Wl,--section-start={s_name}=0x{dram_addr:x}') dram_addr += section.header['sh_size'] @@ -177,7 +181,8 @@ def main(): dram_addr = align_up(dram_addr, s_alignment) - command.append(f'-Wl,--section-start={s_name}=0x{dram_addr:x}') + if not is_relocatable: + command.append(f'-Wl,--section-start={s_name}=0x{dram_addr:x}') dram_addr += section.header['sh_size'] @@ -189,7 +194,8 @@ def main(): start_addr = align_up(start_addr, s_alignment) - command.append(f'-Wl,--section-start={s_name}=0x{start_addr:x}') + if not is_relocatable: + command.append(f'-Wl,--section-start={s_name}=0x{start_addr:x}') start_addr += section.header['sh_size'] @@ -201,10 +207,11 @@ def main(): start_addr = align_up(start_addr, s_alignment) - if s_name == '.data': - command.append(f'-Wl,-Tdata=0x{start_addr:x}') - else: - command.append(f'-Wl,--section-start={s_name}=0x{start_addr:x}') + if not is_relocatable: + if s_name == '.data': + command.append(f'-Wl,-Tdata=0x{start_addr:x}') + else: + command.append(f'-Wl,--section-start={s_name}=0x{start_addr:x}') start_addr += section.header['sh_size'] diff --git a/scripts/llext_offset_calc.py b/scripts/llext_offset_calc.py index 0f302a8cbe12..2a07984b725f 100755 --- a/scripts/llext_offset_calc.py +++ b/scripts/llext_offset_calc.py @@ -47,6 +47,9 @@ def get_elf_size(elf_name): if section.header['sh_addr'] + section.header['sh_size'] > end: end = section.header['sh_addr'] + section.header['sh_size'] + if start == 0xffffffff: + return 0 + size = end - start return size diff --git a/src/include/sof/lib_manager.h b/src/include/sof/lib_manager.h index 29c226eb61a7..87ddc082d409 100644 --- a/src/include/sof/lib_manager.h +++ b/src/include/sof/lib_manager.h @@ -116,6 +116,8 @@ struct lib_manager_module { unsigned int n_dependent; /* For auxiliary modules: number of dependents */ bool mapped; struct lib_manager_segment_desc segment[LIB_MANAGER_N_SEGMENTS]; + uintptr_t vma_base; + size_t vma_size; }; struct lib_manager_mod_ctx { diff --git a/src/library_manager/llext_manager.c b/src/library_manager/llext_manager.c index 4c1e4f02d5b5..a098b7b61bcd 100644 --- a/src/library_manager/llext_manager.c +++ b/src/library_manager/llext_manager.c @@ -50,6 +50,101 @@ extern struct tr_ctx lib_manager_tr; #define PAGE_SZ CONFIG_MM_DRV_PAGE_SIZE +#include +#include + +#define LLEXT_LIB_PAGES (CONFIG_LIBRARY_REGION_SIZE / PAGE_SZ) +SYS_BITARRAY_DEFINE_STATIC(lib_vma_bitarray, LLEXT_LIB_PAGES); + +static uintptr_t llext_manager_alloc_vma(size_t size) +{ + size_t num_pages = ALIGN_UP(size, PAGE_SZ) / PAGE_SZ; + size_t offset; + int ret; + + ret = sys_bitarray_alloc(&lib_vma_bitarray, num_pages, &offset); + if (ret < 0) { + tr_err(&lib_manager_tr, "llext_manager_alloc_vma: failed to allocate %zu pages", num_pages); + return 0; + } + + return CONFIG_LIBRARY_BASE_ADDRESS + offset * PAGE_SZ; +} + +void llext_manager_free_vma(uintptr_t vma, size_t size) +{ + if (!vma || !size) + return; + + size_t num_pages = ALIGN_UP(size, PAGE_SZ) / PAGE_SZ; + size_t offset = (vma - CONFIG_LIBRARY_BASE_ADDRESS) / PAGE_SZ; + + sys_bitarray_free(&lib_vma_bitarray, num_pages, offset); +} + +static enum llext_mem llext_manager_get_sec_mem_idx(const char *name, const elf_shdr_t *shdr) +{ + if (strcmp(name, ".exported_sym") == 0) + return LLEXT_MEM_EXPORT; + + switch (shdr->sh_type) { + case SHT_NOBITS: + return LLEXT_MEM_BSS; + case SHT_PROGBITS: + if (shdr->sh_flags & SHF_EXECINSTR) + return LLEXT_MEM_TEXT; + else if (shdr->sh_flags & SHF_WRITE) + return LLEXT_MEM_DATA; + else + return LLEXT_MEM_RODATA; + case SHT_PREINIT_ARRAY: + return LLEXT_MEM_PREINIT; + case SHT_INIT_ARRAY: + return LLEXT_MEM_INIT; + case SHT_FINI_ARRAY: + return LLEXT_MEM_FINI; + default: + return LLEXT_MEM_COUNT; + } +} + +static size_t llext_manager_layout_sections(uint8_t *elf_buf, uintptr_t vma_base) +{ + elf_ehdr_t *hdr = (elf_ehdr_t *)elf_buf; + elf_shdr_t *shdrs = (elf_shdr_t *)(elf_buf + hdr->e_shoff); + elf_shdr_t *shstr_shdr = shdrs + hdr->e_shstrndx; + const char *shstrtab = (const char *)(elf_buf + shstr_shdr->sh_offset); + + uintptr_t current_vma = vma_base; + enum llext_mem last_region = LLEXT_MEM_COUNT; + + for (int i = 0; i < hdr->e_shnum; i++) { + elf_shdr_t *shdr = shdrs + i; + + if (!(shdr->sh_flags & SHF_ALLOC) || shdr->sh_size == 0) + continue; + + const char *name = shstrtab + shdr->sh_name; + enum llext_mem s_region = llext_manager_get_sec_mem_idx(name, shdr); + + if (s_region == LLEXT_MEM_COUNT) + continue; + + if (last_region != LLEXT_MEM_COUNT && last_region != s_region) { + current_vma = ALIGN_UP(current_vma, PAGE_SZ); + } + last_region = s_region; + + current_vma = ALIGN_UP(current_vma, shdr->sh_addralign); + if (vma_base) { + shdr->sh_addr = current_vma; + } + current_vma += shdr->sh_size; + } + + return current_vma - vma_base; +} + static int llext_manager_update_flags(void __sparse_cache *vma, size_t size, uint32_t flags) { size_t pre_pad_size = (uintptr_t)vma & (PAGE_SZ - 1); @@ -383,6 +478,26 @@ static int llext_manager_link(const char *name, } if (!*llext || mctx->mapped) { + if (!*llext) { + uint8_t *elf_buf = (uint8_t *)mctx->ebl->buf; + size_t total_size = llext_manager_layout_sections(elf_buf, 0); + if (total_size == 0) { + tr_err(&lib_manager_tr, "llext_manager_link: layout sections failed"); + return -EINVAL; + } + + uintptr_t vma_base = llext_manager_alloc_vma(total_size); + if (!vma_base) { + tr_err(&lib_manager_tr, "llext_manager_link: VMA allocation failed"); + return -ENOMEM; + } + + mctx->vma_base = vma_base; + mctx->vma_size = total_size; + + llext_manager_layout_sections(elf_buf, vma_base); + } + /* * Either the very first time loading this module, or the module * is already mapped, we just call llext_load() to refcount it @@ -395,8 +510,15 @@ static int llext_manager_link(const char *name, }; ret = llext_load(ldr, name, llext, &ldr_parm); - if (ret) + if (ret) { + tr_err(&lib_manager_tr, "llext_load failed: ret=%d", ret); + if (mctx->vma_base) { + llext_manager_free_vma(mctx->vma_base, mctx->vma_size); + mctx->vma_base = 0; + mctx->vma_size = 0; + } return ret; + } } /* All code sections */ diff --git a/src/library_manager/llext_manager_dram.c b/src/library_manager/llext_manager_dram.c index 2f6cff2b3501..1cc8fad942f8 100644 --- a/src/library_manager/llext_manager_dram.c +++ b/src/library_manager/llext_manager_dram.c @@ -14,6 +14,8 @@ LOG_MODULE_DECLARE(lib_manager, CONFIG_SOF_LOG_LEVEL); +void llext_manager_free_vma(uintptr_t vma, size_t size); + struct lib_manager_dram_storage { struct ext_library ext_lib; struct lib_manager_mod_ctx *ctx; @@ -332,6 +334,9 @@ int llext_manager_restore_from_dram(void) if (mod[k].llext) llext_unload(&mod[k].llext); + if (mod[k].vma_base) + llext_manager_free_vma(mod[k].vma_base, mod[k].vma_size); + if (mod[k].ebl) rfree(mod[k].ebl); }