-1

I am debugging a bare-metal project on Kinetis TWRK70M120 using Cross ARM GCC toolchain. (Kinetis Design Studio)

I have one question:
Why malloc() doesn't return NULL pointer when it overruns the heap size?
How is that possible?

I would not expect it, but after analyzing different situations it showed up, that malloc() returns NULL pointer only if there is not enough space neither in HEAP nor in STACK. I found this question on StackOverflow forum:

When does malloc return NULL in a bare-metal environment?

They are talking about the HEAP and STACK collision, what could be related with this problem. Although, i am not sure if we can talk about collision even if both (STACK and HEAP) address ranges are managed correctly (it seems to me). For example if i define an local array with 10 integers it will occupy cca 40-48 bytes at the top of the STACK. It means that this part of stack is not available for dynamic allocation and malloc() returns NULL only if you try to allocate address space bigger than cca HEAP+STACK-48bytes. In my project 0x400 + 0x500 - 48bytes(mentioned array) - other local variables. I am really not sure if this is common behavior or i just don't understand correctly to malloc() function.

Here is my code which i used for testing the malloc function.
HEAP size = 0x20000400
HEAP size = 0x20000500
Here is my code which i used for testing the malloc function.

#include "MK70F12.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

extern uint32_t __HeapBase;
extern uint32_t __HeapLimit;
extern uint32_t __StackTop;
extern uint32_t __StackLimit;

//MY TEST
static int j=0, k=0, i=0;   //Global variables

int main(void)
{
    //For testing purposes I defined STACK = 0x0x00000500 (cca 1280bytes) and
                                    //HEAP = 0x0x00000400 (cca 1024bytes) what means that
    //I have approximately (theoretically) 2304 bytes available.

    /* Write your code here */
     void* pHeapBase = ((void*)&__HeapBase);
     void* pHeapLimit= ((void*)&__HeapLimit);
     void* pStackTop = ((void*)&__StackTop);
     void* pStackLimit= ((void*)&__StackLimit);
     //---------------------------------------------------------------------------------------------------------
     char* pMalloc_data1=(char*)malloc(1200*sizeof(char));  //Dynamically allocated array which size is bigger
                                                        //than the HEAP size (beginning address in HEAP space).
                                                        //It seems, when it overruns the heap size it doesn't
                                                        //return NULL, but it continues growing into the stack.
                                                        //Malloc returns NULL pointer only if there is
                                                        //no space available neither in the HEAP nor in the STACK.
     if (pMalloc_data1==NULL)
     {
         return(1);                     //Allocation failed
     }
     for(j=0;j<1200;j++)
     {
         *(pMalloc_data1+j)='a'; //just some initialization with character
                                        //memory filled with 1200 chars
                                        //address range 0x200000a8 - 0x20000559(0x20000558?)
     }
     //---------------------------------------------------------------------------------------------------------

     int *pMalloc_data2 = (int*)malloc(10*sizeof(int)); //Dynamically allocated array
                                                        //(beginning address in STACK space)!!!.
                                                        //Malloc returns NULL pointer only if there is
                                                        //no space available in the STACK (HEAP is already full).
     if (pMalloc_data2==NULL)
     {
         return(1);
     }
     for(i=0;i<10;i++)
     {
         *(pMalloc_data2+i)=i+4000;     //memory filled with 200 integers
                                        //address range 0x20000560 - (theoretically)0x20000588 (0x20000590?)
     }

     int array[10] = {10,15,78,62,80,6528,8,58,64,984};   //Local static array => in STACK
                                                          //For this array is space in STACK reserved
                                                          //at the beginning of the program
     free(pMalloc_data1);
     free(pMalloc_data2);
     for(;;)
     {
         k++;
     }
    return 0;
}
trincot
  • 317,000
  • 35
  • 244
  • 286
pyfilekW
  • 1
  • 1
  • 1
    Answer to your one question: it depends entirely on the implementation of `malloc`. – Jabberwocky Feb 01 '17 at 10:20
  • 1
    You should ask to Free....NX...Qualcomm support cimmunity. – LPs Feb 01 '17 at 10:37
  • Comment is misleading in `for(i=0;i<10;i++) { *(pMalloc_data2+i)=i+4000; //memory filled with 200 integers`. What is true code's intent? – chux - Reinstate Monica Feb 01 '17 at 13:28
  • Unclear about `For this array is space in STACK reserved at the beginning of the program`, What can't the stack space be allocated at that point rather than the beginning of the program? AFAIK, that allocation failed and not the prior `malloc()` calls. A stronger case would put `array[10]` prior to the `malloc()` calls and use those values in code prior and post `malloc()` code. Perhaps printing the address of various allocations and array would illuminate. – chux - Reinstate Monica Feb 01 '17 at 13:32
  • malloc() and baremetal are mutually exclusive. – old_timer Feb 02 '17 at 13:35
  • I think baremetal by definition is "without operating system". So malloc and baremetal are not mutually exclusive, because your development environment provides a C standard library which has malloc, and this is the case with all development environments I have used for Kinetis K70. – BillyJoe Feb 02 '17 at 16:26

1 Answers1

0

With a bare metal environment, you need to implement an _sbrk function like this:

#include <unistd.h>
#include <errno.h>

// start and end of Heap as defined in the linker script (_heap_start < _heap_end)
extern unsigned int const _heap_start;
extern unsigned int const _heap_end;


/**
 * @brief                   Changes data segment space allocation (it may be called by malloc) .
 * @details                 It may be called by malloc() .
 * @param[in]   increment   Number of byte to be allocated .
 * @return                  On success the previous program break (program break = beginning of available mem) .
                            On error -1 .
 */
void *_sbrk(ptrdiff_t increment)
{
    static void *heap_free = 0;
    void *heap_prev;

    if  (heap_free == 0)
    {
        heap_free = (void *) &_heap_start;      // First call
    }

    if ((heap_free + increment) < ((void *) &_heap_end))
    {
        heap_prev = heap_free;
        heap_free += increment;
        return heap_prev;
    }
    else
    {
        errno = ENOMEM;
        return ((void *) -1);
    }
}

On K70 the stack is growing down while the heap is growing up. With that function, you will never have a collision as long as you set _heap_end = _stack_end in your linker script and your stack size is big enough.

When you have an operating system, _sbrk is provided by it.

BillyJoe
  • 878
  • 7
  • 25
  • malloc and _sbrk are not bare metal, they former doesnt exist the latter is specific to one or more C libraries (which are somewhat by definition not baremetal as many require system calls) but is in general not the answer to why malloc() doesnt work. – old_timer Feb 02 '17 at 13:34