Какая польза от "push% ebp; movl% esp,% ebp", созданного GCC для x86? - PullRequest
35 голосов
/ 25 марта 2010

Как эти две инструкции влияют на код сборки, сгенерированный gcc для машин x86:

push %ebp
movl %esp, %ebp

Ответы [ 6 ]

59 голосов
/ 25 марта 2010

объяснение раскручивания - буквальная истина (несмотря на одну незначительную ошибку направления), но не объясняет почему.

%ebp - это «базовый указатель» для вашего стекового фрейма. Это указатель, используемый средой выполнения C для доступа к локальным переменным и параметрам в стеке. Вот типичный код пролога функции, сгенерированный GCC (точнее, g ++). Сначала источник C ++.

// junk.c++
int addtwo(int a)
{
    int x = 2;

    return a + x;
}

Создает следующий ассемблер.

.file   "junk.c++"
    .text
.globl _Z6addtwoi
    .type   _Z6addtwoi, @function
_Z6addtwoi:
.LFB2:
    pushl   %ebp
.LCFI0:
    movl    %esp, %ebp
.LCFI1:
    subl    $16, %esp
.LCFI2:
    movl    $2, -4(%ebp)
    movl    -4(%ebp), %edx
    movl    8(%ebp), %eax
    addl    %edx, %eax
    leave
    ret
.LFE2:
    .size   _Z6addtwoi, .-_Z6addtwoi
    .ident  "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
    .section    .note.GNU-stack,"",@progbits

Теперь, чтобы объяснить этот пролог-код (все до .LCFI2:), сначала:

  1. pushl %ebp хранит кадр стека , вызывающий функцию в стеке.
  2. movl %esp, %ebp берет текущий указатель стека и использует его в качестве фрейма для , называемой функцией.
  3. subl $16, %esp оставляет место для локальных переменных.

Теперь ваша функция готова для бизнеса. Любые ссылки с отрицательным смещением из регистра %ebp% являются вашими локальными переменными (x в этом примере). Любые ссылки с положительным смещением от регистра %ebp% являются вашими параметрами, переданными в.

Последним интересным моментом является инструкция leave, которая является инструкцией ассемблера x86, которая выполняет работу по восстановлению стекового фрейма вызывающей функции. Обычно это оптимизируется для более быстрой последовательности move %ebp %esp и pop %ebp% в C-коде. Однако для наглядности я вообще не компилировал никаких оптимизаций.

9 голосов
/ 25 марта 2010

Это типичный код, который вы видите в начале функции.

Сохраняет содержимое регистра EBP в стеке, а затем сохраняет содержимое текущего указателя стека в EBP.

Стек используется во время вызова функции для хранения локальных аргументов. Но в функции указатель стека может измениться, поскольку значения хранятся в стеке.

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

В конце функции вы, вероятно, увидите команду

pop %ebp   ; restore original value 
ret        ; return 
7 голосов
/ 25 марта 2010
push %ebp

Это поместит 32-битный (расширенный) регистр базового указателя в стек, то есть указатель стека (% esp) будет вычтен из четырех, а затем значение% ebp скопировано в местоположение, на которое указывает указатель стека.

movl %esp, %ebp

Копирует регистр указателя стека в регистр базового указателя.

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

2 голосов
/ 25 марта 2010

Это часть так называемого пролога функции .

Сохраняет текущий базовый указатель, который будет извлечен после завершения функции, и устанавливает новый ebp в начало нового кадра.

1 голос
/ 13 февраля 2014

Я также думаю, что важно отметить, что часто после push %ebp и movl %esp, %ebp сборка будет иметь push %ebx или push %edx. Это сохраненные вызовы регистров %ebx и %edx. В конце обычного вызова регистры будут восстановлены с их исходными значениями.

Также - %ebx, %esi, %edi все регистры сохранения вызываемого абонента. Поэтому, если вы хотите перезаписать их, сначала нужно сохранить их, а затем восстановить.

0 голосов
/ 01 января 2016

Кусок кода устанавливает стек для вашей программы.

В стеке x86 информация хранится в двух регистрах.


    Base pointer (bp): Holds starting address of the stack
    Stack pointer (sp): Holds the address in which next value will be stored

Эти регистры имеют разные имена в разных режимах:


                            <code>Base pointer           Stack pointer</code>
    16 bit real mode:       bp                     sp
    32 bit protected mode:  ebp(%ebp)              esp(%esp)
    64 bit mode:            rbp                    rsp

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

Теперь, чтобы объяснить ваш код,


    push %ebp

Этот код помещает текущий адрес стека в стек, чтобы функция могла правильно "выйти" или "вернуться".


    movl %esp, %ebp

Этот код устанавливает стек для вашей функции.

За дополнительной информацией обращайтесь к этому вопросу .

Надеюсь, что это поможет!

...