Le variabili dello stack sono allineate dall'attributo __ GCC __ ((align (x)))?


88

Ho il codice seguente:

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

E ho il seguente output:

0x7fffbfcd2da0 0x7fffbfcd2da4 0x7fffbfcd2da8 0x7fffbfcd2dac

Perché l'indirizzo di a[0]non è un multiplo di 0x1000?

Cosa __attribute__((aligned(x)))fa esattamente ? Ho frainteso questa spiegazione?

Sto usando gcc 4.1.2.

Risposte:


98

Credo che il problema sia che il tuo array è nello stack e che il tuo compilatore è troppo vecchio per supportare le variabili dello stack sovrapposte. GCC 4.6 e versioni successive hanno risolto questo bug .

C11 / C ++ 11 alignas(64) float a[4];Funziona solo per qualsiasi potenza di 2 allineamento.
Così fa la GNU C __attribute__((aligned(x)))come la stavi usando.

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


Ma nel tuo caso di un allineamento molto ampio, a un confine di pagina 4k, potresti non volerlo sullo stack.

Poiché il puntatore allo stack potrebbe essere qualsiasi cosa all'avvio della funzione, non è possibile allineare l'array senza allocare molto di più del necessario e regolarlo. (I compilatori useranno and rsp, -4096o equivalgono e non useranno nessuno dei byte da 0 a 4088 allocati; sarebbe possibile ramificare se quello spazio è abbastanza grande o meno ma non viene fatto perché gli allineamenti enormi sono molto più grandi della dimensione dell'array o di altri locali non sono il caso normale.)

Se sposti l'array fuori dalla funzione e in una variabile globale, dovrebbe funzionare. L'altra cosa che potresti fare è mantenerla come variabile locale (che è un'ottima cosa), ma fallo static. Ciò impedirà che venga memorizzato nello stack. Attenzione che entrambi questi metodi non sono thread-safe o ricorsivi, poiché ci sarà solo una copia dell'array.

Con questo codice:

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

Ho capito:

0x804c000 0x804c004 0x804c008 0x804c00c

che è ciò che ci si aspetta. Con il tuo codice originale, ottengo solo valori casuali come hai fatto tu.


11
+1 risposta corretta. Una soluzione alternativa consiste nel rendere statico l'array locale. L'allineamento in pila è sempre un problema ed è meglio prendere l'abitudine di evitarlo.
Dan Olson

Oh sì, non ho pensato di renderlo statico. Questa è una buona idea poiché previene le collisioni di nomi. Modificherò la mia risposta.
Zifre

3
Nota che renderlo statico lo rende anche non rientrante e non thread-safe.
ArchaeaSoftware

3
Anche gcc 4.6+ lo gestisce correttamente anche sullo stack.
textshell

1
Questa risposta era corretta, ma ora non lo è. gcc vecchio come 4.6, forse più vecchio, sa come allineare il puntatore allo stack per implementare correttamente C11 / C ++ 11 alignas(64)o qualsiasi altra cosa su oggetti con memorizzazione automatica. E ovviamente GNU C__attribute((aligned((64)))
Peter Cordes

41

C'era un bug in gcc che faceva sì che l' attributo allineato non funzionasse con le variabili dello stack. Sembra essere stato risolto con la patch collegata di seguito. Il link sottostante contiene anche un bel po 'di discussioni sul problema.

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

Ho provato il tuo codice sopra con due diverse versioni di gcc: 4.1.2 da una scatola RedHat 5.7, e non è riuscito in modo simile al tuo problema (gli array locali non erano in alcun modo allineati su limiti di byte 0x1000). Ho quindi provato il tuo codice con gcc 4.4.6 su RedHat 6.3 e ha funzionato perfettamente (gli array locali erano allineati). La gente di Myth TV ha avuto un problema simile (che la patch gcc sopra sembrava risolvere):

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

Ad ogni modo, sembra che tu abbia trovato un bug in gcc, che sembra essere stato corretto nelle versioni successive.


3
Secondo il bug collegato, gcc 4.6 è stata la prima versione con questo problema completamente risolto per tutte le architetture.
textshell

Inoltre, il codice assembly generato da gcc per creare variabili allineate sullo stack è così orribile e così non ottimizzato. Quindi, ha senso allocare le variabili allineate sullo stack invece di chiamare memalign()?
Jérôme Pouiller

13

Il recente GCC (testato con 4.5.2-8ubuntu4) sembra funzionare come previsto con l'array allineato correttamente.

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

Ottengo:

0x7ffffffefff0 0x7ffffffefff4 0x7ffffffefff8 0x7ffffffefffc
0x7ffffffef000 0x7ffffffef004 0x7ffffffef008 0x7ffffffef00c
0x7ffffffe0000 0x7ffffffe0004 0x7ffffffe0008 0x7ffffffe000c

Questo è un po 'sorprendente, considerando che gli array sono allocati nello stack, significa che lo stack è ora pieno di buchi?
ysap

Oppure il suo stack è allineato a 16 byte.
user7116

9

L'allineamento non è efficace per tutti i tipi. Dovresti considerare l'utilizzo di una struttura per vedere gli attributi in azione:

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

E poi leggerai:

0x603000 0x604000 0x605000 0x606000

Che è quello che ti aspettavi.

Edit: Spinto dal @yzap e seguendo @Caleb caso commento, il problema iniziale è dovuto alla versione GCC solo . Ho controllato GCC 3.4.6 vs GCC 4.4.1 con il codice sorgente del richiedente:

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

È ora ovvio che le versioni precedenti di GCC (da qualche parte prima della 4.4.1) mostrano patologie di allineamento.

Nota 1: il mio codice proposto non risponde alla domanda che ho capito come "allineamento di ogni campo dell'array".

Nota 2: Portare un [] non statico all'interno di main () e compilare con GCC 3.4.6 interrompe la direttiva di allineamento dell'array di struct ma mantiene una distanza 0x1000 tra le strutture ... ancora male! (vedi risposta @zifre per soluzioni alternative)


2
Come ha risposto zifre, non è il tipo, ma il fatto che l'hai reso statico nella tua versione.
ysap

@ysap, è stata sia la versione GCC che la definizione globale a farlo funzionare. Grazie per il tuo commento! Ho modificato la risposta per risolverlo. :)
levif
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.