FreeRTOS, malloc() and SP check with GNU Tools

FreeRTOS has many memory allocation options (see Memory Management) with four ‘schemes’. One of it is the a simple wrapper over the library malloc() and free() routines. I admit, I have not used them, as usually I avoid to include such kind of libraries, as they have their own problems. Anyway, a discussion in the FreeRTOS forum raised my interest: obviously some malloc() implementation (as in the EWL library of CodeWarrior) are making a safety check against the current stack pointer.

sbrk() checks the Stack Pointer

That check is inside alloc.c, function sbrk():

void *sbrk ( uint32_t delta )
{
extern char _end; /* Defined by the linker */
static char *heap_end;
char *prev_heap_end;

  if (heap_end == 0) {
    heap_end = &_end;
  }

  prev_heap_end = heap_end;
  if (prev_heap_end+delta > get_stack_pointer()) {
         return (void *) -1L;
  }
  heap_end += delta;
  return (void *) prev_heap_end;
}

The interesting part is at line 12 where it checks against the current stack pointer. That check is fine in normal environment, but as our tasks (and stack pointer) is inside the heap, that test most likely will fail 😦

Replacing sbrk()

The solution to this is to get rid of that check. But how to do this? Recompiling the library is really painful. But there is an easier way (as I discussed in “Link Order: Using Multiple Definitions with ARM GNU Linker and Eclipse“): to write my version of sbrk() and have it overwrite the one in the library.

For this, I copy the implementation of sbrk() and place it in my application code, with the check on the stack pointer removed/disabled:

void *sbrk ( uint32_t delta )
{
extern char _end; /* Defined by the linker */
static char *heap_end;
char *prev_heap_end;

  if (heap_end == 0) {
    heap_end = &_end;
  }

  prev_heap_end = heap_end;
  //if (prev_heap_end+delta > get_stack_pointer()) {
  //       return (void *) -1L;
  //}
  heap_end += delta;
  return (void *) prev_heap_end;
}

💡 For GNU ARM Embedded (launchpad) tools (as used in Kinetis Design Studio v3.0.0), the name of the function needs to be _sbrk (with an underscore in front).

Luckily, there are no dependencies :-).

Linker

Linking will produce a linker error:

ARM_GCC_Support/ewl/EWL_C/src/stdlib/alloc.c:252: multiple definition of `sbrk'
./Sources/ProcessorExpert.o:C:\wsp_INTRO_10.5\test2\FLASH/../Sources/Application.c:79: first defined here
mingw32-make: *** [test2.elf] Error 1

So I need to add the

-z muldefs

option to the linker settings:

Added muldefs linker option

Added muldefs linker option

And voilà: It is using now my custom sbrk() function, and does not check the stack pointer any more 🙂

Summary

Library malloc() implementation might conflict with the way the heap is used with an operating system like FreeRTOS. Instead to recompile the library, an easier option is to give a custom overwrite of a function to fix the problem.

Happy Mallocing();

16 thoughts on “FreeRTOS, malloc() and SP check with GNU Tools

  1. Hi Erich, Thank you for your great tutorial. Does this work in Kinetis Design Studio? I tried adding the -z multidefs to the CROSS ARM C++ Linker in KDS, but the compiler ignores the flags:

    c:/freescale/kds_1.1.1/toolchain/bin/../lib/gcc/arm-none-eabi/4.8.0/../../../../arm-none-eabi/bin/ld.exe: warning: -z multidefs ignored.

    Also, do you think KDS has a link order feature like CodeWarrior?
    Thank you,
    Sina

    Like

  2. Pingback: Using Kinetis Design Studio V3.0.0 with the Launchad 4.9-2015-q2 Release | MCU on Eclipse

  3. Pingback: Semihosting (again!) with NXP Kinetis SDK V2.0 | MCU on Eclipse

  4. Is the malloc() reentrant? If I use C++ new operator or other C++ functions or call malloc directly, and they are run in the same time, will this cause chaos? What can I do to ensure the thread safety?

    Like

  5. Hi Erich!

    Thanks for sharing, really useful post as we also were facing this issue.
    Two things I want to add: When saying “That check is fine in normal environment, but as our tasks (and stack pointer) is inside the heap, that test most likely will fail” – it’s not (only) related to that. I could have an RTOS which uses static stack allocation for tasks. I need to ensure that the context (i.e. stack) of the stack comes after the heap, otherwise the check will fail. So this check in newlib’s _sbrk takes the assumption that stack always comes after heap (also for non RTOS system a naive assumption)
    Second, avoiding the usage of explicit usage malloc() doesn’t avoid the implicit usage: newlib makes the non-thread safe stdlib APIs (like srand(), rand(), strtok()…) reentrant by adding a wrapper around it. This wrapper makes use of malloc(). So calling one of these APIs calls malloc implicitly.

    Like

    • Hi Christian,
      indeed, good points, thanks for sharing! That’s another reason to avoid these standard ANSI library API calls, and most embedded applications completely avoid usage of the ANSI library because of this. The ‘hack’ for this would be to wrap such calls to srand() etc from the tasks from where they get called.

      Like

  6. I have no heap fail msg now?!
    Is that a good solution? I don’t think so…

    It would be great to have a malloc that could report mem allocation errors.

    PS: the _sbrk function depends on linker flags: –specs= nosys.specs or rdimon.specs

    –basti

    Like

  7. Hi, erich

    Thanks for your sharing, It really solved my critical issue.
    And if some guys has this issue, i think you can define a data region behind heap, and then static allocate stack from this region, call xTaskCreateStack() to pass a static stack to a task.
    so the stack is always located after heap, then malloc() go through.

    /WX

    Like

  8. Pingback: Steps to use FreeRTOS with newlib reentrant Memory Allocation | MCU on Eclipse

  9. what about stm32cubeide?

    void *_sbrk(ptrdiff_t incr)
    {
    extern uint8_t _end; /* Symbol defined in the linker script */
    extern uint8_t _estack; /* Symbol defined in the linker script */
    extern uint32_t _Min_Stack_Size; /* Symbol defined in the linker script */
    const uint32_t stack_limit = (uint32_t)&_estack – (uint32_t)&_Min_Stack_Size;
    const uint8_t *max_heap = (uint8_t *)stack_limit;
    uint8_t *prev_heap_end;

    /* Initialize heap end at first call */
    if (NULL == __sbrk_heap_end)
    {
    __sbrk_heap_end = &_end;
    }

    /* Protect heap from growing into the reserved MSP stack */
    if (__sbrk_heap_end + incr > max_heap)
    {
    errno = ENOMEM;
    return (void *)-1;
    }

    prev_heap_end = __sbrk_heap_end;
    __sbrk_heap_end += incr;

    return (void *)prev_heap_end;
    }

    Like

What do you think?

This site uses Akismet to reduce spam. Learn how your comment data is processed.