Why does this alignment attribute have to be specified in a typedef?

950 views Asked by At

I originally wrote this question from my tablet and took a lot of shortcuts in doing so that I think ultimately lead to confusion for people who read and/or attempted to answer the question.

I'm not asking for a solution to the problem I originally started with. If you really want the back story read the next paragraph, otherwise skip it.

What brought this up is some old code operating on an array of data of the form {struct, data, struct, data, ...}, where each data has arbitrary length. The code accesses each struct through a pointer and when we switched to gcc it began crashing in Solaris due to mis-aligned accesses. One idea for solving this was to alter the type's alignment, as demonstrated below, but I'm probably not going to do that.

The questions to be answered can be summarized as:

  • The documentation states alignment cannot be decreased with aligned, but I'm able to do it with a typedef. Is it working as intended?
  • If it's working as intended, why does it require the typedef? Why can't I lower alignment as part of the struct definition?
    • note: it can be done with typedef struct {...}__attribute__((aligned(1))) Typename; as well

Here's a link to some sample code running on wandbox. In case the link goes dead:

#include <cstdio>
#include <assert.h>

#define ALIGN __attribute__((aligned(1)))

struct       Misaligned_1_t { int x; double y; float z; };
struct ALIGN Misaligned_2_t { int x; double y; float z; };
struct       Misaligned_3_t { int x; double y; float z; } ALIGN;

// The gcc documentation indicates that the "aligned" attribute
// can only be used to increase alignment, so I was surprised
// to discover this actually works.  Why does it work?
typedef Misaligned_1_t ALIGN Aligned_t;

int main( int, char** ) {
  char buffer[256];
  // The following is meant to simulate a more complicated scenario:
  //   {SomeStruct, char[arbitrary length], SomeStruct, char[arbitrary length], ...}
  // ... where accessing, using and changing each SomeStruct will result in
  // misaligned accesses.
  auto *m1 = (Misaligned_1_t*)&buffer[1];
  auto *m2 = (Misaligned_1_t*)&buffer[1];
  auto *m3 = (Misaligned_1_t*)&buffer[1];
  auto *a1 = (Aligned_t*)&buffer[1];

  // The documentation says we can only reduce alignment with the "packed" attribute,
  // but that would change the size/layout of the structs.  This is to demonstrate
  // that each type is the same size (and should have the same layout).
  assert(   sizeof(m1) == sizeof(m2)
         && sizeof(m1) == sizeof(m3)
         && sizeof(m1) == sizeof(a1) );

  m1->y = 3.14159265358979323846264; // misaligned access

  std::printf( "%0.16f\n", m2->y ); // misaligned access
  std::printf( "%0.16f\n", m3->y ); // misaligned access
  std::printf( "%0.16f\n", a1->y ); // works fine

  return 0;
}
2

There are 2 answers

0
Brian Vandenberg On BEST ANSWER

I found the answer. I must be blind. From the GCC documentation:

When used on a struct, or struct member, the aligned attribute can only increase the alignment; in order to decrease it, the packed attribute must be specified as well. When used as part of a typedef, the aligned attribute can both increase and decrease alignment, and specifying the packed attribute generates a warning.

5
Rishikesh Raje On

From the gcc help files

You may specify the aligned and transparent_union attributes either in a typedef declaration or just past the closing curly brace of a complete enum, struct or union type definition and the packed attribute only past the closing brace of a definition.

So you can use

struct Test_t {
  int x;
  double y;
  float z;
} __attribute__((aligned(1)));

and then define the variables with

struct Test_t a,b;
struct Test_t *test;

Or you can use the way that is given above. It is the same.