Alineamiento de memoria hoy y hace 20 años
En el famoso artículo "Smashing the Stack for Fun and Profit", su autor toma una función C
void function(int a, int b, int c) {
char buffer1[5];
char buffer2[10];
}
y genera la salida del código de ensamblaje correspondiente
pushl %ebp
movl %esp,%ebp
subl $20,%esp
El autor explica que, dado que las computadoras direccionan la memoria en múltiplos de tamaño de palabra, el compilador reservó 20 bytes en la pila (8 bytes para el buffer1, 12 bytes para el buffer2).
Traté de recrear este ejemplo y obtuve lo siguiente
pushl %ebp
movl %esp, %ebp
subl $16, %esp
Un resultado diferente! Intenté varias combinaciones de tamaños para el buffer1 y el buffer2, y parece que el gcc moderno ya no rellena los tamaños del buffer a múltiplos de tamaño de palabra. En cambio, cumple con el-mpreferred-stack-boundary
opción.
Como ilustración, usando las reglas aritméticas del papel, para buffer1 [5] y buffer2 [13] obtendría 8 + 16 = 24 bytes reservados en la pila. Pero en realidad obtuve 32 bytes.
El periódico es bastante viejo y desde entonces sucedieron muchas cosas. Me gustaría saber, ¿qué motivó exactamente este cambio de comportamiento? ¿Es el movimiento hacia máquinas de 64 bits? ¿O algo mas?
Editar
El código se compila en una máquina x86_64 usando gcc versión 4.8.2 (Ubuntu 4.8.2-19ubuntu1) así:
$ gcc -S -o example1.s example1.c -fno-stack-protector -m32