ARM GCC Address of packed struct warning - c

In my code, I have something like this:
#include <stdint.h>
typedef struct __attribute__((packed)) {
uint8_t test1;
uint16_t test2;
} test_struct_t;
test_struct_t test_struct;
int main(void)
{
uint32_t *ptr = (uint32_t*) &test_struct;
return 0;
}
When I compile this using arm-none-eabi-gcc, I get the warning
.\test.c:11:2: warning: converting a packed 'test_struct_t' pointer
(alignment 1) to a 'uint32_t' {aka 'long unsigned int'} pointer
(alignment 4) may result in an unaligned pointer value
[-Waddress-of-packed-member]
Can anyone tell me why this is happening? Taking the address of a packed struct member is of course dangerous. But the whole struct itself should always be aligned, shouldn't it?

There is an answer in the comments, but since it's author didn't post it, I take the liberty to post it myself. All the credit is due to #Clifford.
By default, when the struct is packed, compilers also change alignment of the struct to 1 byte. However, for your case you need the struct to be both packed and aligned as 32-bit unsigned integer. This can be done by changing the packing attribute as following:
#include <stdint.h>
struct __attribute__((packed, aligned(sizeof(uint32_t)))) TestStruct {
uint8_t test1;
uint16_t test2;
};
struct TestStruct test_struct;
int32_t* p = (int32_t*)(&test_struct);
This compiles for ARM platform without any warnings.

In my experience, "packed" structs are almost always a bad idea. They don't always do what people think they do, and they might do other things as well. Depending on compilers, target processors, options, etc., you might find the compiler generating code that uses multiple byte accesses to things that you expect to be 16-bit or 32-bit accesses.
Hardware registers are always going to be properly aligned on a microcontroller. (Bitfields may be a different matter, but you are not using bitfields here.) But there might be gaps or padding.
And the whole idea of trying to access this using a pointer to a uint32_t is wrong. Don't access data via pointer casts like this - in fact, if you see a pointer cast at all, be highly suspicious.
So how do you get a structure that matches a hardware structure exactly? You write it out explicitly, and you use compile-time checks to be sure:
#pragma GCC diagnostic error "-Wpadded"
struct TestStruct {
uint8_t test1;
uint8_t padding;
uint16_t test2;
};
_Static_assert(sizeof(struct TestStruct) == 4, "Size check");
The padding is explicit. Any mistakes will be caught by the compiler.
What if you really, really want an unaligned 16-bit field in the middle here, and you haven't made a mistake in reading the datasheets? Use bitfields:
#pragma GCC diagnostic error "-Wpadded"
struct TestStruct2 {
uint32_t test1 : 8;
uint32_t test2 : 16;
uint32_t padding : 8;
};
_Static_assert(sizeof(struct TestStruct2) == 4, "Size check");
Put the padding in explicitly. Tell the compiler to complain about missing padding, and also check the size. The compiler doesn't charge for the extra microsecond of work.
And what if you really, really, really need to access this as a uint32_t ? You use a union for type-punning (though not with C++) :
union TestUnion {
uint32_t raw;
struct TestStruct2 s;
};

Your packed structure has a size of 3 bytes, and there can be no padding in it. Thus, if we were to create an array of such structures, with the first element having a 4-byte aligned address then, by the definition of arrays (contiguous memory), the second element would be three bytes (sizeof(struct test_struct_t)) from that. Thus, the second element would have only single byte alignment – so, the alignment requirement of your structure is, by deduction, one byte.
On your ARM platform, a unit32_t requires 4 byte alignment, hence the warning.

Related

Accessing unaligned struct member using pointers

