Alinhamento da memória hoje e há 20 anos
No famoso artigo "Smashing the Stack for Fun and Profit", seu autor assume uma função C
void function(int a, int b, int c) {
char buffer1[5];
char buffer2[10];
}
e gera a saída do código de montagem correspondente
pushl %ebp
movl %esp,%ebp
subl $20,%esp
O autor explica que, como os computadores endereçam a memória em múltiplos de tamanho de palavra, o compilador reservou 20 bytes na pilha (8 bytes para o buffer1, 12 bytes para o buffer2).
Tentei recriar este exemplo e obtive o seguinte
pushl %ebp
movl %esp, %ebp
subl $16, %esp
Um resultado diferente! Tentei várias combinações de tamanhos para buffer1 e buffer2, e parece que o gcc moderno não suporta tamanhos de buffer para múltiplos de tamanho de palavra. Em vez disso, cumpre o-mpreferred-stack-boundary
opção.
Como ilustração - usando as regras aritméticas do papel, para buffer1 [5] e buffer2 [13], eu receberia 8 + 16 = 24 bytes reservados na pilha. Mas, na realidade, tenho 32 bytes.
O jornal é bastante antigo e muita coisa aconteceu desde então. Eu gostaria de saber o que exatamente motivou essa mudança de comportamento? É a mudança para máquinas de 64 bits? Ou alguma outra coisa?
Editar
O código é compilado em uma máquina x86_64 usando a versão gcc 4.8.2 (Ubuntu 4.8.2-19ubuntu1) assim:
$ gcc -S -o example1.s example1.c -fno-stack-protector -m32