Werden Stapelvariablen durch das GCC __attribute __ ((ausgerichtet (x))) ausgerichtet?

88

Ich habe den folgenden 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]);
}

Und ich habe folgende Ausgabe:

0x7fffbfcd2da0 0x7fffbfcd2da4 0x7fffbfcd2da8 0x7fffbfcd2dac

Warum ist die Adresse von a[0]kein Vielfaches von 0x1000?

Was genau __attribute__((aligned(x)))macht das? Ich habe diese Erklärung falsch verstanden ?

Ich benutze gcc 4.1.2.

Cojocar
quelle

Antworten:

98

Ich glaube, das Problem ist, dass sich Ihr Array auf dem Stapel befindet und Ihr Compiler zu alt ist, um überausgerichtete Stapelvariablen zu unterstützen. GCC 4.6 und höher haben diesen Fehler behoben .

C11 / C ++ 11 Funktioniert alignas(64) float a[4];nur für jede Zweierpotenzausrichtung.
So auch das GNU C, __attribute__((aligned(x)))wie Sie es verwendet haben.

(In C11 #include <stdalign.h>für #define alignas _Alignas: cppref ).


Bei einer sehr großen Ausrichtung an einer 4-KB-Seitengrenze möchten Sie diese möglicherweise nicht auf dem Stapel haben.

Da der Stapelzeiger beim Starten der Funktion alles sein kann, gibt es keine Möglichkeit, das Array auszurichten, ohne viel mehr zuzuweisen, als Sie benötigen, und es anzupassen. (Compiler verwenden and rsp, -4096oder äquivalent und verwenden keines der zugewiesenen 0 bis 4088 Bytes. Eine Verzweigung, ob dieser Speicherplatz groß genug ist oder nicht, wäre möglich, wird jedoch nicht durchgeführt, da große Ausrichtungen viel größer sind als die Größe des Arrays oder anderer lokaler Elemente sind nicht der Normalfall.)

Wenn Sie das Array aus der Funktion in eine globale Variable verschieben, sollte es funktionieren. Das andere, was Sie tun können, ist, es als lokale Variable beizubehalten (was eine sehr gute Sache ist), aber machen Sie es static. Dadurch wird verhindert, dass es auf dem Stapel gespeichert wird. Beachten Sie, dass beide Methoden nicht thread- oder rekursionssicher sind, da nur eine Kopie des Arrays vorhanden ist.

Mit diesem 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]);
}

Ich verstehe das:

0x804c000 0x804c004 0x804c008 0x804c00c

Welches ist, was erwartet wird. Mit Ihrem Originalcode erhalte ich nur zufällige Werte wie Sie.

