I am writing a memory manager for microcontrollers that uses a uint8_t
array for the pool. From this pool, it allocates memory of the requested size to the user.
I am looking at other memory implementations. Contiki has one named mmem. In their documentation they state:
It must be noted that the memory allocated with mmem_alloc() is 1-byte aligned. This is different from what malloc() does. The memory allocated with malloc() is suitably aligned for every data type and the returned void pointer can be safely casted to any other pointer type.
Instead, the pointer to the memory allocated by mmem_alloc() cannot be safely converted to any pointer type other than char*, signed char* or unsigned char*.
This means that if the allocated memory chunk is used to store the contents of a struct type, either the struct must be declared packed or memcpy() must be used. With GCC a packed struct can be specified using the following syntax:
...
So this is a pretty big gotcha that I was never aware of.
A couple of questions:
Is this true for all architectures? I am reading on Wikipedia and they state that most archetectures support unaligned data, it just slows things down.
(For x68): However, there are also instructions for unaligned access such as MOVDQU.
The Contiki link talks about packing the structs as a way to get around the alignment problem. Is there a way to "pack" a
uint16_t
orint32_t
value? I provide macros to the user for common data access, so this could be a possible solution
Do ARM processors support automatic unaligned access? How about x86? Are there really this many gotchas?
Should I just only allow aligned allocation requests, and fill in whatever other data? This would be very annoying for me.
Edit:
Thank you for the helpful answers, however I cannot write something where the user requests alignment because of how the memory manager works (it has to defragment by moving data -- I am not willing to add the complexity of worrying about what alignment some data is, as that will significantly effect the performance of the whole system)
It seems that on both my system (Ubuntu) and the ARM core I am targeting, data alignment is not an issue.
From here:
-munaligned-access
-mno-unaligned-access
Enables (or disables) reading and writing of 16- and 32- bit values from addresses that are not 16- or 32- bit aligned. By default unaligned access is disabled for all pre-ARMv6 and all ARMv6-M architectures, and enabled for all other architectures....
Also, the following code does as you would want it to do on my (x86_64 Ubuntu 14.04) system:
#include "stdio.h"
int main(){
char data[100];
unsigned int *value;
// Some random data
unsigned int check = (unsigned int)0x324FE23A;
// make the pointer unaligned
value = (unsigned int *)(data + 1);
*value = check;
printf("bool=%u, value=%x, check=%x\n", *value==check, *value, check);
return 0;
}
when I compile and run it:
$ cc playground cc align.c && ./a.out
bool=1, value=324fe23a, check=324fe23a
Solution:
I will add a precompiler flag to request that requests are fulfilled in words so that all data is automatically aligned. However, for the systems I am currently targeting this should not be necessary.
Outstanding Question:
If anybody knows a way to do:
#define tm_uint16_p(index) ((uint16_t *)tm_void_p(index))
In a way that it will never matter if tm_void_p(index)
is aligned then that would be great.
Note: the above just converts an index into a void pointer, then casts it as a uint16_t
pointer. Obviously on some systems this will fail due to alignment issues -- is their a way to specify to the compiler to just deal with it?