It's a problem I ran into in my work. Our embedded platform is a customized Android OS running on quad-core Cortex-A7.
Background: I'm receiving a set of parameters from outside my process, and it's fed to me through a callback that looks something like this:
void some_callback(const void * pdata, size_t n)
{
if (*(const uint8_t*)pdata == PARAM_FP32 && n >= 5)
{
pdata++;
my_variable = *(const float *)pdata; ///< Crashes the application
}
/// Other processing
}
Problem: When directly assigning the type-converted byte sequence to my variable (Note: I couldn't remember where this variable is stored - be it static? global? in stack? in heap?), application crashes.
I replaced the assignment with memcpy(), and it worked fine.
void some_callback(const void * pdata, size_t n)
{
if (*(const uint8_t*)pdata == PARAM_FP32 && n >= 5)
{
pdata++;
memcpy(&my_variable, pdata, sizeof(float)); ///< Worked fine
}
/// Other processing
}
I asked someone in the team and got the following reply: Direct assignment failed because of data alignment issues, the bytes to be assigned are (probably) not 4-byte aligned; assigning to a 4-byte aligned variable causes a bus error.
My questions:
- Is my colleague correct? Is that the actual reason why assignment crashes?
That seems very likely indeed. Accessing data byte per byte or with memcpy means there are no alignment requirements and no instructions for such are generated. Whereas accessing data as int, float etc often means that the pointer needs to point at an address with proper alignment for the type used.
Please also note that while C allows wild & crazy pointer conversions, it does not allow wild & crazy data conversions. You can't just take a chunk of raw bytes and cast a pointer that raw data into some other type. This may not only cause alignment problems, but also "strict aliasing" problems which is a bug that happens when the programmer takes liberties with C's type system which are actually not guaranteed to be safe by the C language. Both misaligned access and strict aliasing violations are forms of undefined behavior - serious bugs where anything can happen.
To know if this was a misaligned access and/or strict aliasing violation, we need to see the caller code and variable declarations.
As for pdata++ it is not valid C since we aren't allowed to do pointer arithmetic on void pointers. Some compilers support this as a dangerous non-standard extension. If you are using for example gcc, make sure to compile with -std=c11 -pedantic-errors, where c11 is one's favorite flavor of the C language. These two options block non-standard, non-portable, dangerous GNU C crap from compiling. You shouldn't be using GNU C in a professional context.
With the dangerous gcc extension present, it treats void pointers as if they are character pointers and ++ increments one char item, not one float item. So that will cause misaligned access in case the intention was to increase by one float.
- Why am I not experiencing this issue in MCU development - like with STM32/AVR?
Different targets have different alignment requirements. Some support misaligned access, some don't, and some others don't even have alignment requirements.
Also, many embedded system compilers do not implement optimizations that assume that no strict aliasing violations are present. This is mainly a problem in the gcc compiler but maybe also in clang. If you aren't using gcc for MCU development then that could be why.
Other than that, it may be a dormant bug in your compiler port for the Cortex A target or maybe you compile with different optimization settings etc.
As for AVR, it's an ancient 8-bitter and so it doesn't have alignment requirements.
- Is there another way of doing the data copy correctly, other than using memcpy() ?
What you should probably be doing no matter system is to copy the data using the actual type which that data was declared as. Again - that's where you will find the source of all your problems, in the actual data declaration, which you didn't post.
Also ask yourself why you are doing type-generic programming with void pointers to begin with - is it really necessary and if so is this the best way to do it?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With