I have the following struct:
typedef struct __attribute__ ((__packed__))
{
uint16_t a;
uint32_t b;
} st_t;
-> the "b" member is unaligned.
When I do the following, gcc raises a warning:
st_t st;
uint32_t * b_p = &st.b;
*b_p = 0;
warning log:
taking address of packed member of 'struct <anonymous>' may result in an unaligned pointer value
But when I do the following, It does not raise any warning:
st_t st;
st_t * st_p = &st;
st_p->b = 0;
I don't understand why it does not raise a warning in the second case, since I am still accessing an unaligned member.
Can't provide a standard quote, so someone else will surely write a better answer, but here's a short one.
For a packed struct, from the very definition of what a "packed struct" is, compiler has to generate working machine code for any member access. Unaligned access is known and will be handled appropriately for the CPU.
Pointer to int, however, is assumed to point to a valid int, which may mean it has to be aligned (or there will be "bus error" or some such). Compiler generating valid unaligned access machine code for every int pointer dereference would be quite inefficient on CPUs where it matters, so compiler basically has to make this assumption.
So, if compiler notices a pointer to unaligned address, it rightfully warns, because it is illegal operation on some CPUs, and it may be slower than aligned access even if it is legal. It won't warn for packed struct, because programmer explocitly says "this is unaligned, deal with it" by making the struct packed.
This is a badly designed warning from GCC.
When you apply the packed attribute to a structure, it makes the alignment requirement of its members one byte. Thus, when you take the address of a uint32_t member, what you get is not a uint32_t * but a uint32_t __attribute__ ((__aligned__(1))) *.1 It would be valid to assign this address to a uint32_t __attribute__ ((__aligned__(1))) * or (with a cast) to an unsigned char *.
Thus, ideally, the compiler would not warn you when you take the address of the member but rather when you use it in a way that requires greater alignment than it is guaranteed to have. Implementing such a warning may have been difficult at the time support for packed structures was created and this warning was added.
In this code:
st_t st;
st_t * st_p = &st;
st_p->b = 0;
the address of an st_t is taken and is assigned to an st_t *, so there is no problem. Then st_p->b accesses the member b but does not take its address, so there is no hazard that an unaligned address may be used as a pointer to an aligned address, so no warning is needed.
Footnote
1 GCC’s aligned attribute only allows increasing the alignment requirement of a type, not decreasing it. packed can decrease an alignment requirement, but GCC does not accept it on a plain uint32_t. So the __aligned__(1) notation is used in this answer to convey the intent of a uint32_t with a one-byte alignment requirement, in spite of the fact there appears to be no way to specify this in GCC.

Byte allocation in struct containing struct within C

I have the following structs in a C program.
typedef struct
{
unsigned char msg_id : 8;
unsigned char msg_num : 8;
} Message_Header_Type;
typedef struct
{
Message_Header_Type header;
int msg[32];
} Raw_Message_Type;
What I'm seeing is that the header in the Raw_Message_Type is taking up 4 bytes but I only want it to take only 2. How would I go about doing this?
What you are looking for is struct packing, which is platform and compiler dependent, the details are not specified in the C standard.
With GCC, on a 32-bit platform, you could do the following to get a struct of size 2:
typedef struct __attribute__((__packed__))
{
unsigned char msg_id;
unsigned char msg_num;
} Message_Header_Type;
From the GCC documentation:
packed
This attribute, attached to an enum, struct, or union type definition, specified that the minimum required memory be used to represent the type.
Message_Header_type inside of Raw_Message_Type is still taking 2 bytes as you would expect, but the compiler is padding the structure for alignment reasons, so your Raw_Message_Type is 132 bytes long instead of the 130 bytes you expected. This is probably because 32-bit aligned accesses are more efficient on your processor.
If you are using gcc, you can tell the compiler to align on 2-byte boundaries instead of the 4-byte boundaries that it is using by using #pragma pack.
typedef struct
{
unsigned char msg_id : 8;
unsigned char msg_num : 8;
} Message_Header_Type;
#pragma pack(push, 2) // save current alignment and set to 2-byte boundaries
typedef struct
{
Message_Header_Type header;
int msg[32];
} Raw_Message_Type;
#pragma pack(pop) // restore the previous alignment
Special packing like this is often necessary when using fixed-size structures in files, but be aware that there may be a (light) performance penalty for using a different packing than what the processor prefers. In this specific case, your 32 msg[] ints are now all 2 bytes off from the preferred alignment for your platform.
C implementations are free to insert padding into struct layouts between members, at the end, or both. It is common for them to do so for alignment purposes. It is also common for compilers to provide a mechanism to influence or override the padding, but details are necessarily implementation-specific.
With GCC, for example, you could apply the packed attribute to both your structs:
typedef struct __attribute__((__packed__))
{
unsigned char msg_id : 8;
unsigned char msg_num : 8;
} Message_Header_Type;
typedef struct __attribute__((__packed__))
{
Message_Header_Type header;
int msg[32];
} Raw_Message_Type;
Some other implementations borrow GCC's approach; some use pragmas for the same purpose; and some provide other mechanisms. You'll need to check your compiler's documentation.
Structure objects are not guaranteed the size of the sum of bits used in member bit fields. This is due to padding.
In your case though Raw_Message_Type has another member int msg[32];. It seems logical to use two more bytes for the purpose alignment so that msg[0] can be aligned to a 4-byte boundary.
There is a good chance that
Message_Header_Type obj;
printf("Size : %zu\n",sizeof(obj));
would give you 2 as a the result.

