Today I learned: How to read address zero on RISC-V
I have been working on a "yet another" flashloader with standard flash operations like readback and blank checks.
The chip has flash mapped starting from address
0x0 so I wrote the code with obvious
uint32_t *p = 0x0;. I built the code with
gcc like countless times before
but I did not get the behaviour I expected. The only thing "special" this time was that the CPU
was a RISC-V.
gcc generated a warning about a null pointer but I could deal with the warning later.
When running the code the debugger always stopped on an
ebreak in RISC-V ISA
is the equivalent of
bkpt from the ARM world. Simply stop and signal the debugger. But I did not put
any breakpoint instructions?!
objdump revealed that the blank check function compiled basically into a breakpoint and nothing else:
1 2 3 4 5
A quick search lead
me to a discovery
gcc optimization feature
-fdelete-null-pointer-checks and its counterpart
-fdelete-null-pointer-checks the compiler can "optimize" any known null pointer accesses to traps (breakpoints).
This is a pretty nice thing if dereferencing a pointer to
0x0 is undefined behaviour but on my chip address zero is
It turns out that, depending on the CPU architecture, an access to address zero is or is not treated
gcc. The documentation clearly mentions the null check feature is not available
on AVR and MSP430. Apparently it is also not switched on by default on Cortex-M (otherwise I would
have encountered it before). Guess with which exact three architectures I had previous bare-metal experience before
starting development for RISC-V?
It turns out that for RISC-V
gcc a pointer to zero is more null than on other architectures. 🙂
It seems that the smallest granularity that optimization options can be applied to is a complete function.
I wanted to change the default
gcc behaviour as little as possible so I did not change
any global build flags but instead kept my
"special" pieces of code short and only applied the attribute to a few functions.
Accessing address zero is still pretty unusual outside of flashloaders & bootloaders (and maybe startup code) so
I am very happy to keep the check enabled and I like that potentially bad code screams with an
The complete function attribute is
For example the flash blank check code looks similar to this:
1 2 3 4 5 6 7 8 9 10