malloc() не возвращает null при переполнении HEAP (голое железо)

Я отлаживаю проект с голым железом на Kinetis TWRK70M120, используя набор инструментов Cross ARM GCC. (Kinetis Design Studio)

У меня один вопрос:
Why malloc() doesn't return NULL pointer when it overruns the heap size?
How is that possible?

Не ожидал, но после анализа разных ситуаций обнаружился , что malloc() возвращает указатель NULL только в том случае, если не хватает места ни в HEAP, ни в STACK. Я нашел этот вопрос на форуме StackOverflow:

Когда malloc возвращает NULL в чистом окружении?

Они говорят о столкновении HEAP и STACK, что может быть связано с этой проблемой. Хотя я не уверен, что мы можем говорить о коллизии, даже если оба (STACK и HEAP) диапазоны адресов управляются правильно (мне кажется). Например, если я определяю локальный массив с 10 целыми числами, он будет занимать около 40-48 байтов в верхней части стека. Это означает, что эта часть стека недоступна для динамического выделения, и malloc() возвращает NULL, только если вы пытаетесь выделить адресное пространство больше, чем cca HEAP+STACK-48bytes. В моем проекте 0x400 + 0x500 - 48 байт (упомянутый массив) - другие локальные переменные. Я действительно не уверен, что это обычное поведение, или я просто неправильно понимаю функцию malloc().

Вот мой код, который я использовал для тестирования функции malloc.
Размер кучи = 0x20000400
Размер кучи = 0x20000500
Вот мой код, который я использовал для тестирования функции malloc.

#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;
}

person pyfilekW    schedule 01.02.2017    source источник
comment
Ответьте на один ваш вопрос: это полностью зависит от реализации malloc.   -  person Jabberwocky    schedule 01.02.2017
comment
Вы должны попросить Free....NX...Qualcomm поддержать cиммунитет.   -  person LPs    schedule 01.02.2017
comment
Комментарий вводит в заблуждение в for(i=0;i<10;i++) { *(pMalloc_data2+i)=i+4000; //memory filled with 200 integers. Какова цель истинного кода?   -  person chux - Reinstate Monica    schedule 01.02.2017
comment
Неясно насчет For this array is space in STACK reserved at the beginning of the program. Что нельзя выделить в стеке в этой точке, а не в начале программы? Насколько я знаю, это распределение не удалось, а не предыдущие вызовы malloc(). Более сильный случай поместил бы array[10] перед вызовами malloc() и использовал бы эти значения в коде до и после кода malloc(). Возможно, печать адресов различных распределений и массивов подсветила бы.   -  person chux - Reinstate Monica    schedule 01.02.2017
comment
malloc() и baremetal являются взаимоисключающими.   -  person old_timer    schedule 02.02.2017
comment
Я думаю, что baremetal по определению не имеет операционной системы. Таким образом, malloc и baremetal не исключают друг друга, потому что ваша среда разработки предоставляет стандартную библиотеку C, в которой есть malloc, и это относится ко всем средам разработки, которые я использовал для Kinetis K70.   -  person BillyJoe    schedule 02.02.2017


Ответы (1)


В среде «голого железа» вам необходимо реализовать функцию _sbrk следующим образом:

#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);
    }
}

На K70 стек растет вниз, а куча растет. С этой функцией у вас никогда не будет коллизии, если вы установите _heap_end = _stack_end в своем скрипте компоновщика и размер вашего стека достаточно велик.

Если у вас есть операционная система, она предоставляет _sbrk.

person BillyJoe    schedule 01.02.2017
comment
malloc и _sbrk не являются голым металлом, они не существуют, последний специфичен для одной или нескольких библиотек C (которые по определению не являются голым металлом, поскольку многие из них требуют системных вызовов), но в целом не является ответом на то, почему malloc() не работает . - person old_timer; 02.02.2017