Skip to content

Commit

Permalink
llext: compensate for pre-padding in aligned regions
Browse files Browse the repository at this point in the history
Several operations in the LLEXT module were not taking into account the
alignment padding now added to the start of each region, which could
lead to incorrect results when referring to the memory regions.

- The loading code would read the padding bytes from the ELF file as
  part of the section data, instead of zeroing them.

- The overlap checks in the section mapping code need to ignore the
  padding to avoid false positives when checking for overlaps.

- The inspect API did not adjust the returned region address, region
  size and section offsets to compensate for the padding, resulting in
  regions and sections larger than the actual specified memory area.

Signed-off-by: Luca Burelli <l.burelli@arduino.cc>
  • Loading branch information
pillo79 committed Feb 25, 2025
1 parent ced507e commit ec7e016
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 9 deletions.
14 changes: 10 additions & 4 deletions include/zephyr/llext/inspect.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,13 @@ static inline int llext_get_region_info(const struct llext_loader *ldr,
if (hdr) {
*hdr = &ldr->sects[region];
}

/* address and size compensated for alignment prepad */
if (addr) {
*addr = ext->mem[region];
*addr = (void *)((uintptr_t)ext->mem[region] + ldr->sects[region].sh_info);
}
if (size) {
*size = ext->mem_size[region];
*size = ext->mem_size[region] - ldr->sects[region].sh_info;
}

return 0;
Expand Down Expand Up @@ -117,14 +119,18 @@ static inline int llext_get_section_info(const struct llext_loader *ldr,
return -ENOTSUP;
}

enum llext_mem mem_idx = ldr->sect_map[shndx].mem_idx;

if (hdr) {
*hdr = &ext->sect_hdrs[shndx];
}
if (region) {
*region = ldr->sect_map[shndx].mem_idx;
*region = mem_idx;
}

/* offset compensated for alignment prepad */
if (offset) {
*offset = ldr->sect_map[shndx].offset;
*offset = ldr->sect_map[shndx].offset - ldr->sects[mem_idx].sh_info;
}

return 0;
Expand Down
14 changes: 11 additions & 3 deletions subsys/llext/llext_load.c
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ static int llext_find_tables(struct llext_loader *ldr, struct llext *ext)
}

/* First (bottom) and last (top) entries of a region, inclusive, for a specific field. */
#define REGION_BOT(reg, field) (size_t)(reg->field)
#define REGION_BOT(reg, field) (size_t)(reg->field + reg->sh_info)
#define REGION_TOP(reg, field) (size_t)(reg->field + reg->sh_size - 1)

/* Check if two regions x and y have any overlap on a given field. Any shared value counts. */
Expand Down Expand Up @@ -311,9 +311,13 @@ static int llext_map_sections(struct llext_loader *ldr, struct llext *ext,

if (region->sh_type == SHT_NULL) {
/* First section of this type, copy all info to the
* region descriptor.
* region descriptor. Clear the 'sh_info' field, not
* used by ELF spec on SHF_ALLOC sections, to store
* the extra bytes added at the start of the region
* for alignment corrections.
*/
memcpy(region, shdr, sizeof(*region));
region->sh_info = 0;
} else {
/* Make sure this section is compatible with the region */
if ((shdr->sh_flags & SHF_BASIC_TYPE_MASK) !=
Expand Down Expand Up @@ -384,7 +388,10 @@ static int llext_map_sections(struct llext_loader *ldr, struct llext *ext,
* 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.
* bytes down will fix the issue, at the cost
* of possibly making the region "overlap" with
* others. This extra offset must be stored for
* further adjustments.
*/
size_t prepad = bot_ofs - ROUND_DOWN(bot_ofs, shdr->sh_addralign);

Expand All @@ -398,6 +405,7 @@ static int llext_map_sections(struct llext_loader *ldr, struct llext *ext,
address -= prepad;
}
bot_ofs -= prepad;
region->sh_info = prepad;
region->sh_addralign = shdr->sh_addralign;
}

Expand Down
12 changes: 10 additions & 2 deletions subsys/llext/llext_mem.c
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,20 @@ static int llext_copy_region(struct llext_loader *ldr, struct llext *ext,
if (region->sh_type == SHT_NOBITS) {
memset(ext->mem[mem_idx], 0, region->sh_size);
} else {
ret = llext_seek(ldr, region->sh_offset);
/* fill prepad bytes, if any, with zeroes */
memset(ext->mem[mem_idx], 0, region->sh_info);

/* actual data area without prepad bytes */
size_t offset = region->sh_offset + region->sh_info;
size_t length = region->sh_size - region->sh_info;
uintptr_t base = (uintptr_t)ext->mem[mem_idx] + region->sh_info;

ret = llext_seek(ldr, offset);
if (ret != 0) {
goto err;
}

ret = llext_read(ldr, ext->mem[mem_idx], region->sh_size);
ret = llext_read(ldr, (void *)base, length);
if (ret != 0) {
goto err;
}
Expand Down

0 comments on commit ec7e016

Please sign in to comment.