Распределение стека, отступы и выравнивание
Я пытаюсь получить более глубокое понимание того, как компиляторы генерируют машинный код, а точнее, как GCC имеет дело со стеком. При этом я пишу простые программы на С, компилируя их в сборку и изо всех сил пытаюсь понять результат. Вот простая программа и ее результат:
asmtest.c
:
void main() { char buffer[5]; }
asmtest.s
:
- Каков размер стека по умолчанию, может ли он расти, как он работает с сборкой мусора?
- Стеки растут вверх или вниз?
- Java / Android - Как распечатать полную трассировку стека?
- В C, скобки действуют как стек стека?
- Увеличьте размер стека в Linux с помощью setrlimit
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, и оптимизация не включена.
- Базовая recursion, проверка сбалансированной скобки
- Объявлять переменные в верхней части функции или в отдельных областях?
- WPF VirtualizingStackPanel для повышения производительности
- Как предотвратить несколько экземпляров активности при запуске с различными намерениями
- Как группировать андроидные уведомления, например whatsapp?
- Почему class Java Vector (и Stack) считается устаревшим или устаревшим?
- Обнаружено разбиение стека
- Как предотвратить возврат к предыдущей деятельности?
Это функция gcc, управляемая параметром -mpreferred-stack-boundary=n
где компилятор пытается сохранить элементы в стеке, совпадающие с 2^n
. Если вы изменили n
на 2
, оно будет выделять только 8 байтов в стеке. Значение по умолчанию для n
равно 4
т. Е. Будет пытаться согласовать границы 16 байтов.
Почему есть «по умолчанию» 8 байтов, а затем 24 = 8 + 16 байт, потому что стек уже содержит 8 байтов для leave
и ret
, поэтому скомпилированный код должен сначала отрегулировать стек на 8 байтов, чтобы совместить его с 2 ^ 4 = 16.
Семейство инструкций SSEx ТРЕБУЕТ упакованных 128-битных векторов, которые должны быть выровнены с 16 байтами – в противном случае вы получите segfault, пытающийся загрузить / сохранить их. Т.е. если вы хотите безопасно передавать 16-байтные векторы для использования с SSE в стеке, стек должен быть последовательно сохранен в соответствии с 16. Учетные записи GCC по умолчанию.
Я нашел этот сайт , у которого есть некоторое достойное объяснение внизу страницы о том, почему стек может быть больше. Масштабируйте концепцию до 64-битной машины, и она может объяснить, что вы видите.
У LWN есть статья о выравнивании памяти , которую вы можете найти интересной.
Для Mac OS X / Darwin x86 ABI требуется выравнивание стека по 16 байт. Это не относится к другим платформам x86, таким как Linux, Win32, FreeBSD …
8 байтов есть потому, что первая команда подталкивает начальное значение% ebp в стеке (предполагая 64-разрядную).