0

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

7
  • "behaviour should be the same regardless of the profile" – no, that's not generally the case
    – cafce25
    Commented 7 hours ago
  • If I understand the question correctly, sp_ptr is equal to 0x0, and you get a crash as above when dereferencing via *sp_ptr?! If so, the answer is quite simple: It is always invalid to dereference the null pointer. Without --release, the compiler will helpfully inject safety checks. Yet in (default) --release, you simply get Undefined Behavior. The fact that it seems to work is irrelevant. Commented 7 hours ago
  • 1
    A pointer with the address 0 and null are not the same thing @user2722968 That being said the check inserted by the compiler is probably the culprit here.
    – cafce25
    Commented 4 hours ago
  • The sp_ptr is a pointer to flash address 0x0. I can disassemble the binary to confirm that there is a check inserted in the debug profile. Thanks for the hint! Commented 3 hours ago
  • Although you made me think about how to prevent such a check. Is a pointer to a 0x0 memory address prohibited to have? If so, how am I able to read memory from the start? :) Commented 3 hours ago

1 Answer 1

0

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.