I want to protect myself from stack overflow. From several articles I got the idea that I can locate the stack in the bottom of the RAM, before .bss section. Since on Cortex M stack grows down, on stack overflow my code will attempt to write in non-existing memory and I'll get an exception. And exception is much better than quit corruption of static data.
I used project for stm32f10x, so 0x08000000 is the beginning of the flash and 0x20000000 is the beginning of RAM.
Using www.keil.com/.../armlink_pge1362065977713.htm i was able to come up with scatter file like this one (based on generated file):
LR_IROM1 0x08000000 0x00020000 { ; load region size_region ER_IROM1 0x08000000 0x00020000 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) } ARM_LIB_STACK 0x20000000 EMPTY 0x400 ; Stack region growing down { } RW_IRAM1 0x20000408 0x00005000-0x408 { ; RW data .ANY (+RW +ZI) } }
Here I create the stack area of size 0x400 and the rest of the RAM is given to IRAM1 section. So far so good, it seems to work. However, there are three things that puzzle me:
1) When I tried to do "RW_IRAM1 0x20000400 0x00005000-0x400", I got linker about overlapping regions of ARM_LIB_STACK and RW_IRAM1 (although I can't reproduce it right now wich is even more odd).
2) I presumed that this way the beginning of the stack would be exactly at the end of the stack region. However, when I look in the first entry of the vector table, I see there a value of 0x20000410. And this value seems to change not according the size of the stack region but according the beginning of the RW_IRAM1 region.
3) I had to edit my startup.s file and set Stack_Size equal to 0; otherwise initial stack pointer value in the vector table was a sum of 0x20000410 and Stack_Size.
So my question is - am I on the right track? Is this the correct way to split .bss and stack regions? What is causing this dispersancy between stack region and and initial stack pointer value from vector table? My test global variable is placed at the beginning of the IRAM1 and it is initialized correctly, can I pretend that everything is just fine?
And how will simply moving the stack do that?
If it overflows, it will overflow - no matter where it is located!
Andrew Neil if I leave things as is, stack will be located on top of the RAM and .bss is located at the bottom. So when stack overflows it can grow right to the .bss section and will overwrite it, quietly. I have seen absolutely random bugs when IRQ happened on top of long function call chain and overwrote some memory location somewhere, causing an assertion later in different place.
If I relocate the stack as suggested, then when it overflows, HardFault Exception will be generated and everything will break loudly and immediately. I believe this is much better.
OK - so is this just a means to try to catch the event and, thence diagnose the problem?
If so, why not just set a memory access breakpoint just beyond the bottom of the stack...?
Andrew Neil, yes, it's just to spot the problem when it occurs.
>If so, why not just set a memory access breakpoint just beyond the bottom of the stack...?
Well, because "just beyond the bottom of the stack" can potentially be a correct location in .bss section.
And because stack overflow can actually occur not just beyond the bottom but 2 elements below or three or four and I won't be able to set access breakpoints on all of them.
What I did with this scatter file: I created a global variable and assigned a value to it. Its address was equal to the beginning of the RAM. When execution stopped at the beginning of the main, that global variable was correctly initialized.
Then I set an access breakpoint to it's address and saw that after reset this one memory location was several times used as a stack element, before global initialization was complete.
This looks strange and I'm not sure if it is safe.
"just beyond the bottom of the stack" can potentially be a correct location in .bss section.
.. which could be a potential heap overflow. You could always define your heap a little larger to accommodate this testing method
You have several DWT registers, even in a Cortext-M0+. You could even set a range instead of a single address for your Watchpoint.
infocenter.arm.com/.../index.jsp
There are several other debugging techniques that programmers do.
====
I could see this trick working when you have used up all the memory in the device. However, if your device has spare memory this method makes more work for you.
With the stack at the very top of the memroy and the varaibles at the bottom, the unused memory in the middle acted as a free play area - You did not have to be as exact with your stack size definition. As you added new features, the stack could grow into the unused area a bit, as you are still developing your code.
Putting the stack on the bottom, means the stack limit must be much more precise.
http://www.keil.com/support/docs/3709.htm
Finally, the ARM compiler tools can build memory anywhere, but they are optimized to assume the stack is on top. The code will work either way, but I would have to play with examples to see if it changes the assembly code generated, if we change the stack position.