ruststackcortex-mnrf52stack-pointer

Null in a dereferenced Stack Pointer in Rust on Cortex-M4 (nRF52833)


When developing a Rust no_std bootloader for my micro:bit v2.21 (Cortex-M4, nRF52833), I have encountered a weird error.

The bootloader jumps to the main application using cortex_m::asm::bootstrap(sp, pc); call.

Out of curiosity, I wanted to see:

  1. What the beginning of the Vector Table looks like on flash (addresses 0x0 and 0x4).
  2. What addresses in RAM the Stack Pointer and Program Counter point to.

I know I can peek into the vector table using objdump, but I wanted to see it right from the code to ensure I am handing over the bootloader's control to the right place.

What an interesting journey this was :)

Anyway, I ran into a weird issue happening only in the debug profile. Using the rtt_target and log crates, I wanted to see the SP and PC values on flash and the values they point to in RAM.

When logging the content of the SP (dereferencing the value of the 0x0 address in flash), I got:

ERROR [cortex_sp_null] The panic handler was called with PanicInfo: panicked at src/main.rs:43:9:
   null pointer dereference occurred

This is a simplified main fn I used.

#[entry]
fn main() -> ! {
    let flash_origin = 0x0u32;

    // Load memory addresses from the Vector Table in Flash
    let sp_ptr = flash_origin as *const u32;       // Stack Pointer
    let pc_ptr = (flash_origin + 4) as *const u32; // Program Counter pointer

    unsafe {
        info!("SP FLASH ADDRESS: {:#010X}", sp_ptr as usize);
        info!("PC FLASH ADDRESS: {:#010X}", pc_ptr as usize);
        info!("SP RAM ADDRESS:   {:#010X}", *sp_ptr);
        info!("PC RAM ADDRESS:   {:#010X}", *pc_ptr);
    }
    
    loop { /* print the counter */ }
}

And this line caused the panic: info!("SP RAM ADDRESS: {:#010X}", *sp_ptr);

When building and flashing the code with the --release parameter, the code runs fine and prints out:

INFO  [cortex_sp_null] Started the application
INFO  [cortex_sp_null] SP FLASH ADDRESS: 0x00000000
INFO  [cortex_sp_null] PC FLASH ADDRESS: 0x00000004
INFO  [cortex_sp_null] SP RAM ADDRESS:   0x20020000
INFO  [cortex_sp_null] PC RAM ADDRESS:   0x00000401
INFO  [cortex_sp_null] Counter: 0
INFO  [cortex_sp_null] Counter: 1

Without the --release parameter, though, I get:

INFO  [cortex_sp_null] Started the application
INFO  [cortex_sp_null] SP FLASH ADDRESS: 0x00000000
INFO  [cortex_sp_null] PC FLASH ADDRESS: 0x00000004
ERROR [cortex_sp_null] The panic handler was called with PanicInfo: panicked at src/main.rs:43:9:
null pointer dereference occurred

Do you have any idea why this could be happening? I would have assumed that the behaviour should be the same regardless of the profile.

The simplified replication application is available on GitHub, including full replication steps in the README. https://github.com/lobodpav/cortex-sp-null


Solution

  • As I have learned, Rust considers a pointer to the 0x0 address as a null pointer. Hence, the runtime error.

    The valid way to read from the 0x0 memory address is via the assembly language like so:

    let sp_value: u32;
    unsafe { core::arch::asm!("ldr {0}, [{1}]", out(reg) sp_value, in(reg) 0x0); };
    info!("SP RAM ADDRESS:   {:#010X}", sp_value);
    

    The above code has printed the expected result:

    INFO  [cortex_sp_null] SP RAM ADDRESS:   0x20020000