Alocação, preenchimento e alinhamento de pilha

Eu tenho tentado entender melhor como os compiladores geram código de máquina e, mais especificamente, como o GCC lida com a pilha. Ao fazê-lo, tenho escrito programas C simples, compilando-os em assembly e tentando o meu melhor para entender o resultado. Aqui está um programa simples e a saída é gerada:

asmtest.c :

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

asmtest.s :

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

O que me intriga é por que 24 bytes estão sendo alocados para a pilha. Eu sei que por causa de como o processador endereça memory, a pilha tem que ser alocada em incrementos de 4, mas se este fosse o caso, nós só deveríamos mover o ponteiro da pilha em 8 bytes, não 24. Para referência, um buffer de 17 bytes produz um ponteiro de pilha movido 40 bytes e nenhum buffer move o ponteiro de pilha 8. Um buffer entre 1 e 16 bytes inclusive move 24 bytes de ESP .

Agora, assumindo que os 8 bytes são uma constante necessária (para que é necessário?), Isso significa que estamos alocando em blocos de 16 bytes. Por que o compilador estaria se alinhando de tal maneira? Estou usando um processador x86_64, mas mesmo uma palavra de 64 bits deve exigir apenas um alinhamento de 8 bytes. Por que a discrepância?

Para referência, estou compilando isso em um Mac executando 10.5 com gcc 4.0.1 e sem otimizações ativadas.

É um recurso gcc controlado por -mpreferred-stack-boundary=n onde o compilador tenta manter itens na pilha alinhados a 2^n . Se você alterasse n para 2 , só alocaria 8 bytes na pilha. O valor padrão para n é 4 ou seja, ele tentará se alinhar aos limites de 16 bytes.

Por que existem os 8 bytes “padrão” e então 24 = 8 + 16 bytes é porque a pilha já contém 8 bytes para leave e ret , então o código compilado deve ajustar a pilha primeiro por 8 bytes para alinhar com 2 ^ 4 = 16

A família SSEx de instruções REQUER os vetores de 128 bits compactados para serem alinhados a 16 bytes – caso contrário, você obterá um segfault tentando carregá-los / armazená-los. Ou seja, se você deseja transmitir com segurança vetores de 16 bytes para uso com o SSE na pilha, a pilha precisa ser alinhada de forma consistente a 16. Por padrão, o GCC considera isso.

Eu encontrei este site , que tem alguma explicação decente na parte inferior da página sobre por que a pilha pode ser maior. Escale o conceito até uma máquina de 64 bits e isso pode explicar o que você está vendo.

O LWN tem um artigo sobre alinhamento de memory , que você pode achar interessante.

A ABI do Mac OS X / Darwin x86 requer um alinhamento de pilha de 16 bytes. Este não é o caso em outras plataformas x86 como Linux, Win32, FreeBSD …

Os 8 bytes estão lá porque a primeira instrução envia o valor inicial de% ebp na pilha (assumindo 64 bits).