Search code examples
memoryrustembeddedrtos

Why this Rust code allocates buffers on same memory region?


I don't understand the behaviour of this piece of code... I'm writing an RTOS an this issue is halting me. I really don't get why the code acts this way.

Here is some code I tested on the playground that shows the issue.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=cc6cc0ec8bfe76f65e1baaa67caaf9e6

use core::fmt;
use core::fmt::Display;

struct StackPointer(*const usize);

impl Display for StackPointer {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0 as usize)
    }
}

struct Stack<const WORDS: usize> {
    pub sp: StackPointer,
    pub mem: [usize; WORDS],
}

impl<const WORDS: usize> Stack<WORDS> {
    pub fn new() -> Self {
        let mem = [0; WORDS];
        let sp = StackPointer(mem.as_ptr() as *const usize);
        
        Self {
            mem,
            sp,
        }
    }
}


struct PCB<const WORDS: usize> {
    pub stack: Stack<WORDS>,
}

impl<const WORDS: usize> PCB<WORDS> {
    pub fn new() -> Self {
        Self {
            stack: Stack::new(),
        }
    }
}

fn main() {
    let pcb1 = PCB::<128>::new();
    let pcb2 = PCB::<128>::new();
    let pcb3 = PCB::<128>::new();
    
    println!("sp1: {}, sp2: {}, sp3: {}", pcb1.stack.sp, pcb2.stack.sp, pcb3.stack.sp);
}

Solution

  • I don't understand the behaviour of this piece of code... I'm writing an RTOS an this issue is halting me. I really don't get why the code acts this way.

    Because you're writing broken code.

    let mem = [0; WORDS];
    

    this reserves WORDS words on the stack (incidentally why is it usize?)

    let sp = StackPointer(mem.as_ptr() as *const usize);
    

    this takes a pointer to a location in the current stackframe, where you've put your array.

    Self {
        mem,
        sp,
    }
    

    this then blissfully copies the data out of the current stackframe and into the parent stackframe, while keeping a pointer to the now-popped stackframe.

    So on each call to PCB::<128>::new(); you're going to create a stackframe, allocate an array into that stackframe, take a pointer to that array (in the stackframe), then pop the stackframe.

    All the stackframes being in the same location (on top of main's stackframe) they're at roughly the same offset, hence the array is at the same offset in all calls, and all your nonsensical StackPointer store data to the same location, which will be filled with nonsense as soon as you call an other function.