Skip to content

Commit

Permalink
llext: support alignment requirements for sections
Browse files Browse the repository at this point in the history
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 <l.burelli@arduino.cc>
  • Loading branch information
pillo79 committed Feb 25, 2025
1 parent 140a1c9 commit ced507e
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 9 deletions.
35 changes: 34 additions & 1 deletion subsys/llext/llext_load.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
29 changes: 21 additions & 8 deletions subsys/llext/llext_mem.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}

Expand All @@ -103,19 +114,21 @@ 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;
region_align = block_size;
} 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;
}

Expand Down

0 comments on commit ced507e

Please sign in to comment.