Zifre
quelle
11
+1 richtige Antwort. Eine alternative Lösung besteht darin, das lokale Array statisch zu machen. Die Ausrichtung auf dem Stapel ist immer ein Problem und es ist am besten, sich daran zu gewöhnen, dies zu vermeiden.
Dan Olson
Oh ja, ich habe nicht daran gedacht, es statisch zu machen. Das ist eine gute Idee, da es Namenskollisionen verhindert. Ich werde meine Antwort bearbeiten.
Zifre
3
Beachten Sie, dass es durch seine statische Aufladung auch nicht wiedereintrittsfähig und nicht threadsicher ist.
ArchaeaSoftware
3
Auch gcc 4.6+ handhabt dies auch auf dem Stapel korrekt.
Textshell
1
Diese Antwort war früher richtig, aber jetzt nicht mehr. gcc ab 4.6, möglicherweise älter, weiß, wie der Stapelzeiger ausgerichtet wird, um C11 / C ++ 11 alignas(64)oder was auch immer auf Objekten mit automatischem Speicher korrekt zu implementieren . Und natürlich GNU C__attribute((aligned((64)))
Peter Cordes
41

Es gab einen Fehler in gcc, der dazu führte, dass das ausgerichtete Attribut nicht mit Stapelvariablen funktioniert. Es scheint mit dem unten verlinkten Patch behoben zu sein. Der unten stehende Link enthält auch einige Diskussionen zu diesem Problem.

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

Ich habe Ihren obigen Code mit zwei verschiedenen Versionen von gcc ausprobiert: 4.1.2 aus einer RedHat 5.7-Box, und er ist ähnlich wie bei Ihrem Problem fehlgeschlagen (die lokalen Arrays waren in keiner Weise auf 0x1000-Byte-Grenzen ausgerichtet). Ich habe dann Ihren Code mit gcc 4.4.6 auf RedHat 6.3 ausprobiert und es hat einwandfrei funktioniert (die lokalen Arrays wurden ausgerichtet). Die Myth TV-Leute hatten ein ähnliches Problem (das der obige gcc-Patch zu beheben schien):

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

Wie auch immer, es sieht so aus, als hätten Sie einen Fehler in gcc gefunden, der in späteren Versionen behoben zu sein scheint.

rts1
quelle
3
Laut dem verknüpften Fehler war gcc 4.6 die erste Version, bei der dieses Problem für alle Architekturen vollständig behoben wurde.
Texthell
Außerdem ist der von gcc generierte Assemblycode zum Erstellen einer ausgerichteten Variablen auf dem Stapel so schrecklich und nicht optimiert. Ist es also sinnvoll, ausgerichtete Variablen auf dem Stapel zuzuweisen, anstatt sie aufzurufen memalign()?
Jérôme Pouiller
13

Aktuelle GCC (getestet mit 4.5.2-8ubuntu4) scheinen wie erwartet zu funktionieren, wenn das Array richtig ausgerichtet ist.

#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]);
}

Ich bekomme:

0x7ffffffefff0 0x7ffffffefff4 0x7ffffffefff8 0x7ffffffefffc
0x7ffffffef000 0x7ffffffef004 0x7ffffffef008 0x7ffffffef00c
0x7ffffffe0000 0x7ffffffe0004 0x7ffffffe0008 0x7ffffffe000c
Caleb Fall
quelle
Dies ist ein wenig überraschend, wenn man bedenkt, dass die Arrays im Stapel zugeordnet sind. Bedeutet dies, dass der Stapel jetzt voller Löcher ist?
Ysap
Oder sein Stapel ist 16 Byte ausgerichtet.
user7116
9

Die Ausrichtung ist nicht für alle Typen wirksam. Sie sollten eine Struktur verwenden, um die Attribute in Aktion zu sehen:

#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]);
}

Und dann lesen Sie:

0x603000 0x604000 0x605000 0x606000

Welches ist, was Sie erwartet hatten.

Edit: Gedrückt von @yzap und folgenden @Caleb Fall Kommentar, ist das ursprüngliche Problem zu GCC - Version wegen nur . Ich habe GCC 3.4.6 gegen GCC 4.4.1 mit dem Quellcode des Anforderers überprüft:

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

Es ist jetzt offensichtlich, dass ältere GCC-Versionen (irgendwo vor 4.4.1) Ausrichtungspathologien aufweisen.

Hinweis 1: Mein vorgeschlagener Code beantwortet nicht die Frage, die ich als "Ausrichten jedes Felds des Arrays" verstanden habe.

Hinweis 2: Wenn Sie ein nicht statisches a [] in main () einfügen und mit GCC 3.4.6 kompilieren, wird die Ausrichtungsanweisung des Arrays von struct unterbrochen, der Abstand zwischen den Strukturen bleibt jedoch 0x1000 ... immer noch schlecht! (Problemumgehungen finden Sie unter @zifre Antwort)

Levif
quelle
2
Wie von zifre beantwortet, ist es nicht der Typ, sondern die Tatsache, dass Sie ihn in Ihrer Version statisch gemacht haben.
Ysap
@ysap, es war sowohl die GCC-Version als auch die globale Definition, die dafür gesorgt hat, dass es funktioniert. Danke für deinen Kommentar! Ich habe die Antwort bearbeitet, um sie zu beheben. :)
Levif