How to override C compiler aligning word-sized variable in struct to word boundary

I have a structure specified as following
Member 1, 16 bits
Member 2, 32 bits
Member 3, 32 bits
which I shall be reading from a file. I want to read straight from the file into the struct.
The problem is that the C compiler will align the variables m1, m2 and m3 to word boundaries which are at 32 bits since I am working on an ARM Cortex M3 for the following struct declaration:
typedef struct
{
uint16_t m1;
uint32_t m2;
uint32_t m3;
}something;
Reading directly from file will put wrong values in m2 and m3, and reads 2 extra bytes too.
I have hacked around and am currently using the following which works just fine:
typedef struct
{
uint16_t m1;
struct
{
uint16_t lo;
uint16_t hi;
}m2;
struct
{
uint16_t lo;
uint16_t hi;
}m3;
}something;
However, this looks like a really dirty hack. I cannot help wishing for a cleaner way to force the compiler to put halves of m2 and m3 in different words, however sub-optimal it may be.
I am using arm-none-eabi-gcc. I know about bit packing, but am unable to work around this optimisation.
Edit: Turns out I didn't know enough about bit-packing :D
What you are looking for is the packed attribute. This will force gcc to not do any padding around members. Taken from the GCC Online docs:
packed
This attribute, attached to an enum, struct, or union type definition, specified that the minimum required memory be used to represent the type.
Specifying this attribute for struct and union types is equivalent to specifying the packed attribute on each of the structure or union members. Specifying the -fshort-enums flag on the line is equivalent to specifying the packed attribute on all enum definitions.
You may only specify this attribute after a closing curly brace on an enum definition, not in a typedef declaration, unless that declaration also contains the definition of the enum.
So what you want is something like:
typedef struct
{
uint16_t m1;
uint32_t m2;
uint32_t m3;
} __attribute__ ((packed)) something;
In addition I would recommend using a compile time assertion check to ensure that the size of the struct is really what you want it to be.
Perhaps #pragma pack(2). That should force the compiler to use 2-byte alignment
You cannot directly read such a struct from a file, and you should never try to. Misalignement can cause traps on certain architecture, and you should not rely on pragma to fix that.
The almost(*) portable way if to read file elements into struct elements unless your are sure that the struct was written with same architecture and alignement (at least compatible) as your are using for reading.
So for your use case, I would recommend :
fread(&something.m1, sizeof(something.m1), 1, fd);
fread(&something.m2, sizeof(something.m2), 1, fd);
fread(&something.m3, sizeof(something.m3), 1, fd);
(*) it is almost portable because it assume that there are no endian problems which can be correct or not depending on your needs. If you are on one single machine or one single architecture it is fine, but if you write struct on a big endian machine and read it on a little endian one, bad things will occur ...
__attribute__ ((aligned (2)));

Error while typecasting structure to integer

