2009-06-30 16 views
40

Ho cercato di comprendere meglio come i compilatori generano il codice macchina e in particolare come GCC gestisce lo stack. Così facendo ho scritto programmi C semplici, li ho compilati in assemblea e ho fatto del mio meglio per capirne l'esito. Ecco un semplice programma e l'uscita che genera:Assegnazione, riempimento e allineamento dello stack

asmtest.c:

void main() { 
    char buffer[5]; 
} 

asmtest.s:

pushl %ebp 
movl %esp, %ebp 
subl $24, %esp 
leave 
ret 

Che cosa sconcertante per me è il motivo per cui 24 byte vengono allocate per lo stack. So che a causa del modo in cui il processore indirizza la memoria, lo stack deve essere allocato in incrementi di 4, ma se questo fosse il caso, dovremmo spostare il puntatore dello stack di 8 byte, non 24. Come riferimento, un buffer di 17 byte produce un puntatore dello stack spostato di 40 byte e nessun buffer sposta il puntatore dello stack 8. Un buffer compreso tra 1 e 16 byte inclusi sposta ESP 24 byte.

Ora supponendo che gli 8 byte siano una costante necessaria (cosa è necessario per?), Ciò significa che stiamo allocando in blocchi di 16 byte. Perché il compilatore dovrebbe essere allineato in questo modo? Sto usando un processore x86_64, ma anche una parola a 64 bit dovrebbe richiedere solo un allineamento a 8 byte. Perché la discrepanza?

Per riferimento, lo sto compilando su un Mac che esegue 10.5 con gcc 4.0.1 e nessuna ottimizzazione abilitata.

risposta

43

È una funzione gcc controllata da -mpreferred-stack-boundary=n in cui il compilatore tenta di mantenere gli elementi nello stack allineati a 2^n. Se hai modificato n a 2, allocherebbe solo 8 byte nello stack. Il valore predefinito per n è 4, vale a dire che cercherà di allineare i limiti a 16 byte.

Perché c'è il "default" 8 byte e poi 24 = 8 + 16 byte è perché lo stack contiene già 8 byte per leave e ret, in modo che il codice compilato deve regolare la pila prima di 8 byte per farlo allineato 2^4 = 16.

+0

ha fatto "push% ebp" fatto esp diminuito di 8 byte? più gli 8 byte di ret, dovrebbero già essere allineati con 16 byte. Perché il compilatore di dose ha bisogno di questi 8 byte aggiuntivi? –

+1

oh, ho capito. Questo è un machince a 32 bit. Scusate.Dovrebbe essere ret 4 byte + ebp 4 byte + allineati 8 byte + buffer 16 –

+1

Le versioni correnti degli iSi System V i386 e x86-64 richiedono l'allineamento dello stack 16B (prima di un'istruzione 'call'), quindi le funzioni possono assumere quella. Storicamente, l'ABI i386 richiedeva solo l'allineamento 4B. (consultare https://stackoverflow.com/tags/x86/info per i collegamenti ai documenti ABI). GCC mantiene anche '% esp' allineato anche nelle funzioni foglia (che non chiamano altre funzioni), quando deve riservare qualsiasi spazio, e questo è quello che sta succedendo qui. –

3

Ho trovato this site, che ha qualche spiegazione decente in fondo alla pagina sul motivo per cui lo stack potrebbe essere più grande. Ridimensiona il concetto fino a una macchina a 64 bit e potrebbe spiegare ciò che stai vedendo.

-1

Gli 8 byte ci sono perché la prima istruzione spinge il valore iniziale di% ebp sullo stack (assumendo 64-bit).

+1

L'indirizzo di ritorno e il puntatore di base vengono entrambi inseriti nello stack. – dreamlax

11

La famiglia di istruzioni SSEx RICHIEDE pacchetti di vettori a 128 bit da allineare a 16 byte - altrimenti si ottiene un segfault che prova a caricarli/memorizzarli. Cioè se si desidera passare in modo sicuro i vettori a 16 byte per l'uso con SSE nello stack, lo stack deve essere costantemente mantenuto allineato a 16. I conti GCC lo richiedono per impostazione predefinita.

+0

Potrei avere troppa poca esperienza per sostenere che la tua risposta è sbagliata. Ma non usi le istruzioni "movupd" e simili ** u ** naligned esattamente per quello scopo (caricamento/memorizzazione di _unaligned_ dati compressi)? Da quanto ho capito, si può ottenere un comportamento errato quando si tenta di usare 'movapd' e istruzioni simili su dati non allineati, ma i dati non allineati non dovrebbero essere un problema in generale. – andreee

1

L'OSI Mac OS X/Darwin x86 richiede un allineamento dello stack di 16 byte. Questo non è il caso su altre piattaforme x86 come Linux, Win32, FreeBSD ...

+1

Il requisito ABI effettivo è che lo stack sia allineato a 16 byte * ai limiti delle chiamate di funzione *. –

+2

Questo è vero, ma dal momento che i prologhi/gli epiloghi delle funzioni riguardano solo le posizioni in cui viene modificato il puntatore dello stack, ciò equivale a dire che deve essere allineato in ogni momento. – Ringding

Problemi correlati