Подтвердить что ты не робот

Что такое "выравнивание стека"?

Что такое выравнивание стека? Почему он используется? Можно ли это контролировать с помощью настроек компилятора?

Подробности этого вопроса взяты из проблемы, возникающей при попытке использовать библиотеки ffmpeg с msvc, однако то, что меня действительно интересует, является объяснением того, что такое "выравнивание стека".

Подробности:

  • При запуске моей программы, совместимой с msvc, которая ссылается на avcodec, я получаю следующая ошибка: "Компилятор не выровнял переменные стека. Libavcodec имеет был скомпрометирован ", после чего произошел сбой в avcodec.dll.
  • avcodec.dll не был скомпилирован с msvc, поэтому я не вижу, что происходит внутри.
  • При запуске ffmpeg.exe и использовании того же avcodec.dll все работает хорошо.
  • Файл ffmpeg.exe не был скомпилирован с помощью msvc, он соответствовал gcc/mingw (то же, что и avcodec.dll)

Спасибо,

Dan

4b9b3361

Ответ 1

Выравнивание переменных в памяти (короткая история).

На прошлых компьютерах была 8-битная шина данных. Это означает, что каждый такт 8 бит информации может быть обработан. Тогда это было хорошо.

Затем появились 16-разрядные компьютеры. Из-за нисходящей совместимости и других проблем был сохранен 8-битный байт, и было введено 16-битное слово. Каждое слово было 2 байта. И каждый такт 16 бит информации может быть обработан. Но это создавало небольшую проблему.

Посмотрим на карту памяти:

+----+
|0000| 
|0001|
+----+
|0002|
|0003|
+----+
|0004|
|0005|
+----+
| .. |

В каждом адресе есть байт, доступ к которому возможен индивидуально. Но слова могут быть получены только по четным адресам. Поэтому, если мы читаем слово в 0000, мы читаем байты в 0000 и 0001. Но если мы хотим прочитать слово в позиции 0001, нам нужны два обращения к чтению. Сначала 00000001, а затем 0002 0003, и мы сохраняем только 0001 0002.

Конечно, это заняло некоторое дополнительное время, и это не было оценено. Вот почему они изобрели выравнивание. Таким образом, мы сохраняем переменные слов в границах слов и байтовых переменных на границах байтов.

Например, если у нас есть структура с байтовым полем (B) и полем слов (W) (и очень наивным компилятором), мы получаем следующее:

+----+
|0000| B
|0001| W
+----+
|0002| W
|0003|
+----+

Это не весело. Но при использовании выравнивания слов мы находим:

+----+
|0000| B
|0001| -
+----+
|0002| W
|0003| W
+----+

Здесь память приносится в жертву за скорость доступа.

Вы можете себе представить, что при использовании двойного слова (4 байта) или четырех слова (8 байтов) это еще более важно. Поэтому для большинства современных компиляторов вы можете выбрать, какое выравнивание вы используете при компиляции программы.

Ответ 2

IIRC, выравнивание стека - это когда переменные помещаются в стек "выровнены" с определенным количеством байтов. Поэтому, если вы используете 16-битное выравнивание стека, каждая переменная в стеке будет начинаться с байта, который кратен 2 байтам от текущего указателя стека внутри функции.

Это означает, что если вы используете переменную, которая равна < 2 байта, например char (1 байт), между ним и следующей переменной будет 8 бит неиспользуемого "дополнения". Это позволяет определенные оптимизации с допущениями на основе переменных местоположений.

При вызове функций один метод передачи аргументов следующей функции состоит в том, чтобы поместить их в стек (вместо того, чтобы помещать их непосредственно в регистры). Независимо от того, используется ли здесь выравнивание, важно, поскольку вызывающая функция помещает переменные в стек, чтобы считаться вызывающей функцией с помощью смещений. Если вызывающая функция выравнивает переменные, и вызываемая функция ожидает, что они будут не выровнены, то вызываемая функция не сможет их найти.

Кажется, что скомпилированный код msvc не согласен с выравниванием переменных. Попробуйте выполнить компиляцию при выключенных оптимизациях.

Ответ 3

Некоторые архитектуры процессоров требуют конкретного выравнивания различных типов данных и будут генерировать исключения, если вы не соблюдаете это правило. В стандартном режиме x86 не требует этого для базовых типов данных, но может пострадать от штрафов за производительность (см. Www.agner.org для советов по оптимизации на низком уровне).

Однако набор

Ответ 4

Насколько я знаю, компиляторы обычно не выравнивают переменные, находящиеся в стеке. Библиотека может зависеть от некоторого набора параметров компилятора, который не поддерживается вашим компилятором. Нормальное исправление заключается в объявлении переменных, которые должны быть выровнены как статические, но если вы сделаете это в коде других людей, вы должны быть уверены, что эти переменные, о которых идет речь, инициализируются позже в функции, а не в декларация.

// Some compilers won't align this as it on the stack...
int __declspec(align(32)) needsToBe32Aligned = 0;
// Change to
static int __declspec(align(32)) needsToBe32Aligned;
needsToBe32Aligned = 0;

В качестве альтернативы найдите компилятор, который выравнивает переменные в стеке. Очевидно, что синтаксис выравнивания "__declspec", который я использовал здесь, может быть не таким, каким использует ваш компилятор.