Обязательны ли пролог и эпилог при написании ассемблерных функций?

Недавно я переписал некоторые функции libc в ассемблере и, для некоторых из них (те, которые не нуждались в каких-либо инструкциях вызова или системного вызова, например, strlen), я отбрасываю пролог и эпилог, потому что мои тесты не провалились без него (может быть, я не достаточно сложные тесты). Во время экспертной оценки кто-то сказал мне, что отрицать это плохая практика, но не смог объяснить, почему.

Итак, я сталкиваюсь с проблемами, когда я вызываю функции asm, у которых нет комбо пролога / эпилога?

Полезно ли добавлять его, даже если в стеке не требуется дополнительное пространство?

Если это необходимо по каким-то причинам, почему компилятор (я использовал nasm) не позаботится об этом?

Спасибо за прочтение

1 ответ

Решение

Если вы не настроите правильный фрейм стека, отладчику может быть сложно узнать, в какой функции вы находитесь сейчас. Для ELF-целей вы должны вручную предоставить данные CFI (см. Эту статью), если вы явно не устанавливаете фрейм стека. Без данных CFI разматывание стека не работает, и отладчик может не определить, в какой функции вы находитесь. Если вы не хотите вручную добавлять данные CFI (что несколько утомительно и легко ошибиться), я рекомендую вам принять незначительную потерю производительности и просто установить полный кадр стека.

Обязательны ли пролог и эпилог при написании функций ассемблера?

Для чистой сборки вам даже не нужны "функции" - например, у вас может быть кусок кода с несколькими разными точками входа и одним "ret" (что эквивалентно тому, что вы можете получить после того, как приличный компилятор сделает оптимизации "хвостового вызова").

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

Обратите внимание, что для 80x86 ни одно из соглашений о вызовах не требует кадра стека (как EBP или RBP) - это просто исторические памятные вещи, возникшие из-за плохой конструкции древних отладчиков, и перестали быть разумными, когда отладчики переключились на более совершенные методы около 20 лет назад.

Если это обязательно по каким-то причинам, почему ассемблер (я использовал nasm) не позаботился об этом?

Ассемблеры обычно не знают, какое соглашение о вызовах (если таковое имеется) вы пытаетесь соблюдать.

Другие вопросы по тегам