I am getting error
error: aggregate value used where an integer was expected
on compiling this code:
#include <stdio.h>
typedef unsigned long U32;
typedef struct hello_s
{
U32 a:8;
U32 b:24;
}hello_t;
int main()
{
hello_t str;
U32 var;
str.a = 0xAA;
str.b = 0xAAA;
var = (U32)str;
printf("var : %lX\n", var);
return 0;
}
Can someone please explain what the error means, and what I am doing wrong.
EDIT: I understand this is a stupid thing to do. What I wanted to know was why the compiler is crying about this. Why cant it just assign the first 32 bits to the integer.
var = (U32)str;
Because str is an object of a structure type and you cannot convert structure objects to object of arithmetic types. C does not let you perform this kind of conversion.
If you want to access you structure object as an integer you can create an union of your structure and of an U32.
Note that the common construct var = *(U32 *) str; is undefined behavior in C. It violates aliasing and alignment rules.
Well, I think one should not mistake the C99 spec by assuming that it is against the language standards.
The standard only says that the results of the conversion may not portable across different machines/architectures.
As long as you have coded the program to work on a particular architecture, it's fine.
For example I group selective members of a data structure DataStr_t that uniquely identifies the object (i.e., the key), packed into another struct, say, DataStrKey_t. I'll make sure that sizeof(DataStrKey_t) is equal to sizeof(uint64) and for all practical purposes use it as uint64 as it is easy to handle.
I also do the below operation often:
memcmp(&var_uint64, &var_DataStructKey, sizeof(uint64));
If you read access or write access the object using the key on a machine the value resulting from conversion is predictable and consistent in bit-wise alignment.
Well if you move "only this data" to a different machine (which actually didn't write the data) and try to read it, things may break.
Your program slightly modified for more explanation and successful compilation:
Here, as long as LINE_A and LINE_B are execute on the same machine result is always predictable.
But if you write the (var_uint64,var_DataStructKey) to a file and read it from a different machine, then execute LINE_B on those populated values, comparison "may" fail.
#include <stdio.h>
#include <string.h>
typedef unsigned long U32;
typedef struct hello_s
{
U32 a:8;
U32 b:24;
}hello_t;
int main()
{
hello_t str;
U32 var;
str.a = 0xAA;
str.b = 0xAAA;
var = *(U32 *)(&str); //LINE_A
if(0 == memcmp(&var, &str, sizeof(U32))) //LINE_B
printf("var : %lu\n", var);
return 0;
}
I guess my answer is too late, but attempted to explain.
And what do you expect that cast to result in exactly? You could always just cast it's address to a pointer to int and dereference it... but are you sure you can safely do so (no, you can't)? Is structure member alignment going to bite you someday (the answer is "probably, yes, it depends")?
Also, from the C99 styandard:
C99 §6.7.2.1, paragraph 10: "The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined."
That is not always a stupid thing to do. In my case, I have a struct that I need to send over a network connection. The data must be sent over an SPI bus in byte form, so the struct must be accessed a byte at a time. I used this define to access each byte. You must be aware of the byte ordering of your platform to do this correctly. Also, you must make sure your structs are __PACKED (see also: C/C++: Force Bit Field Order and Alignment), so the compiler does not insert any padding blocks or alignment blocks. This will also not work if any of the bit members fall across the byte boundaries (at least, with the Microchip XC16 compiler it does not).
typedef unsigned char byte;
#define STRUCT_LB(x) ((byte *)(&x))[0]
#define STRUCT_HB(x) ((byte *)(&x))[1]
A nicer way to do this is to define your struct as a union of a bitfield and a byte array like so:
typedef unsigned char byte;
typedef struct {
union {
struct __PACKED {
byte array[2];
} bytes;
struct __PACKED {
byte b0: 1;
byte b1: 1;
byte b2: 1;
byte b3: 1;
byte b4: 1;
byte other: 3;
byte more: 6;
byte stuff: 2;
} fields;
};
} MyData;
Not all typecasting are allowed in C. Per this manual, only the following cases are legal,
Convert an integer to any pointer type.
Convert a pointer to any integer type.
Convert a pointer to an object to a pointer to another object.
Convert a pointer to a function to a pointer to another function.
Correctness of converting null between pointers (either object or function).
Hence, casting a struct to an integer is obviously not a legal conversion.

long long alignment problem (MSVC vs. GCC)

