malloc hangs

Edward.Hartmann
Mon Aug 20, 2018 2:26 pm
If I use malloc() with a size greater than the available memory, the malloc hangs instead of returning an null pointer. Is there any way around this?

heisan
Mon Aug 20, 2018 2:51 pm
[Edward.Hartmann – Mon Aug 20, 2018 2:26 pm] –
…Is there any way around this?

Don’t allocate more than the available memory ;) .

Without an OS to dish out memory, the simpler embedded algorithms don’t have any checks.

If you want some code to implement your own free ram check, there is a recent thread which covers this:
viewtopic.php?f=3&t=4009

But be careful, as the libc malloc is a block allocator, so it will allocate more than you request – how much more depends on a number of heuristics.

EDIT:
The simplest free space check should be something like:
char foo; // this will place a variable on the *bottom* of the stack area
uint32_t freespace = &foo - (char*)sbrk(0); // sbrk(0) will return the *top* of the heap area


Rick Kimball
Mon Aug 20, 2018 3:17 pm
[Edward.Hartmann – Mon Aug 20, 2018 2:26 pm] –
If I use malloc() with a size greater than the available memory, the malloc hangs instead of returning an null pointer. Is there any way around this?

  • You could provide your own malloc /free _sbrk routines. Look in stm32STM32F1/variants/generic_stm32f103c/wirish/syscalls.c
  • You could malloc all the memory you need in setup() and dole it out yourself.
  • You could declare a global uint8_t buffer[8192] array as a global variable and manage its use yourself.
  • You could make a custom c++ class and override the operator new and delete routines. Again snagging all the memory you need up front.
  • You could change platform.txt and use -spec=nano.specs to use the smaller/faster to release newlib malloc routines

heisan
Mon Aug 20, 2018 5:44 pm
It does not look like the STM32 processors have hardware support for stack limits, so you can never have a ‘safe’ memory allocator. There will always be a chance of the stack overflowing the heap.

You can prevent the heap from overflowing the stack with a small change to _sbrk in sycalls.c:
//if ((CONFIG_HEAP_END - pbreak < incr) || // change this to
if (((void*)&ret - pbreak < incr) ||


heisan
Mon Aug 20, 2018 6:19 pm
How about something like this for _sbrk:

volatile int __sbrk_stack_reserve = 256;
void *_sbrk(int incr) {
static void * pbreak = NULL; /* current program break */
void * ret;

if (pbreak == NULL) {
pbreak = CONFIG_HEAP_START;
}

//if ((CONFIG_HEAP_END - pbreak < incr) ||
if (((void*)&ret - pbreak < incr + __sbrk_stack_reserve) ||
(pbreak - CONFIG_HEAP_START < -incr)) {
errno = ENOMEM;
return (void *)-1;
}

ret = pbreak;
pbreak += incr;
return ret;
}


ag123
Tue Aug 21, 2018 12:56 pm
i saw a ‘free stack’ code that’s distributed with sd-fat in util.h

extern "C" char* sbrk(int incr);
static int FreeStack() {
char top = 't';
return &top - reinterpret_cast<char*>(sbrk(0));
}


heisan
Tue Aug 21, 2018 1:23 pm
That will work with nano.specs (libc_s.a), but the standard libc has a block allocator, so it will try to grab more than requested – this is why I proposed a change to _sbrk(). malloc() will eventually call _sbrk() to acquire the memory, so when it returns -1, malloc will return NULL, and hopefully the application will handle that cleanly.

ag123
Tue Aug 21, 2018 1:29 pm
my thoughts are a real malloc() would need the memory book keeping tables for the block allocator, 20k sram is simply ‘too precious’ to keep extra tables to manage the block allocation, this ‘malloc’ basically tries to build a ‘stack’ of blobs in the ‘global’ memory area. strictly speaking, the mcu won’t even bother with that and may overwrite the ‘global’ memory area if it needs it for the stack. well i’m not too sure about this but it’d seem that way

heisan
Tue Aug 21, 2018 1:39 pm
I am researching ways to replace malloc() in a lightweight way. The default malloc() is big (over 5k of flash) and overly complicated for a MCU environment. I just need to figure out how to tell the linker to skip the related symbols in libc.a and use those from a local library. At the moment I weaken the whole libc, but that is not a distributable solution.

ag123
Tue Aug 21, 2018 1:44 pm
i’d think it wouldn’t be libc ‘malloc’ but rather something else, that ‘custom’ malloc would need to do memory book keeping in ways that won’t be the ‘conventional’ malloc. in some ways i’d think it may be quite similar to c++ concept of ‘smart’ pointers

Edward.Hartmann
Wed Aug 22, 2018 11:24 am
Thanks for all the replies, I really don’t need to use malloc and I did not realize the malloc was so large. – I was just trying to use it to determine how much space was available before defining a large array for an event timer queue I am using. I will just continue hand sizing the array as I build up my code to give me the largest safe array size.

ag123
Wed Aug 22, 2018 7:10 pm
i think most of us comes from the ‘big systems’ rather than ’embedded systems’ environment, and it is easy to forget that these mcus are ‘little machines’ which don’t come with a MMU
https://en.wikipedia.org/wiki/Memory_management_unit
there isn’t an OS and more than that there isn’t *virtual memory*, since a long time back when x86 become the 286, 386 with transition to 32 bits is the other addition which is the MMU that creates a 4GB virtual address space, along with that elaborate memory management by the os kernel and libraries etc. that makes it possible for hardware memory protection and more importantly *between processes*

i think stm32 (even including the F1) has an MPU, but the MPU isn’t the MMU and it is somewhat a ‘dark art’ (pretty much ignored as the thoughts is there is (only) 20k of sram, u’d just use whatever there is out there ;) )

i did a little google and run into a few articles about the largely ignored/neglected MPU, i doubt if it is literally in use as that would cost likely more codes and memory usage on bp/mm etc
https://www.embedded.com/electronics-bl … Cortex-MPU
https://www.st.com/resource/en/applicat … 272912.pdf
^^ this doc from ST tells quite abit about the memory architecture of Cortex-M procesors, one of the key things mentioned is “fixed default memory map” (section 2.1 memory model). i.e. no virtual address, hence you can’t create the illusion that the app has all the memory to itself and create elaborate page fault management (e.g. you can’t separate ‘stack’ and global memory, they are in the *same* memory)

given this, i’d think it is quite possible the stack grows downwards and overwrite ‘global’ memory where the global variables are stored (it is 20k of sram after all). if one really want to explore the depths, one may delve deeper into the MPU to see how that may help.

bare metal programming is pretty much a compromise of sorts, pretty much a ‘best effort’ basis given the extremely limited ram etc
i’d think the safest *dynamic* memory is the *stack* (i.e. local variables), for a queue, i used fixed length circular buffer
https://hackaday.com/2015/10/29/embed-w … r-buffers/


Leave a Reply

Your email address will not be published. Required fields are marked *