Распределение стека, заполнение и выравнивание - PullRequest
45 голосов
/ 30 июня 2009

Я пытался глубже понять, как компиляторы генерируют машинный код, и более конкретно, как GCC работает со стеком. При этом я писал простые программы на Си, собирал их в ассемблер и старался изо всех сил понять результат. Вот простая программа и вывод, который она генерирует:

asmtest.c * * 1004

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

asmtest.s

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

Что меня удивляет, так это то, почему для стека выделяется 24 байта. Я знаю, что из-за того, как процессор обращается к памяти, стек должен выделяться с шагом 4, но если бы это было так, мы должны перемещать указатель стека только на 8 байтов, а не 24. Для справки: буфер 17 в байтах создается указатель стека, перемещенный на 40 байтов, и ни один буфер вообще не перемещает указатель стека 8. Буфер между 1 и 16 байтами включительно перемещается ESP 24 байта.

Теперь, предполагая, что 8 байтов является необходимой константой (для чего она нужна?), Это означает, что мы выделяем порциями по 16 байтов. Почему компилятор будет выравнивать таким образом? Я использую процессор x86_64, но даже для 64-битного слова требуется только 8-байтовое выравнивание. Почему расхождение?

Для справки, я собираю это на Mac под управлением 10.5 с gcc 4.0.1 и без оптимизации.

Ответы [ 6 ]

47 голосов
/ 30 июня 2009

Это функция gcc, управляемая -mpreferred-stack-boundary=n, когда компилятор пытается сохранить элементы в стеке выровненными по 2^n. Если вы изменили n на 2, он выделит только 8 байтов в стеке. Значение по умолчанию для n равно 4, то есть оно будет пытаться выровняться по 16-байтовым границам.

Почему существуют 8 байтов по умолчанию, а затем 24 = 8 + 16 байтов, потому что стек уже содержит 8 байтов для leave и ret, поэтому скомпилированный код должен сначала настроить стек на 8 байтов он выровнен по 2 ^ 4 = 16.

12 голосов
/ 30 июня 2009

Семейство инструкций SSEx ТРЕБУЕТ упакованных 128-битных векторов, которые должны быть выровнены до 16 байтов - в противном случае вы получаете segfault, пытающийся загрузить / сохранить их. То есть если вы хотите безопасно передать 16-байтовые векторы для использования с SSE в стеке, стек необходимо постоянно поддерживать равным 16. GCC учитывает это по умолчанию.

3 голосов
/ 30 июня 2009

Я нашел этот сайт , в нижней части страницы которого есть несколько объяснений, почему стек может быть больше. Масштабируйте концепцию до 64-битной машины, и это может объяснить, что вы видите.

2 голосов
/ 30 июня 2009

У LWN есть статья по выравниванию памяти , которая может вас заинтересовать.

1 голос
/ 06 августа 2009

Для Mac OS X / Darwin x86 ABI требуется выравнивание стека 16 байтов. Это не относится к другим платформам x86, таким как Linux, Win32, FreeBSD ...

0 голосов
/ 30 июня 2009

Имеется 8 байтов, потому что первая инструкция помещает начальное значение% ebp в стек (при условии 64-разрядного).

...