Author Topic: C compiler: Local variables overflowing ram?  (Read 2075 times)

0 Members and 2 Guests are viewing this topic.

Offline DavidAlfaTopic starter

  • Super Contributor
  • ***
  • Posts: 6281
  • Country: es
C compiler: Local variables overflowing ram?
« on: July 03, 2021, 05:08:18 pm »
I recently added some more variables to the stm32 project and noticed some global variables were overwritten after entering a function that declared a huge local variable.
Then I read the C compiler uses stack to alllocate local variables, so there's no way to check them at compile time.
Is that correct? So it only worked until now because I was lucky and the memory was not full?

A simple example, this compiles perfectly in a stm32F103 with 20KB Ram, although I'm allocating 64KB.
Of course, it crashes. I guess the only way for this to work is to use malloc and check a valid pointer?

Code: [Select]
void test(void){
  volatile uint8_t dat[65536];
   for(uint32_t t=0;t<sizeof(dat);t++){
     dat[t]=GPIOA->IDR;
   }
   for(uint32_t t=0;t<sizeof(dat);t++){
     GPIOB->ODR=dat[t];
   }
}


« Last Edit: July 03, 2021, 05:11:43 pm by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline Feynman

  • Regular Contributor
  • *
  • Posts: 199
  • Country: ch
Re: C compiler: Local variables overflowing ram?
« Reply #1 on: July 03, 2021, 05:18:04 pm »
Local variables are always stored on the stack by default. So it's really a bad idea to declare such huge local variables on an embedded system.
On a Cortex-M architecture the stack grows down to lower address and eventually grows into the data section where it corrupts the data.

There are several things you can do:
- Don't store huge variables on the stack
- Estimate the expected maximum stack size and increase its size accordingly
- Change the order of memory sections in the linker script so the stack does not "point" towards the data section
- ...

Apart from that its always a good idea to install proper exception handling in the case of a stack overflow.
« Last Edit: July 03, 2021, 05:28:17 pm by Feynman »
 

Offline DavidAlfaTopic starter

  • Super Contributor
  • ***
  • Posts: 6281
  • Country: es
Re: C compiler: Local variables overflowing ram?
« Reply #2 on: July 03, 2021, 05:22:03 pm »
Yeah, I never cared too much about the stack size. Also rarely used huge local variables.
So my approach will be to check the stack usage report, and set the project settings stack allocation a little higher!
That should do it, right?

But why I don't get any warning? I see free ram 700 bytes, max stack cost 920 bytes (Hence the current problem).
Why is nothing checking that, and I must do it manually?
Btw  it's Stm32 Cube IDE (Would that explain a lot of thing? :-DD )
« Last Edit: July 03, 2021, 05:25:12 pm by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 15479
  • Country: fr
Re: C compiler: Local variables overflowing ram?
« Reply #3 on: July 03, 2021, 05:26:35 pm »
I recently added some more variables to the stm32 project and noticed some global variables were overwritten after entering a function that declared a huge local variable.
Then I read the C compiler uses stack to alllocate local variables, so there's no way to check them at compile time.
Is that correct? So it only worked until now because I was lucky and the memory was not full?

A simple example, this compiles perfectly in a stm32F103 with 20KB Ram, although I'm allocating 64KB.
Of course, it crashes. I guess the only way for this to work is to use malloc and check a valid pointer?

Code: [Select]
void test(void){
  volatile uint8_t dat[65536];
   for(uint32_t t=0;t<sizeof(dat);t++){
     dat[t]=GPIOA->IDR;
   }
   for(uint32_t t=0;t<sizeof(dat);t++){
     GPIOB->ODR=dat[t];
   }
}

Yes, local variables are allocated on the stack. That's standard. (Now depending on optimization levels, some local variables may be put in registers, but obviously if they can fit in them. A 64KB array will of course be allocated on the stack.)

To answer your question, first you need to know if you really need this array to be dynamically allocated. In small embedded stuff, it's pretty common that statically allocated arrays will work just fine. In particular, if your function is not meant to be re-entrant, you don't need a dynamic allocation. Now the rationale for dynamic allocation would be of course if you need this particular array only temporarily during the program's execution, and would like to be able to use the corresponding RAM for other dynamic allocations the rest of the time.

Using malloc() in embedded settings is often questioned, too. So you need to consider if the benefits really overweigh the risks.
Another point I would like to stress out regarding checking if the allocation succeeded: for small embedded targets, it's very common that implementations do not handle malloc() failure (in case of not enough RAM available) at all. So you may call malloc() with a large size, the allocation would be impossible, but it will still return a pointer. Often to the beginning of a free block, but the size of which will be much smaller than what you asked for. So you're basically screwed.

So, if you really settle on using malloc(), do check on your particular application that calling it with a size that you know won't fit in RAM effectively returns NULL. This is often not guaranteed on base setups for many MCUs. For this to properly work, you often have to write a custom linker script + implement the _sbrk() "system" function. But check it - if you're lucky and failed malloc() returns NULL in your case, then no need to bother; I'm just pointing out it rarely works "out of the box" with the vendor-provided linker scripts and libraries.

We had a whole thread about estimating stack usage. Since this is something not trivial (or sometimes impossible depending on code flow) to statically estimate (meaning: at compile and link time), it's pretty usual that no check, and thus no warning, will be issued by the linker if you use up too much stack.
 

Offline DavidAlfaTopic starter

  • Super Contributor
  • ***
  • Posts: 6281
  • Country: es
Re: C compiler: Local variables overflowing ram?
« Reply #4 on: July 03, 2021, 05:40:25 pm »
I confused myself with the heap size. As I almost never use malloc, I set it close to 0.
Done! I have learned something, also feeling a little of idiot for such basic mistake  :-DD
Now I use the value from "Max  cost" in the call graph, is that correct?
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf