Are stack variables aligned by the GCC __attribute__((aligned(x)))?
Asked Answered
W

4

90

i have the following code:

#include <stdio.h>

int
main(void)
{
        float a[4] __attribute__((aligned(0x1000))) = {1.0, 2.0, 3.0, 4.0};
        printf("%p %p %p %p\n", &a[0], &a[1], &a[2], &a[3]);
}

And i have the following output:

0x7fffbfcd2da0 0x7fffbfcd2da4 0x7fffbfcd2da8 0x7fffbfcd2dac

Why the address of a[0] is not a multiple of 0x1000?

What exactly __attribute__((aligned(x))) does? I misunderstood this explanation?

I'm using gcc 4.1.2.

Walliw answered 8/5, 2009 at 19:34 Comment(0)
G
101

I believe the problem is that your array is on the stack, and that your compiler is too old to support over-aligned stack variables. GCC 4.6 and later fixed that bug.

C11/C++11 alignas(64) float a[4]; Just Works for any power of 2 alignment.
So does the GNU C __attribute__((aligned(x))) as you were using it.

(In C11, #include <stdalign.h> for the #define alignas _Alignas: cppref).


But in your case of a very large alignment, to a 4k page boundary, you may not want it on the stack.

Because the stack pointer could be anything when the function starts, there is no way to align the array without allocating a lot more than you need and adjusting it. (Compilers will and rsp, -4096 or equivalent and not use any of the 0 to 4088 bytes that allocated; branching on whether that space is large enough or not would be possible but isn't done because huge alignments much larger than the size of the array or other locals are not the normal case.)

If you move the array out of the function and into a global variable, it should work. The other thing you could do is keep it as a local variable (which is a very good thing), but make it static. This will prevent it from being stored on the stack. Beware that both of these ways are not thread-safe or recursion-safe, since there will be only one copy of the array.

With this code:

#include <stdio.h>

float a[4] __attribute__((aligned(0x1000))) = {1.0, 2.0, 3.0, 4.0};

int
main(void)
{
        printf("%p %p %p %p\n", &a[0], &a[1], &a[2], &a[3]);
}

I get this:

0x804c000 0x804c004 0x804c008 0x804c00c

which is what is expected. With your original code, I just get random values like you did.

Granada answered 8/5, 2009 at 19:41 Comment(7)
+1 correct answer. An alternate solution is to make the local array static. Alignment on the stack is always a problem and it's best to get into the habit of avoiding it.Wherewithal
Oh yes, I didn't think of making it static. That is a good idea since it prevents name collisions. I will edit my answer.Granada
Note that making it static also makes it non-reentrant and non-thread-safe.Alice
Also gcc 4.6+ handle this correctly even on the stack.Eppie
What is the difference between __attribute__((aligned(0x1000))) and __align(0x1000)?Treed
In macOS the compiler align any array to 16 Byte. Does GCC do that as well on 64 Bit system?Bergmans
This answer used to be correct, but now it's not. gcc as old as 4.6, maybe older, knows how to align the stack pointer to correctly implement C11 / C++11 alignas(64) or whatever on objects with automatic storage. And of course GNU C __attribute((aligned((64)))Escutcheon
G
42

There was a bug in gcc that caused attribute aligned to not work with stack variables. It appears to be fixed with the patch linked below. The link below also contains quite a bit of discussion for the problem as well.

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=16660

I have tried your code above with two different versions of gcc: 4.1.2 from a RedHat 5.7 box, and it failed similarly to your problem (the local arrays wre in no way aligned on 0x1000 byte boundaies). I then tried your code with gcc 4.4.6 on RedHat 6.3, and it worked flawlessly (the local arrays were aligned). The Myth TV folks had a similar problem (that the gcc patch above seemed to fix):

http://code.mythtv.org/trac/ticket/6535

Anyway, it looks like you found a bug in gcc, that appears to be fixed in later versions.

Gonfalon answered 15/10, 2012 at 22:17 Comment(2)
According to the linked bug gcc 4.6 was the first release with this problem fully fixed for all architectures.Eppie
Beside that, assembly code generated by gcc to create aligned variable on stack is so horrible and so unoptimized. So, does it make sense to allocate aligned variables on stack instead of calling memalign()?Tersanctus
B
13

Recent GCC (tested with 4.5.2-8ubuntu4) appear to work as expected with the array aligned properly.

#include <stdio.h>

int main(void)
{
    float a[4] = { 1.0, 2.0, 3.0, 4.0 };
    float b[4] __attribute__((aligned(0x1000))) = { 1.0, 2.0, 3.0, 4.0 };
    float c[4] __attribute__((aligned(0x10000))) = { 1.0, 2.0, 3.0, 4.0 };

    printf("%p %p %p %p\n", &a[0], &a[1], &a[2], &a[3]);
    printf("%p %p %p %p\n", &b[0], &b[1], &b[2], &b[3]);
    printf("%p %p %p %p\n", &c[0], &c[1], &c[2], &c[3]);
}

I get:

0x7ffffffefff0 0x7ffffffefff4 0x7ffffffefff8 0x7ffffffefffc
0x7ffffffef000 0x7ffffffef004 0x7ffffffef008 0x7ffffffef00c
0x7ffffffe0000 0x7ffffffe0004 0x7ffffffe0008 0x7ffffffe000c
Bolometer answered 4/5, 2011 at 2:36 Comment(2)
This is a little surprising, considering the arrays are allocated in the stack - does it mean that the stack is now full of holes?Zygote
Or his stack is 16-byte aligned.Selfdrive
T
9

Alignement is not effective for all types. You should consider using a structure to see the attributes in action:

#include <stdio.h>

struct my_float {
        float number;
}  __attribute__((aligned(0x1000)));

struct my_float a[4] = { {1.0}, {2.0}, {3.0}, {4.0} };

int
main(void)
{
        printf("%p %p %p %p\n", &a[0], &a[1], &a[2], &a[3]);
}

And then, you'll read:

0x603000 0x604000 0x605000 0x606000

Which is what you were expecting.

Edit: Pushed by @yzap and following @Caleb Case comment, the initial problem is due to GCC version only. I've checked on GCC 3.4.6 vs GCC 4.4.1 with the requester's source code:

$ ./test_orig-3.4.6
0x7fffe217d200 0x7fffe217d204 0x7fffe217d208 0x7fffe217d20c
$ ./test_orig-4.4.1
0x7fff81db9000 0x7fff81db9004 0x7fff81db9008 0x7fff81db900c

It's now obvious that older GCC versions (somewhere before 4.4.1) shows alignment pathologies.

Note 1: My proposed code doesn't answer the question which I understood as "aligning each field of the array".

Note 2: Bringing non-static a[] inside main() and compiling with GCC 3.4.6 breaks the alignment directive of the array of struct but keeps 0x1000 distance between structs... still bad ! (see @zifre answer for workarounds)

Tropophilous answered 12/11, 2010 at 17:5 Comment(2)
As answered by zifre, it is not the type, but the fact that you made it static in your version.Zygote
@ysap, it was both GCC version and global definition that made it works. Thanks for you comment ! I edited the answer to fix it. :)Tropophilous

© 2022 - 2024 — McMap. All rights reserved.