diff --git a/subsys/llext/llext_load.c b/subsys/llext/llext_load.c index 70957f9462eb..df8049b3354d 100644 --- a/subsys/llext/llext_load.c +++ b/subsys/llext/llext_load.c @@ -163,13 +163,14 @@ static int llext_find_tables(struct llext_loader *ldr, struct llext *ext) elf_shdr_t *shdr = ext->sect_hdrs + i; LOG_DBG("section %d at 0x%zx: name %d, type %d, flags 0x%zx, " - "addr 0x%zx, size %zd, link %d, info %d", + "addr 0x%zx, align 0x%zx, size %zd, link %d, info %d", i, (size_t)shdr->sh_offset, shdr->sh_name, shdr->sh_type, (size_t)shdr->sh_flags, (size_t)shdr->sh_addr, + (size_t)shdr->sh_addralign, (size_t)shdr->sh_size, shdr->sh_link, shdr->sh_info); @@ -368,6 +369,38 @@ static int llext_map_sections(struct llext_loader *ldr, struct llext *ext, size_t top_ofs = MAX(region->sh_offset + region->sh_size, shdr->sh_offset + shdr->sh_size); + if (shdr->sh_addralign > region->sh_addralign) { + /* This section uses a larger alignment value + * than what is currently used by the region. + * Make sure its final position in memory is + * correct by adjusting the start of the + * region, if needed. + * + * For example, in such a situation: + * + * cur region: ofs 0x24, size 0xdc, align 0x4 + * new shdr: ofs 0x100, size 0x20, align 0x10 + * + * an aligned allocation would make the section + * start at bot_ofs + 0xdc, which does not meet + * its constraint. Moving the region start 4 + * bytes down will fix the issue. + */ + size_t prepad = bot_ofs - ROUND_DOWN(bot_ofs, shdr->sh_addralign); + + if (prepad > address && ldr->hdr.e_type == ET_DYN) { + LOG_ERR("Bad section alignment for %s (region %d)", + name, mem_idx); + return -ENOEXEC; + } + + if (ldr->hdr.e_type == ET_DYN) { + address -= prepad; + } + bot_ofs -= prepad; + region->sh_addralign = shdr->sh_addralign; + } + region->sh_addr = address; region->sh_offset = bot_ofs; region->sh_size = top_ofs - bot_ofs; diff --git a/subsys/llext/llext_mem.c b/subsys/llext/llext_mem.c index 125ea002eb66..9cce6a14d132 100644 --- a/subsys/llext/llext_mem.c +++ b/subsys/llext/llext_mem.c @@ -76,15 +76,23 @@ static int llext_copy_region(struct llext_loader *ldr, struct llext *ext, /* Directly use data from the ELF buffer if peek() is supported */ ext->mem[mem_idx] = llext_peek(ldr, region->sh_offset); if (ext->mem[mem_idx]) { - llext_init_mem_part(ext, mem_idx, (uintptr_t)ext->mem[mem_idx], - region_alloc); - ext->mem_on_heap[mem_idx] = false; - return 0; + if (!IS_ALIGNED(ext->mem[mem_idx], region_align) && + !(ldr_parm && ldr_parm->pre_located)) { + LOG_WRN("Cannot peek region %d: %p not aligned to 0x%zx", + mem_idx, ext->mem[mem_idx], (size_t)region_align); + } else { + llext_init_mem_part(ext, mem_idx, + (uintptr_t)ext->mem[mem_idx], + region_alloc); + ext->mem_on_heap[mem_idx] = false; + return 0; + } } } else if (ldr_parm && ldr_parm->pre_located) { /* - * ldr_parm cannot be NULL here with the current flow, but - * we add a check to make it future-proof + * In pre-located files all regions, including BSS, + * are placed by the user with a linker script. No + * additional memory allocation is needed here. */ ext->mem[mem_idx] = NULL; ext->mem_on_heap[mem_idx] = false; @@ -93,6 +101,9 @@ static int llext_copy_region(struct llext_loader *ldr, struct llext *ext, } if (ldr_parm && ldr_parm->pre_located) { + /* The ELF file is supposed to be pre-located, but some + * regions are not accessible or not in the correct place. + */ return -EFAULT; } @@ -103,7 +114,7 @@ static int llext_copy_region(struct llext_loader *ldr, struct llext *ext, /* On ARM with an MPU, regions must be sized and aligned to the same * power of two (larger than 32). */ - uintptr_t block_size = MAX(region_alloc, LLEXT_PAGE_SIZE); + uintptr_t block_size = MAX(MAX(region_alloc, region_align), LLEXT_PAGE_SIZE); block_size = 1 << LOG2CEIL(block_size); /* align to next power of two */ region_alloc = block_size; @@ -111,11 +122,13 @@ static int llext_copy_region(struct llext_loader *ldr, struct llext *ext, } else { /* Otherwise, round the region to multiples of LLEXT_PAGE_SIZE. */ region_alloc = ROUND_UP(region_alloc, LLEXT_PAGE_SIZE); - region_align = LLEXT_PAGE_SIZE; + region_align = MAX(region_align, LLEXT_PAGE_SIZE); } ext->mem[mem_idx] = llext_aligned_alloc(region_align, region_alloc); if (!ext->mem[mem_idx]) { + LOG_ERR("Failed allocating %zd bytes %zd-aligned for region %d", + (size_t)region_alloc, (size_t)region_align, mem_idx); return -ENOMEM; }