I'm writing C cross-platform library but eventually I've got error in my unittests, but only on Windows machines. I've tracked the problem and found it's related to alignment of structures (I'm using arrays of structures to hold data for multiple similar objects). The problem is: memset(sizeof(struct)) and setting structures members one by one produce different byte-to-byte result and therefore memcmp() returns "not equal" result.
Here the code for illustration:
#include <stdio.h>
#include <string.h>
typedef struct {
long long a;
int b;
} S1;
typedef struct {
long a;
int b;
} S2;
S1 s1, s2;
int main()
{
printf("%d %d\n", sizeof(S1), sizeof(S2));
memset(&s1, 0xFF, sizeof(S1));
memset(&s2, 0x00, sizeof(S1));
s1.a = 0LL; s1.b = 0;
if (0 == memcmp(&s1, &s2, sizeof(S1)))
printf("Equal\n");
else
printf("Not equal\n");
return 0;
}
This code with MSVC 2003 # Windows produce following output:
16 8
Not equal
But the same code with GCC 3.3.6 # Linux works as expected:
12 8
Equal
This makes my unit-testing very hard.
Am I understand correctly that MSVC uses size of biggest native type (long long) to determine alignment to structure?
Can somebody give me advice how can I change my code to make it more robust against this strange alignment problem? In my real code I'm working with arrays of structures via generic pointers to execute memset/memcmp and I'm usually don't know exact type, I have only sizeof(struct) value.
Your unit test's expectation is wrong. It (or the code it tests) should not scan the structure's buffer byte-by-byte. For byte-precise data the code should create a byte buffer explicitly on stack or on heap and fill it with the extracts from each member. The extracts can be obtained in CPU-endianness-independent way by using the right shift operation against the integer values and casting the result by the byte type such as (unsigned char).
BTW, your snippet writes past s2. You could fix that by changing this
memset(&s2, 0x00, sizeof(S1));
s1.a = 0LL; s1.b = 0;
if (0 == memcmp(&s1, &s2, sizeof(S1)))
to this,
memset(&s2, 0x00, sizeof(S2));
s1.a = 0LL; s1.b = 0;
if (0 == memcmp(&s1, &s2, sizeof(S2)))
but the result is technically "undefined" because the alignment of members in the structures is compiler-specific.
GCC Manual:
Note that the alignment of any given struct or union type is required by the ISO C standard to be at least a perfect multiple of the lowest common multiple of the alignments of all of the members of the struct or union in question.
Also, this typically introduces an element of padding (i.e. filler bytes to have the structure aligned). You can use the #pragma with an argument of packed. Note, #pragmas are NOT a portable way of working. Unfortunately, this is also about the only way of working in your case.
References:
Here GCC on structure alignment.
MSDN structure alignment.
What we have done is used the #pragma pack to specify how big the objects should be:
#pragma pack(push, 2)
typedef struct {
long long a;
int b;
} S1;
typedef struct {
long a;
int b;
} S2;
#pragma pack(pop)
If you do this, the structures will be the same size on both platforms.
Note that this is not a 'strange' alignment problem. MSVC has chosen to ensure that the struct is aligned on a 64-bit boundary since it has a 64-bit member so it adds some padding at the end of the struct to ensure that arrays of those objects will have each element properly aligned. I'm actually surprised that GCC doesn't do the same.
I'm curious what you're unit testing does that hits a snag with this - most of the time alignment of structure members isn't necessary unless you need to match a binary file format or a wire protocol or you really need to reduce the memory used by a structure (especially used in embedded systems). Without knowing what you're trying to do in your tests I don't think a good suggestion can be given. Packing the structure might be a solution, but it comes at some cost - performance (especially on non-Intel platforms) and portability (how struct packing is set up is can be different from compiler to compiler). These may not matter to you, but there might be a better way to solve the problem in your case.
You can either do something like
#ifdef _MSC_VER
#pragma pack(push, 16)
#endif
/* your struct defs */
#ifdef _MSC_VER
#pragma pack(pop)
#endif
to give a compiler directive forcing alignment
Or go into the project options and change the default struct alignment [under Code Generation]
Structure padding for 64-bit values is different on different compilers. I've seen differences between even between gcc targets.
Note that explicitly padding to 64-bit alignment will only hide the problem. It will come back if you begin naively nesting structures, because the compilers will still disagree on the natural alignment of the inner structures.

Resources