When I saw your first question, I thought: stay away from there. Maybe you get it working, but it is fragile: things may go wrong when optimisation changes, or when the compiler (-flags) gets upgraded.
FreeRTOS+TCP uses packed structs to represent IP-packets. I liked it because structs are easy while debugging code: you can see all member values together in a nice table.
It must be said that in most cases the packed attribute (“pack_struct_start.h”) was not necessary, because even without packing the fields would be nicely aligned.
There is only notable exception is:
#include "pack_struct_start.h"
struct xARP_HEADER
{
// 32-byte aligned
MACAddress_t xSenderHardwareAddress; // 6 bytes, aligned
uint8_t ucSenderProtocolAddress[ 4 ]; // 4 bytes, unaligned
MACAddress_t xTargetHardwareAddress; // 6 bytes, aligned
uint32_t ulTargetProtocolAddress; // 4 bytes, aligned
}
#include "pack_struct_end.h"
typedef struct xARP_HEADER ARPHeader_t;
A MAC address is 6 bytes long, and therefore the next field is 2-byte aligned. We turned uint32_t uxSenderProtocolAddress into an array of 4 bytes to avoid alignment errors.
FreeRTOS+TCP uses safe methods to access variables bitwise, for instance in BitConfig.h. BitConfig is used by the DHCPv6 client. It parses arrays of bytes, reading them as either char, int16 or int32. Here is one example:
vBitConfig_write_16( &( xMessage ), pxDHCPMessage->xClientID.usHardwareType );
vBitConfig_write_32( &( xMessage ), pxDHCPMessage->ulTimeStamp );
vBitConfig_write_uc( &( xMessage ), pxEndPoint->xMACAddress.ucBytes, ipMAC_ADDRESS_LENGTH_BYTES );
When using this method, you don’t have to worry about alignment, because all data objects are handled as character arrays.
No worries? Hm, be alert when changing the optimisation level. I have seen that GCC would replace the following (big endian) code:
pucData[ 0 ] = ( uint8_t ) ( ( ulValue >> 24 ) & 0xFFU );
pucData[ 1 ] = ( uint8_t ) ( ( ulValue >> 16 ) & 0xFFU );
pucData[ 2 ] = ( uint8_t ) ( ( ulValue >> 8 ) & 0xFFU );
pucData[ 3 ] = ( uint8_t ) ( ulValue & 0xFFU );
with either:
( ( uint16_t *)pucData )[ 0 ] = ( uint16_t ) ( ( ulValue >> 16 ) & 0xFFFFU );
( ( uint16_t *)pucData )[ 1 ] = ( uint16_t ) ( ulValue & 0xFFFFU );
or with:
*( ( uint32_t *)pucData ) = ulValue;
Both caused a fatal exception.
EDIT
The following function from a gzip library was broken by -Os, optimisation by size:
uint32_t LG(uint8_t *ptr)
{
uint32_t result;
memcpy (&result, ptr, 4);
/*
// Broken version:
uint32_t result;
uint8_t *target = (uint8_t *)&result;
*(target + 0) = *(ptr + 0);
*(target + 1) = *(ptr + 1);
*(target + 2) = *(ptr + 2);
*(target + 3) = *(ptr + 3);
*/
return result;
}
hoping that memcpy() is aware of alignment.