Werden Stack-Variablen durch das GCC-__attribute__((aligned(x))) ausgerichtet?

Lesezeit: 6 Minuten

Benutzeravatar von cojocar
cojocar

Ich habe 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 die Adresse von a[0] ist kein Vielfaches von 0x1000?

Was genau __attribute__((aligned(x))) tut? ich habe es falsch verstanden Dies Erläuterung?

Ich verwende gcc 4.1.2.

Benutzeravatar von Zifre
Zifre

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

C11/C++11 alignas(64) float a[4]; Funktioniert einfach für jede Potenz von 2 Ausrichtung.
So auch GNU C __attribute__((aligned(x))) wie du es benutzt hast.

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


Aber in Ihrem Fall einer sehr großen Ausrichtung an einer 4k-Seitengrenze möchten Sie sie möglicherweise nicht auf dem Stapel haben.

Da der Stapelzeiger beim Start der Funktion beliebig sein kann, gibt es keine Möglichkeit, das Array auszurichten, ohne viel mehr als nötig zuzuordnen und anzupassen. (Compiler werden and rsp, -4096 oder gleichwertig und verwenden Sie keines der zugewiesenen 0 bis 4088 Bytes; Eine Verzweigung, ob dieser Platz groß genug ist oder nicht, wäre möglich, wird aber nicht durchgeführt, da große Ausrichtungen, die viel größer als die Größe des Arrays sind, oder andere lokale nicht der Normalfall sind.)

Wenn Sie das Array aus der Funktion in eine globale Variable verschieben, sollte es funktionieren. Die andere Sache, die Sie tun könnten, ist, es als lokale Variable zu behalten (was eine sehr gute Sache ist), aber es zu machen static. Dadurch wird verhindert, dass es auf dem Stack gespeichert wird. Beachten Sie, dass diese beiden Methoden weder Thread-sicher noch rekursionssicher sind, da es nur eine Kopie des Arrays gibt.

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 bekomme das:

0x804c000 0x804c004 0x804c008 0x804c00c

was erwartet wird. Mit Ihrem ursprünglichen Code erhalte ich nur zufällige Werte wie Sie.

  • +1 richtige Antwort. Eine alternative Lösung besteht darin, das lokale Array statisch zu machen. Die Ausrichtung auf dem Stack ist immer ein Problem und es ist am besten, es sich zur Gewohnheit zu machen, es zu vermeiden.

    – Dan Olson

    8. Mai 2009 um 19:47 Uhr

  • 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

    8. Mai 2009 um 19:49 Uhr

  • Beachten Sie, dass es durch die statische Einstellung auch nicht-reentrant und nicht-threadsicher wird.

    – ArchaeaSoftware

    30. September 2013 um 17:59 Uhr

  • Auch gcc 4.6+ handhabt dies sogar auf dem Stack korrekt.

    – Texthülle

    26. Dezember 2015 um 14:54 Uhr

  • Früher war diese Antwort richtig, jetzt ist sie es nicht mehr. gcc so alt wie 4.6, vielleicht älter, weiß, wie man den Stapelzeiger ausrichtet, um C11 / C++11 korrekt zu implementieren alignas(64) oder was auch immer auf Objekten mit automatischer Speicherung. Und natürlich GNU C __attribute((aligned((64)))

    – Peter Cordes

    15. Januar 2019 um 5:10 Uhr


Es gab einen Fehler in gcc, der verursachte Attribut ausgerichtet, um nicht mit Stack-Variablen zu arbeiten. Es scheint mit dem unten verlinkten Patch behoben zu sein. Der Link unten enthält auch ziemlich viel Diskussion für das 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 von einer RedHat 5.7-Box, und es ist ähnlich wie bei Ihrem Problem fehlgeschlagen (die lokalen Arrays waren in keiner Weise an 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 gcc-Patch oben 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.

  • Laut dem verlinkten Fehler war gcc 4.6 die erste Version mit diesem Problem, das für alle Architekturen vollständig behoben wurde.

    – Texthülle

    26. Dezember 2015 um 14:53 Uhr

  • Abgesehen davon ist der von gcc generierte Assembler-Code zum Erstellen ausgerichteter Variablen auf dem Stapel so schrecklich und so nicht optimiert. Ist es also sinnvoll, ausgerichtete Variablen auf dem Stapel zuzuweisen, anstatt sie aufzurufen? memalign()?

    – Jérôme Pouiller

    17. September 2018 um 12:14 Uhr

Neuere 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

  • Dies ist ein wenig überraschend, wenn man bedenkt, dass die Arrays im Stapel zugewiesen sind. Bedeutet dies, dass der Stapel jetzt voller Löcher ist?

    – jap

    23. März 2012 um 9:52 Uhr

  • Oder sein Stapel ist 16-Byte-ausgerichtet.

    – Benutzer7116

    16. Januar 2013 um 20:58 Uhr

Benutzeravatar von levif
levif

Die Ausrichtung ist nicht für alle Typen wirksam. Sie sollten erwägen, eine Struktur zu 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 liest du:

0x603000 0x604000 0x605000 0x606000

Was Sie erwartet haben.

Bearbeiten:
Von @yzap und nach dem Kommentar von @Caleb Case gepusht, ist das anfängliche Problem auf die GCC-Version zurückzuführen nur. Ich habe GCC 3.4.6 im Vergleich zu 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 zeigen.

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

Anmerkung 2: Bringen Sie nicht-statische a[] innerhalb von main() und das Kompilieren mit GCC 3.4.6 unterbricht die Ausrichtungsdirektive des Arrays von Structs, behält aber einen Abstand von 0x1000 zwischen Structs bei … immer noch schlecht! (Siehe @zifre-Antwort für Problemumgehungen)

1419700cookie-checkWerden Stack-Variablen durch das GCC-__attribute__((aligned(x))) ausgerichtet?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy