Be careful with packed structures!

If you use the C language, you may have probably wanted to pack your structures so no alignment is done by the compiler. This can be useful for example to build network packets. You can find the basics of packed structures using GCC, here or here.

If everything seems clear, why am I writing this? Today, a coworker has found a bug related to packed structures. The issue was with internal structures (i.e. a substructure). A year ago, or so, I knew that substructures were not packed even if its enclosing structure is packed, but it seems I forgot about it, so I have decided that it was worth writing it here so I do not forget it again (I’m sure I will).

I will take the example found in GCC documentation (only since version 3.4.0). Suppose you have the following code:

struct my_unpacked_struct
{
  char c;
  int i;
};

struct my_packed_struct
{
  char c;
  int  i;
  struct my_unpacked_struct s;
} __attribute__ ((__packed__));

struct my_packed_struct my = {
  .c = 10,
  .i = 20,
  .s.c = 30,
  .s.i = 40
};

If we generate the assembly for this (I have omitted some things not needed for the example), we will get:

        .globl _my
        .data
_my:
        .byte   10  <--- c
        .long   20  <--- i
        .byte   30  <--- s.c
        .space 3    <--- 3 bytes of alignment
        .long   40  <--- s.i

As you can see, the compiler has not aligned the internal structure, but the enclosing one. So, what you need to do if you want the internal structure also packed is to pack my_unpacked_struct:

struct my_unpacked_struct
{
  char c;
  int i;
} __attribute__ ((__packed__));

Now, we get what we initially expected:

        .globl _my
        .data
_my:
        .byte   10  <--- c
        .long   20  <--- i
        .byte   30  <--- s.c
        .long   40  <--- s.i

Packing the whole structure my_unpacked_struct is fine if you do not use it anywhere else, but it would be great to use variable attributes (we have used type attributes so far), so we could only pack the internal substructure variable like this (it doesn’t work):

struct my_packed_struct
{
  char c;
  int  i;
  struct my_unpacked_struct s __attribute__ ((__packed__));
} __attribute__ ((__packed__));

Update 2007/07/25: read the first comment to understand why the variable attribute is not working in this case.

By the way, in the example I have initialized the structure my using designated initializers.

And remember, be careful with packed structures!

About these ads

4 Responses to “Be careful with packed structures!”

  1. drj11 Says:

    It pretty much has to work this way. Consider what happens when I have a random pointer to the unpacked struct:

    struct my_unpacked_struct *p;
    p = …;
    printf(“%d\n”, p->i);

    When the compiler is generating code for «p->i» it has to know what the offset is to get from the beginning of the struct to the “i” field. This is a fact about the struct, not about where the struct is. The compiler can’t tell whether the pointer p points to an “ordinary” struct my_unpacked_struct or one that is embedded inside another (packed) struct. There’s no difference. The pointer doesn’t stored any information that will allow it tell.

    That’s why embedding a struct can’t change how the embedded struct is packed.

    There’s another reason you should be careful with packed structures: they’re non-standard.

  2. aleix Says:

    I see… this is why variable attribute is not working for the internal structure, so you are forced to pack the type if you want it to be packed.

    As you say, this is a non-standard feature (even most compilers support it), and of course it has a lot of performance penalties as the compiler needs to generate code to unpack unaligned data. We’re using it to easily represent network packets in C, and we found that it was a pretty good way (at list for writing the code to manage packet fields).

    Thanks!

  3. nico Says:

    So you’re doing something like that:
    struct my_packed_struct s;
    read_from_net(&s, sizeof(s));
    /* … */
    do_stuff(s.i)
    ? That will crash on everything but on x86 architectures, because usually you can’t read integers on non-word boundaries. Packed structures are very unportable.

  4. aleix Says:

    Yes, it’s true that packed structures are not really recommended because of portability. I’m using them on an embedded system with an SPARC CPU and they work fine (in this project), I don’t really see why your code doesn’t work (what is the read_from_net function doing?). If you have a buffer with the data coming from the network and just memcpy it to the address of your packed structure it works fine, you only need to take into account the endianess, all the rest (field alignment) is automatically done by the compiler.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


Follow

Get every new post delivered to your Inbox.

%d bloggers like this: