Skip to content

Llext relocatable fixes - #11219

Draft
jsarha wants to merge 9 commits into
thesofproject:mainfrom
jsarha:llext-relocatable-fixes
Draft

jsarha wants to merge 9 commits into
thesofproject:mainfrom
jsarha:llext-relocatable-fixes

Conversation

@jsarha

@jsarha jsarha commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Fixes and additions to #11037

lrgirdwo and others added 9 commits July 24, 2026 16:22
When CONFIG_LLEXT_TYPE_ELF_RELOCATABLE is active, bypass appending
static address flags (-Ttext, --section-start, -Tdata) in the linker
helper script. This keeps section base addresses at 0.
Also adjust the offset calculator to avoid integer parsing errors
when all section addresses are set to 0.

Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
…table modules

Implement page-level virtual memory mapping for LLEXT libraries by
reserving VMA ranges from the single shared vpage allocator
(zephyr/lib/vpage.c), the same allocator already used by the vregion
pipeline-resource-management code. This avoids introducing a second,
private virtual-address allocator/region for libraries and keeps all
virtual page bookkeeping in one place.

Compile section layout at load-time to allocate virtual addresses and
rewrite section sh_addr headers in-place. This enables Zephyr LLEXT to
naturally relocate references.

Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Enable CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID=y in llext_relocatable.conf
to link relocatable LLEXT modules against build-time function signature
hashing, providing load-time ABI mismatch protection.

Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
- Fix off-by-one error in vpages_unreserve() when relocating last in-use element.
- Use DIV_ROUND_UP() for page calculation.
- Guard ALIGN_UP() on section alignment when sh_addralign <= 1.

Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
Add doxygen documentation to llext functions.

Signed-off-by: Jyri Sarha <jyri.sarha@linux.intel.com>
Add more comments to llext_manager_layout_sections() and
llext_manager_layout_sections() to explain linking process in a bit
more detail.

Signed-off-by: Jyri Sarha <jyri.sarha@linux.intel.com>
Loading a relocatable (ET_REL) module could fault while copying sections
into SRAM, or leave part of the image unmapped. Three separate problems
in the same path, all coming from region sizes that do not describe the
virtual addresses the module is actually placed at.

1. mctx->segment[] took its size straight from the llext region
   descriptor. llext_map_sections() builds those sizes from ELF *file*
   offsets, pads them by the region alignment, and only shifts sh_addr
   back for ET_DYN images. SOF pre-locates ET_REL modules at freshly
   allocated virtual addresses, so the file span says nothing about the
   VMA span. Regions overlapped each other and, for mfcc and
   mixin_mixout, reached a page past the module's own vpage reservation,
   re-mapping and re-permissioning a page belonging to another module.
   Measure the real extent of each region's non-detached sections
   instead, in the new llext_manager_region_extent().

2. The destination bound passed to memcpy_s() was 'size - s_offset',
   mixing the region size with a file-offset delta. The inflated sizes
   from 1. hid this; with correct sizes it under-bounds the destination
   and memcpy_s() starts rejecting valid copies. Bound the copy by the
   space left in the mapped range instead, and log the section that
   failed rather than returning silently.

3. The layout page-aligns .bss into a page of its own, so it is normally
   not adjacent to .data. The merge only added bss_size to data_size and
   ignored the gap between them, so the mapping covered the .data page
   alone and the memset() that zeroes .bss faulted on an unmapped page.
   smart_amp hit this with 8 bytes of .data and 4 bytes of .bss a page
   apart. Map the range spanning both, which is what the comment on the
   .data mapping already promises.

Tested on ACE30/PTL: MICSEL, PEAKVOL, MIXIN and SMATEST all load and map,
and playback runs cleanly. Note that a matching mm fix is needed for the
HPSRAM banks above the firmware image to be powered on at all.
The virtual page allocator is brought up by a SYS_INIT() handler, so it
starts out empty on every firmware boot, including the D0 restore that
follows DSP power gating. llext_manager_restore_from_dram() brings back
modules that are still resident at the addresses they were given before
gating, but never tells the allocator that those ranges are taken.

The first allocation after such a restore then hands out memory that a
restored module already occupies. On PTL the SRC module sits at the very
start of the region, so a DP module heap is given the same base and the
overlap is only caught when the module's memory domain is populated:

  vpage_alloc ptr 0xa02b9000 pages 7 free 480/487
  vregion_create: new at base 0xa02b9000 size 0x7000
  check_add_partition: partition base a02b9000 (size 12288) overlaps
                       existing base a02b9000 (size 28672)
  scheduler_dp_task_init: failed to add LLEXT to domain -22

The "free 480/487" above shows the allocator accounting for the seven
pages it just handed out and nothing else, while the five restored
extensions hold 102 pages. Reproduced by running playback and capture
concurrently, which makes the power gating cycle far more likely.

Add vpage_reserve_at() to claim a specific range and use it to account
for each restored module, so the allocator only offers addresses that
are genuinely free.

Signed-off-by: Jyri Sarha <jyri.sarha@linux.intel.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants