Skip to content

Commit 5204679

Browse files
committed
llext: Keep constant data in flash.
This patch adds a new Kconfig option that allows read-only data sections without relocations to be mapped directly (remain in flash) instead of being copied to RAM during extension loading. This helps extensions that have large constant data, such as certificates, lookup tables etc.. to save saving significant RAM, especially with MPU is enabled. This feature requires the extension's linker script to place rodata sections with and without relocations into separate, non-overlapping section called .rodata.reloc. Signed-off-by: Ibrahim Abdalkader <[email protected]>
1 parent 923ece1 commit 5204679

File tree

4 files changed

+34
-0
lines changed

4 files changed

+34
-0
lines changed

include/zephyr/llext/llext.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ enum llext_mem {
4545
LLEXT_MEM_TEXT, /**< Executable code */
4646
LLEXT_MEM_DATA, /**< Initialized data */
4747
LLEXT_MEM_RODATA, /**< Read-only data */
48+
#ifdef CONFIG_LLEXT_RODATA_NO_RELOC
49+
LLEXT_MEM_RODATA_NO_RELOC, /**< Read-only data without relocations (kept in flash) */
50+
#endif
4851
LLEXT_MEM_BSS, /**< Uninitialized data */
4952
LLEXT_MEM_EXPORT, /**< Exported symbol table */
5053
LLEXT_MEM_SYMTAB, /**< Symbol table */
@@ -62,6 +65,11 @@ enum llext_mem {
6265
/* Number of memory partitions used by LLEXT */
6366
#define LLEXT_MEM_PARTITIONS (LLEXT_MEM_BSS+1)
6467

68+
#ifdef CONFIG_LLEXT_RODATA_NO_RELOC
69+
/* Section name prefix for read-only data kept in flash */
70+
#define LLEXT_SECTION_RODATA_NO_RELOC ".rodata.noreloc"
71+
#endif
72+
6573
struct llext_loader;
6674
/** @endcond */
6775

subsys/llext/Kconfig

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,21 @@ config LLEXT_STORAGE_WRITABLE
108108
Select if LLEXT storage is writable, i.e. if extensions are stored in
109109
RAM and can be modified in place
110110

111+
config LLEXT_RODATA_NO_RELOC
112+
bool "Keep read-only data without relocations in flash"
113+
help
114+
When enabled, read-only data sections (.rodata) that do not have
115+
any relocations are kept in their original location (typically flash)
116+
instead of being copied to RAM.
117+
118+
This is useful for large constant data like certificates, lookup
119+
tables, or string literals that don't contain any pointers.
120+
121+
This option requires the extension's linker script to place read-only
122+
data with relocations and read-only data without relocations into
123+
separate, non-overlapping sections. If these sections overlap in the
124+
ELF file, loading will fail.
125+
111126
config LLEXT_EXPORT_DEVICES
112127
bool "Export all DT devices to llexts"
113128
select LLEXT_EXPORT_SYMBOL_GROUP_DEVICE

subsys/llext/llext_load.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,10 @@ static int llext_map_sections(struct llext_loader *ldr, struct llext *ext,
262262
mem_idx = LLEXT_MEM_TEXT;
263263
} else if (shdr->sh_flags & SHF_WRITE) {
264264
mem_idx = LLEXT_MEM_DATA;
265+
#ifdef CONFIG_LLEXT_RODATA_NO_RELOC
266+
} else if (strcmp(name, LLEXT_SECTION_RODATA_NO_RELOC) == 0) {
267+
mem_idx = LLEXT_MEM_RODATA_NO_RELOC;
268+
#endif
265269
} else {
266270
mem_idx = LLEXT_MEM_RODATA;
267271
}

subsys/llext/llext_mem.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ static void llext_init_mem_part(struct llext *ext, enum llext_mem mem_idx,
6666
ext->mem_parts[mem_idx].attr = K_MEM_PARTITION_P_RW_U_RW;
6767
break;
6868
case LLEXT_MEM_RODATA:
69+
#ifdef CONFIG_LLEXT_RODATA_NO_RELOC
70+
case LLEXT_MEM_RODATA_NO_RELOC:
71+
#endif
6972
ext->mem_parts[mem_idx].attr = K_MEM_PARTITION_P_RO_U_RO;
7073
break;
7174
default:
@@ -132,10 +135,14 @@ static int llext_copy_region(struct llext_loader *ldr, struct llext *ext,
132135
ext->mem[mem_idx] = llext_peek(ldr, region->sh_offset);
133136
if (ext->mem[mem_idx]) {
134137
if ((IS_ALIGNED(ext->mem[mem_idx], region_align) ||
138+
#ifdef CONFIG_LLEXT_RODATA_NO_RELOC
139+
(mem_idx == LLEXT_MEM_RODATA_NO_RELOC) ||
140+
#endif
135141
ldr_parm->pre_located) &&
136142
((mem_idx != LLEXT_MEM_TEXT) ||
137143
INSTR_FETCHABLE(ext->mem[mem_idx], region_alloc))) {
138144
/* Map this region directly to the ELF buffer */
145+
LOG_DBG("Region %d kept at %p", mem_idx, ext->mem[mem_idx]);
139146
llext_init_mem_part(ext, mem_idx,
140147
(uintptr_t)ext->mem[mem_idx],
141148
region_alloc);

0 commit comments

Comments
 (0)