From ced507e0a87b7cf0f086badc8a360c349f8445f0 Mon Sep 17 00:00:00 2001 From: Luca Burelli Date: Thu, 6 Feb 2025 12:31:04 +0100 Subject: [PATCH] llext: support alignment requirements for sections This patch adds support for alignment requirements for sections in the loader. The alignment requirements are taken from the ELF file and checked when the section is copied to memory. The section's offset is also checked for alignment when it is mapped to a memory region. Signed-off-by: Luca Burelli --- subsys/llext/llext_load.c | 35 ++++++++++++++++++++++++++++++++++- subsys/llext/llext_mem.c | 29 +++++++++++++++++++++-------- 2 files changed, 55 insertions(+), 9 deletions(-) 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; }