Skip to content

arch: indirect executable RAM symbol#99225

Open
JordanYates wants to merge 1 commit intozephyrproject-rtos:mainfrom
Embeint:251112_executable_ram
Open

arch: indirect executable RAM symbol#99225
JordanYates wants to merge 1 commit intozephyrproject-rtos:mainfrom
Embeint:251112_executable_ram

Conversation

@JordanYates
Copy link
Contributor

Instead of marking RAM regions as executable when CONFIG_XIP=n, add a dedicated symbol to control the behaviour. XIP is the most obvious case that requires this, but LLEXT for example can also load code into RAM that is then executed.

The addition of XIP_NOT forces EXECUTABLE_RAM when XIP is disabled in order to prevent users from accidently turning it off.

Intended to solve usage faults when using LLEXT on platforms with an MPU or MMU, since currently any attempt to execute a loaded function will fault. Whether this is the right place or not to solve the problem I don't know, but it does solve it for mps2/an385.

Instead of marking RAM regions as executable when `CONFIG_XIP=n`, add
a dedicated symbol to control the behaviour. XIP is the most obvious
case that requires this, but LLEXT for example can also load code into
RAM that is then executed.

The addition of `XIP_NOT` forces `EXECUTABLE_RAM` when `XIP` is disabled
in order to prevent users from accidently turning it off.

Signed-off-by: Jordan Yates <jordan@embeint.com>
@JordanYates JordanYates force-pushed the 251112_executable_ram branch from e5a16d4 to 73e5a1e Compare November 12, 2025 11:37
@pillo79
Copy link
Contributor

pillo79 commented Nov 12, 2025

Intended to solve usage faults when using LLEXT on platforms with an MPU or MMU, since currently any attempt to execute a loaded function will fault.

Actually, MPU/MMU should be supported with USERSPACE set as well (because the memory partition APIs are hidden by that). But thanks for enlarging the use cases! 👍

Not sure how to implement it, but I would very much prefer EXECUTABLE_RAM to be force-set for non MPU/MMU targets, so that we could immediately know from that symbol's absence that there are restrictions in place.
Maybe something like

 config EXECUTABLE_RAM
	bool "Configure RAM to be executable"
	visible if (MMU || MPU)
	default y if !(MMU || MPU)
	default y if LLEXT

would work? Or alternatively have a NO_EXEC_FROM_RAM (or similar) Kconfig which is only y when restrictions are in place?

supply a linker command file when building your image. Enabling this
option increases both the code and data footprint of the image.

config XIP_NOT
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that is a weird way to name a kconfig symbol, where is this being used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know the name is weird. The point of the symbol is to force CONFIG_EXECUTABLE_RAM=y if XIP=n, since its not possible to do something like:

config XIP
    select EXECUTABLE_RAM if !XIP

Implementing it as a default on EXECUTABLE_RAM would allow the user to disable EXECUTABLE_RAM while XIP is disabled, which would be an invalid configuration.

Happy to take suggestions on naming or a better way to achieve the goal (choice symbol maybe?)

@JordanYates
Copy link
Contributor Author

Actually, MPU/MMU should be supported with USERSPACE set as well (because the memory partition APIs are hidden by that). But thanks for enlarging the use cases! 👍

I wouldn't know, I haven't compiled an application with USERSPACE=y in 5 years of using Zephyr, and I have no plans to start now :).

Not sure how to implement it, but I would very much prefer EXECUTABLE_RAM to be force-set for non MPU/MMU targets, so that we could immediately know from that symbol's absence that there are restrictions in place.

At a high level, I don't believe there is any way to reliably know at build time whether running functions loaded from LLEXT is possible. It will work if the allocated RAM is marked as executable (or not marked as no-execute), but that depends on where the memory is being allocated from and any custom configuration done on the memory.

This PR is taking a hammer to the problem by configuring the entire RAM space as executable if LLEXT is enabled, but there could easily be more nuanced solutions (that only configure the system heap as executable for example). EXECUTABLE_RAM wouldn't be set then, but LLEXT functions would still work.

I'm not suggesting this PR is the best long-term solution, but its the simplest I could come up with.

@teburd
Copy link
Contributor

teburd commented Nov 14, 2025

Any reason to do it this way rather than enabling k_mem_partitions without usermode? LLEXT will already set the permission bits to .text/.data/.rodata when done with usermode even if you use the extension in kernel mode. This saves you from making all ram read, write, and execute which likely isn't really what you wanted. But I could be wrong!

It would require a little work and was to some degree my intent here honestly. But yeah its yet another thing that is on the my long never ending todo list sitting under some clutter somewhere.

@JordanYates
Copy link
Contributor Author

JordanYates commented Nov 14, 2025

Any reason to do it this way rather than enabling k_mem_partitions without usermode?

The memory partition API only appears to be available when USERSPACE=y

#ifdef CONFIG_USERSPACE

That might be the correct approach (I've never used k_mem_partition so I don't know), but its not immediately available as a solution.

This saves you from making all ram read, write, and execute which likely isn't really what you wanted. But I could be wrong!

You are correct, it is not ideal. But it is preferable to LLEXT being unusable for functions :)

edit: I guess the ideal solution would be a Kconfig symbol that controls whether the system heap should be executable or not. I'm not sure how hard that would be though.

@teburd
Copy link
Contributor

teburd commented Nov 29, 2025

Any reason to do it this way rather than enabling k_mem_partitions without usermode?

The memory partition API only appears to be available when USERSPACE=y

That's true today but doesn't have to be true tomorrow. Arguably the memory partitions were only needed by usermode previously, but if you have kernel mode extensions this is an incredibly useful feature to enable while not fully enabling userspace.

#ifdef CONFIG_USERSPACE

That might be the correct approach (I've never used k_mem_partition so I don't know), but its not immediately available as a solution.

This saves you from making all ram read, write, and execute which likely isn't really what you wanted. But I could be wrong!

You are correct, it is not ideal. But it is preferable to LLEXT being unusable for functions :)

edit: I guess the ideal solution would be a Kconfig symbol that controls whether the system heap should be executable or not. I'm not sure how hard that would be though.

I would far rather see k_mem_partition usable with userspace=n, which when I had looked at it previously was quite possible but required some work.

@github-actions
Copy link

This pull request has been marked as stale because it has been open (more than) 60 days with no activity. Remove the stale label or add a comment saying that you would like to have the label removed otherwise this pull request will automatically be closed in 14 days. Note, that you can always re-open a closed pull request at any time.

@github-actions github-actions bot added the Stale label Jan 29, 2026
@github-actions github-actions bot closed this Feb 12, 2026
@teburd teburd reopened this Feb 12, 2026
@github-actions github-actions bot removed the Stale label Feb 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: Architectures area: ARM ARM (32-bit) Architecture

7 participants