1

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;
}
Brian Vandenberg
  • 4,011
  • 2
  • 37
  • 53
  • *"Why does this alignment attribute have to be specified in a typedef?"* Opposed to what? What is the actual problem? – user694733 Dec 16 '16 at 08:29
  • Why doesn't `struct __attribute__((aligned(1))) Test{...}` work? My read of the documentation gave me the impression that I should not be able to lower the alignment like this without `packed`, but since it seems to work I'm curious why it doesn't work without typedef – Brian Vandenberg Dec 16 '16 at 08:31
  • aligned(1) says to align struct to 1 byte? I don't think this has any effect. What effect do you expect? – dbrank0 Dec 16 '16 at 08:32
  • It seems to have the desired effect, but I'm both baffled it works and why it cannot be done without the typedef – Brian Vandenberg Dec 16 '16 at 08:37
  • 1
    And what is the desired effect? To not align structs to their default alignment (probably 8 or so) on stack? – dbrank0 Dec 16 '16 at 08:38
  • The existing code loops over a buffer with `{struct, data, struct, data, ..}`, using a ptr to access the struct. There would normally be misaligned accesses but the sun studio compiler we're going away from works magic to allow people to write even worse code than would normally be possible. – Brian Vandenberg Dec 16 '16 at 08:47
  • The desired effect is: allow the code to continue working without something heavy handed like a compiler flag to allow misaligned accesses – Brian Vandenberg Dec 16 '16 at 08:48
  • What architecture are you compiling for? That attribute has no effect on x86-64 (in your testcase at least). You (can) have unaligned access with attribute or without it. Struct defined on stack with "Test" are still 16-byte (i think) aligned. I think it's just a a pretty useless sanitizer warning (telling you such accesses will be slow). – dbrank0 Dec 16 '16 at 09:18
  • It has an effect. The code I posted is working as intended, and if compiled/executed on Sparc/Solaris it won't crash. If I attempt to apply the attribute to the struct instead of using a typedef then it does not work. – Brian Vandenberg Dec 16 '16 at 09:21
  • Ok. Or maybe it's not so useless. http://stackoverflow.com/questions/28893303/is-a-misaligned-load-due-to-a-cast-undefined-behavior. – dbrank0 Dec 16 '16 at 09:22
  • I'm probably going to have to do a far more extensive fix. If nothing else convinced me, this inconsistency did it. The question I'm looking to get answered, though, is why doesn't it work if applied to he struct itself instead of a typedef? – Brian Vandenberg Dec 16 '16 at 09:31

2 Answers2

2

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.

Rishikesh Raje
  • 8,556
  • 2
  • 16
  • 31
  • Unfortunately even after the closing brace doesn't work in any of the compilers I tried. So far only the typedef has worked – Brian Vandenberg Dec 16 '16 at 08:41
  • It is working on my compiler(cygwin 5.4.0). However, attributes are not part of portable C code, so you should look at the compiler documentation for information as to how to use it. – Rishikesh Raje Dec 16 '16 at 08:45
  • Which compiler & version? I've tried GCC 4.9.2, 5.4, 6.somehing, and clang 3.8 – Brian Vandenberg Dec 16 '16 at 08:51
  • I am getting a size of 24, in all three cases, a. no attributes, b. attribute as given in answer, and c. attribute as applied to typedef. (The correct syntax for that is `typedef struct Test_t __attribute__((aligned(1))) Test;`) – Rishikesh Raje Dec 16 '16 at 09:03
  • I modified my wandbox example to demonstrate the issue. With the attribute as part of the struct it gets misaligned accesses. In Sparc Solaris it crashes: http://melpon.org/wandbox/permlink/MsK5E1zuW4wUScyJ – Brian Vandenberg Dec 16 '16 at 09:06
1

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.

Brian Vandenberg
  • 4,011
  • 2
  • 37
  • 53