Если файл C имеет только одну функцию, почему pushq и movq все еще существуют в начале? - PullRequest
0 голосов
/ 16 апреля 2019

Я изучаю сборку AT & T уже несколько месяцев, и мне очень трудно обдумать некоторые повторяющиеся инструкции в моем файле .s. В частности,

main:
pushq %rbp
movq %rsp, %rbp

Из книги, которую я использую , я пришел к выводу, что pushq передает 64-битный адрес вызывающей функции в стек вызовов или сохраняет его; в то время как movq копирует значение (я полагаю, это адрес) из регистра% rsp в регистр% rbp. То есть оба они содержат адрес базы стека.

Кроме того, другие источники (спасибо Говинду) также объяснили этот вопрос довольно хорошо: Какова цель регистра RBP в ассемблере x86_64?

Я понял, я уже знаю, что pushq% rbp сохраняет указатель фрейма вызывающей стороны или сохраняет адрес предыдущего фрейма стека, но если это единственная функция, которую я вызываю в моей программе на C, то, что было "предыдущим фреймом стека" затем? Например, что хранилось в% rbp до моего вызова основной функции?

Например, если моя основная функция вызывает функцию с именем foo (), то код asm в моем файле .S будет выглядеть примерно так:

foo:

pushq %rbp
movq %rsp, %rbp

#whatever instruction

ret

В этом случае я знаю, что было помещено в% rbp (адрес инструкции вызова в main). Тогда имеет смысл сохранить его, потому что нам нужно будет вернуться к основной функции (w / ret). Но почему мы должны делать это в main, если main была единственной функцией в C?

1 Ответ

0 голосов
/ 16 апреля 2019

Эти две строки являются частью пролога функции, используемого для установки нового стека (или активации) фрейма. Первый, pushq %rbp помещает базовый указатель в стек. Второй, movq %rsp, %rbp, перемещает базовый указатель на указатель стека. Должна быть по крайней мере другая строка, где мы вычитаем некоторое значение из точки стека, это приводит к смещению точки стека вниз.

Напомним, что на платформах Intel стек растет вниз, а базовый указатель (rbp) обозначает дно стека, а указатель стека (rsp) указывает на вершину стека.

Теперь на 64-битных машинах больше регистров, поэтому зависимость от использования стека для временного хранения снижается, поэтому эти инструкции могут отсутствовать в 64-битном коде.

Эти инструкции являются частью соглашения о вызовах, или то, как функции называются в вашей книге, должно содержать описание того, что требуется для вызова функции. Однако обратите внимание, что между 32-разрядной и 64-разрядной версиями Intel существуют различия в способах вызова функций.

Адрес, к которому мы возвращаемся (значение в rip - указатель инструкции) помещается вызывающим в стек и не будет виден в коде вызываемого. Обычно вы вызываете функцию с помощью call <fnct_name>, однако это можно заменить на последовательность:

 pushq %rip
 jmp  <fnct_address>

(возможно, неверный синтаксис - я не очень часто использую синтаксис AT & T).

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

...