Are Packed Structs Portable

Are packed structs portable?

You should never use structs across compile domains, against memory (hardware registers, picking apart items read from a file or passing data between processors or the same processor different software (between an app and a kernel driver)). You are asking for trouble as the compiler has somewhat free will to choose alignment and then the user on top of that can make it worse by using modifiers.

No there is no reason to assume you can do this safely across platforms, even if you use the same gcc compiler version for example against different targets (different builds of the compiler as well as the target differences).

To reduce your odds of failure start with the largest items first (64 bit then 32 bit the 16 bit then lastly any 8 bit items) Ideally align on 32 minimum perhaps 64 which one would hope arm and x86 do, but that can always change as well as the default can be modified by whomever builds the compiler from sources.

Now if this is a job security thing, sure go ahead, you can do regular maintenance on this code, likely going to need a definition of each structure for each target (so one copy of the source code for the structure definition for ARM and another for x86, or will need this eventually if not immediately). And then every or every few product releases you get to be called in to do work on the code...Nice little maintenance time bombs that go off...

If you want to safely communicate between compile domains or processors the same or different architectures, use an array of some size, a stream of bytes a stream of halfwords or a stream of words. Significantly reduces your risk of failure and maintenance down the road. Do not use structures to pick apart those items that just restores the risk and failure.

The reason why folks seem to think this is okay because of using the same compiler or family against the same target or family (or compilers derived from other compilers choices), as you understand the rules of the language and where the implementation defined areas are you will eventually run across a difference, sometimes it takes decades in your career, sometimes it takes weeks...Its the "works on my machine" problem...

What other compilers do I need to worry about struct packing?

The premise of your question is mistaken. Packed structs are not something you're "supposed to use" to save space. They're a dubious nonstandard feature with lots of problems that should be avoided whenever possible. (Ultimately it's always possible to avoid them, but some people balk at the tradeoffs involved.) For example, whenever you use packed structures, any use of pointers to members is potentially unsafe because the pointer value is not necessarily a valid (properly aligned) pointer to the type it points to. The only time there is a "need" for packed structures is when you're using them to access memory-mapped hardware registers that are misaligned, or to access in-file/on-disk data structures that are misaligned (but the latter won't be portable anyway since representation/endianness may not match, and both problems are solved together much better with a proper serialization/deserialization function).

If your goal is to save space, then as long as you control the definition of the structure, simply order it so as not to leave unnecessary padding space. This can be achieved simply by ordering the members in order of decreasing size; if you do that, then on any reasonable implementation, the wasted space will be at most the difference between the size of the largest member and the size of the smallest.

Are unpacked struct in packed struct automatically packed?

No, the inner structure is not packed. In this Godbolt example, we can see that struct foo is not packed inside struct bar, which has the packed attribute; the struct bar object created contains three bytes of padding (visible as .zero 3) inside its struct foo member, between the struct foo members c and i.

Current documentation for GCC 10.2 explicitly says the internal layout of a member of a packed structure is not packed (because of the attribute on the outer structure; it could be packed due to its own definition, of course).

(In older documentation that said that applying packed to a structure is equivalent to applying it to its members, it meant the effect of applying packed to the “variable” that is the member, described in the documentation for variable attributes. When packed is applied to a structure member, it causes the member’s alignment requirement to be one byte. That is, it eliminates padding between previous members and that member, because no padding is needed to make it aligned. It does not alter the representation of the member itself. If that member is an unpacked structure, it remains, internally, an unpacked structure.)

When should we not use #pragma pack?

Firmware developer here. #pragma pack is very familiar territory. I'll explain.

In general you should not use #pragma pack. Yes, it will make your structures smaller in memory since it eliminates all padding between struct members. But it can make accessing those members much more expensive since the members may no longer fall along their required alignment. For example, in ARM architectures, 4-byte ints are typically required to be 4-byte aligned, but in a packed struct they might not be. That means the compiler needs to add extra instructions to safely access that struct member, or the developer has to access it byte-by-byte and reconstruct the int manually. Either way it results in more code than an aligned access, so your struct ends up smaller but your accessing code potentially ends up slower and larger.

You should use #pragma pack when your structure must match an exact data layout. This typically happens when you are writing code to match a data transport or access specification... e.g., network protocols, storage protocols, device drivers that access HW registers. In those cases you may need #pragma pack to force your structures to match the spec-defined data layout. This will possibly incur the same performance penalty mentioned in the previous paragraph, but may be the only way to comply with the specification.

Portable alternative for packed structures with a flexible array member

[[gnu::packed]] is a terrible way to do serialization between arbitrary architectures. There are still big-endian machines out there.

The right way to do this is to define a serialization format in terms of octets, and then convert between an octet stream (to or from the UDP socket), and a nicely arranged structure.

There are many libraries which make this straightforward. We use protobuf at work; a previous employer had a home-brewed solution. (Note that a request for a recommendation for such a library is explicit off-topic for Stack Overflow.)

Portable way to find size of a packed structure in C

sizeof is the portable way to find the size of a struct, or of any other C data type.

The problem you're facing is how to ensure that your struct has the size and layout that you need.

#pragma pack or __attribute__((packed)) may well do the job for you. It's not 100% portable (there's no mention of packing in the C standard), but it may be portable enough for your current purposes, but consider whether your code might need to be ported to some other platform in the future. It's also potentially unsafe; see this question and this answer.

The only 100% portable approach is to use arrays of unsigned char and keep track of which fields occupy which ranges of bytes. This is a lot more cumbersome, of course.

How to properly access packed struct members

Is it incorrect to access misaligned pointer data but okay to use it to initialize a properly aligned type? (as in bar).

As far as the C++ language is concerned, there is no such thing as a packed class nor such thing as improperly aligned object. Hence, an improperly aligned pointer would necessarily be invalid.

Whether your compiler that provides a language extension for packed classes also extends the language to allow access through misaligned pointers is up for your compiler vendor to document. The warning implies that latter extension might not be supported.

Between foo and bar, is one approach better than the other?

bar, as per the warning.

Should we copy packed struct individual members to properly aligned data structure and then use it? That would imply that for almost every packed data structure there is a non-packed data structure and packed structure remains confined to serialization layer.

That could be a convenient solution to confine the non-standard packed classes into the serialisation layer.

Note that this isn't the only problem with packed structs. Another problem is portability of serialised data between systems due to different byte orders and sizes of types.

A portable way to serialise data is to not use packed structs at all, but rather shift bytes individually using explicit offsets.



Related Topics



Leave a reply



Submit