1

I am working on some C code on micro processor stm32f103. Since allocating memory from the heap isn't stable, I am not encouraged to use the C library functions malloc() and free() etc. Instead, I thought of declaring a large chunk of static memory in advance during compilation time, and reallocating the memory to suit my pseudo dynamic memory allocation purposes. My new malloc implementation works fine when testing on my computer, but crashes on the stm32 when I do malloc for double data type.

Here is my malloc implementation. I know it is not a real dynamic memory allocation, but i do it just to practice using pointers.

pk_malloc.c

#include "pk_malloc.h"
char pool[RESERVE];
void* alloc[RESERVE];
void mem_init()
{
 for (int i = 0; i != RESERVE; i++)
 {
 alloc[i] = NULL;
 }
}
void* mem_malloc(size_t size)
{
 if (size > 0)
 {
 for (int i = 0; i != RESERVE; i++)
 {
 if (alloc[i] == NULL)
 {
 int end;
 for (end = i; end != RESERVE; end++)
 {
 if (alloc[end] != NULL || end - i == size + 1)
 {
 break;
 }
 }
 if (end - i == size + 1)
 {
 for (int k = i + 1; k != end; k++)
 {
 alloc[k] = &pool[k];
 }
 return alloc[i + 1];
 }
 }
 }
 }
 return NULL;
}
void* mem_realloc(void* mem, size_t new_size)
{
 if (mem == NULL)
 {
 return mem_malloc(new_size);
 }
 int old_size = 0;
 void** alloc_t = &alloc[(char*)(mem) - pool];
 while (*alloc_t != NULL)
 {
 old_size++;
 alloc_t++;
 }
 if (new_size <= old_size)
 {
 mem_free((char*)mem + new_size);
 return mem;
 }
 else
 {
 int i = alloc_t - alloc;
 int size = new_size - old_size;
 int end;
 for (end = i; end != RESERVE; end++)
 {
 if (alloc[end] != NULL || end - i == size + 1)
 {
 break;
 }
 }
 if (end - i == size + 1)
 {
 for (int k = i; k != end - 1; k++)
 {
 alloc[k] = &pool[k];
 }
 return alloc[i];
 }
 else
 {
 void* realloc_t = mem_malloc(new_size);
 if (realloc_t == NULL)
 {
 return mem;
 }
 else
 {
 mem_copy(realloc_t, mem);
 mem_free(mem);
 return realloc_t;
 }
 }
 }
}
void mem_copy(void* dest, void* source)
{
 int dest_index = (char*)(dest) - pool;
 int source_index = (char*)(source) - pool;
 char* writer = (char*)(source);
 while (alloc[source_index] != NULL && alloc[dest_index] != NULL)
 {
 pool[dest_index] = pool[source_index];
 dest_index++;
 source_index++;
 }
}
void mem_free(void* mem)
{
 if (mem != NULL)
 {
 void** alloc_t = &alloc[(char*)(mem) - pool];
 while (*alloc_t != NULL)
 {
 *alloc_t = NULL;
 alloc_t++;
 }
 }
}

pk_malloc.h

#ifndef _PK_MALLOC
#define _PK_MALLOC
#include <stdlib.h>
#define RESERVE 64
void mem_init();
void* mem_malloc(size_t size);
void* mem_realloc(void* mem, size_t new_size);
void mem_copy(void* dest, void* source);
void mem_free(void* mem);
#endif

main.c

int main()
{
 mem_init();
 int* hoho = (int*)(mem_malloc(sizeof(int)));
 *hoho = 123;
 printf("%d", *hoho);
 mem_free(hoho);
}

The code works on my computer, and also works on the STM32. However when I change my datatype to a double:

int main()
{
 mem_init();
 double* hoho = (double*)(mem_malloc(sizeof(double)));
 *hoho = 0.618;
 printf("%f", *hoho);
 mem_free(hoho);
}

It only works on my computer, while it crashed on the STM32.

I did some testing and debugs, I find that this line works, the pointer is not NULL and it has a valid address.

double* hoho = (double*)(mem_malloc(sizeof(double)));

However this line crashed.

*hoho = 0.618;

After more testing, I find any data types occupying more than 4 bytes crashes the same way, including long long etc.

Strangely, I made some user defined structs with lots of int, float data types etc, which definitely occupies more than 4 bytes, the code works fine on the STM32.

struct ABC
{
 int a;
 float b;
};

This line works no problems.

struct ABC* hoho = (struct ABC*)(mem_malloc(sizeof(struct ABC)));

The variable hoho can be assigned, and its members can be accessed without ease, working both on my computer and the STM32.

Please note that all the code works on my laptop, and most data types work for the STM32 too.

I have been stuck with the problem for hours, any help appreciated.

asked Mar 27, 2017 at 16:42
12
  • 5
    There might be alignment requirements for doubles. Commented Mar 27, 2017 at 16:45
  • alignment requirement? does it mean 8 bytes is not enough to store the double? Commented Mar 27, 2017 at 16:47
  • 2
    Yeah, maybe you should try just hacking your code so that all addresses returned are 8-aligned. I would also try 4-aligned to see if that's sufficient. Also, try printing a statically-allocated double just to make sure that printf is not the thing causing a crash. Commented Mar 27, 2017 at 16:50
  • 1
    The error message for the exception / crash could really help us to isolate the issue. Usually the CPU will throw and alignment exception or a SMMU error or something upon bad access. What is this telling you? Also, if this is alignment, just hack your first malloc to make sure that the pointer it returns has a '0' in the address for the last byte- this would be 16-byte aligned and should work for a test. Then use that as a double to check. As to why double and not others? Perhaps the double value is being used by a math co-processor or being shifted to registers by the compiler. Commented Mar 27, 2017 at 17:23
  • 2
    With respect to alignment, do note that the specifications for the standard malloc() and calloc() functions promise that "The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement and then used to access such an object or an array of such objects in the space allocated" (C2011, 7.22.3/1). If your system indeed has alignment requirements larger than 1 for some data types, then your allocator, as presented, cannot make the same promise. Commented Mar 27, 2017 at 17:29

1 Answer 1

1

The core in STM32F1 is a Cortex-M3. This QA here points out that while unaligned word access is allowed by Cortex-M3 for simple instructions, not all instructions support unaligned access. In your case, the C compiler uses an instruction that doesn't support an unaligned address with double.

Notice that

  • the standard library malloc returns a pointer that is "suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement and then used to access such an object or an array of such objects in the space allocated" (C11 7.22.3)

  • while a pointer may be converted to another pointer, "if the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined" (C11 6.3.2.3p7).

Thus the behaviour of a program is undefined already at these lines

int *hoho = mem_malloc(sizeof(int));
double *hoho = mem_malloc(sizeof(double));

if the pointer returned is not suitably aligned for int and double respectively.

To fix the code, change it so that it always returns pointers of proper alignment. The official ARM compilers maintain an 8-byte aligned heap.

answered Mar 28, 2017 at 6:46
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.