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.