zephyrproject-rtos / zephyr

Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
https://docs.zephyrproject.org
Apache License 2.0
10.44k stars 6.4k forks source link

Allow Rust alloc and userspace to coexist #76188

Open d3zd3z opened 1 month ago

d3zd3z commented 1 month ago

There are some "special" challenges to getting the support for Rust memory allocation to work with CONFIG_USERSPACE in Zephyr. It has to do with an implicit assumption in the Rust compiler that global variables will be readable. In order to prevent their strict notions of the stable compiler from being violated, they create a global variable __rust_no_alloc_shim_is_unstable and read from it within the allocator. When code registers a global allocator to the Rust runtime, it inserts this symbol, which ends up in the .bss segment.

However, when CONFIG_USERSPACE is enabled in Zephyr, the .bss segment is not accessible, and only globals that are explicitly placed in a proper segment are accessible. Unfortunately, this symbol cannot be placed in an arbitrary segment, and as such, attempts to allocate in Rust from userspace result in a memory violation.

A simple consequence of this is that it means the combination of userspace and memory allocation can't be used together. Given that a lot of initial mechanism in the Rust bindings require allocation, this is fairly limiting.

When a majority of the application code on the system is written in Rust, having the memory protection provided by CONFIG_USERSPACE is arguably not that much value. But, this makes some aspects of developing Rust support difficult to test. For example, although it is possible to test support for syscalls in Rust, this code is unable to print messages, because the message printing currently requires allocation.

I'm not actually sure what the right fix is here. But I wanted to record this as an enhancement request as a place to point people to.

mjaun commented 1 month ago

Could we solve this by moving any globals from librustapp.a to a separate memory partition and adding this memory partition to the default memory domain? Similar as it is done for libc.a. There is the K_APPMEM_PARTITION_DEFINE() macro to define the partition and the zephyr_library_app_memory() CMake function to place the library.

In this context it probably needs also to be defined which heap the Rust application is allowed to use.

d3zd3z commented 1 month ago

Chatting with some Rust devs, I think an easier at least initial solution imght be to just put the single symbol causing the problem into the rodata segment. Provided that rodata is readable from userspace, this is just a matter of declaring this variable as a const in a C file.

I agree that we probably need to think about partitions and such if we want to be able to use Rust code from userspace. Rust generally eschews global values, but if we do want them, we would need a mechanism to set their partitions. Likely this would involve setting partitions on declarations of values.

But, I would say that is beyond the scope of this ticket.

kevin-vigor commented 1 month ago

I assume you are aware of this discussion:

https://users.rust-lang.org/t/problem-with-external-global-static-linker-region/100081/6

Evidently we need to provide and instance of this symbol in a readable location - I'm not sure where the allocator implementation you are wielding comes from, but can it be modified to specify the section? If not it can surely be done with a gruesome linker script.

At any rate, I agree that userspace mode is out of scope for now