В известной работе «Smashing стека для развлечения и прибыли», автор принимает функцию Cвыравнивание памяти сегодня и 20 лет назад
void function(int a, int b, int c) {
char buffer1[5];
char buffer2[10];
}
и генерирует соответствующий выходной ассемблерный код
pushl %ebp
movl %esp,%ebp
subl $20,%esp
Автор объясняет, что, поскольку компьютеры адресуют память в кратном размере слова, компилятор зарезервировал 20 байт в стеке (8 байтов для buffer1, 12 байтов для buffer2).
я попытался воссоздать этот пример и получил следующий
pushl %ebp
movl %esp, %ebp
subl $16, %esp
другой результат! Я пробовал различные комбинации размеров для buffer1 и buffer2, и кажется, что современные gcc не имеют размер буфера для буферов, чтобы увеличить размер слова. Вместо этого он выполняет опцию -mpreferred-stack-boundary
.
В качестве иллюстрации - с использованием арифметических правил бумаги для буфера 1 [5] и buffer2 [13] Я бы получил 8 + 16 = 24 байта, зарезервированных в стеке. Но на самом деле я получил 32 байта.
Документ довольно старый, и с тех пор произошло много чего. Я хотел бы знать, что именно мотивировало это изменение поведения? Это движение к 64-битным машинам? Или что-то другое?
Редактировать
Код компилируется на x86_64 машину с помощью GCC версии 4.8.2 (Ubuntu 4.8.2-19ubuntu1) так:
$ gcc -S -o example1.s example1.c -fno-stack-protector -m32
8 + 16 - 24, а не 20. Кстати, кажется, что компилятор только немного поумнел, его локальный анализ анализа переменных рассмотрен как массивы символов, а так как массивы символов не нуждаются в выравнивании, он просто скрепил их и выровнял полученный «компактный» массив. –
Есть ли смысл выравнивать значения 'char' в любом случае? –
это на x86 или x86_64? – rmmh