Сборка: медитация шаблона функции

Сейчас я изучаю сборку и ничего не понимаю в (предположительно) стандартном шаблоне функции.

Итак, основываясь на этой замечательной книге, "форма запомнить для функций выглядит следующим образом:"

function_label:
    pushl   %ebp
    movl    %esp, %ebp
    < normal function code goes here>
    movl    %ebp, %esp
    popl    %ebp
    ret

Хорошо, меня все устраивает, но есть одна маленькая вещь, которую я не понимаю. После "нормального кода функции" восстанавливаем исходное (до вызова) значение esp, которое ранее хранилось в ebp.

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

Это какая-то защита от нас самих (на случай, если мы каким-то образом испортим стек где-то в нашем коде), включенная в этот шаблон? Или, может быть, изменение значений стека внутри функции — это нормальная практика? Или возможно, что начальное значение esp может измениться во время выполнения, даже если мы ничего с ним не делаем? (Я не могу понять, как это может быть, на самом деле.)

Я чувствовал себя довольно глупо, думая об этом, и проверил значение esp с gdb в этом простом коде:

.section .data
message:
    .asciz "> Hello from function #%d\n"

.section .text
.globl main

main:
    nop
    call    overhere
    pushl   $0
    call    exit
overhere:
    pushl   %ebp
    movl    %esp, %ebp
    pushl   $1
    pushl   $message
    call    printf
    add     $8, %esp
    movl    %ebp, %esp
    popl    %ebp
    ret 

И esp (как я и ожидал) остался нетронутым, поэтому перемещение ebp в esp на самом деле ничего не изменило.

Теперь, надеюсь, понятно, что я хочу узнать:

  • Может ли значение esp измениться само по себе? (Готов поспорить, что нельзя.)
  • Если не может, то этот шаблон выше, очевидно, предполагает, что программист может как-то изменить его внутри функции. Но я не могу понять, с какой стати это может понадобиться, так что изменение значения esp является ошибкой?

Заранее спасибо и извините за невежество.


person oopcode    schedule 22.11.2014    source источник


Ответы (1)


Я озадачен, как вы пропустили инструкцию, которая явно меняет esp: add $8, %esp. Таким образом, ответ однозначно да, он может меняться во время выполнения функции и не является ошибкой. Обратите внимание, что push и call также изменяют его, фактически add компенсирует две инструкции push (ret в конце printf уравновешивает call). Другой типичной причиной изменения esp является выделение локальных переменных. Это было исключено из шаблона функции, который вы показали, обычно он выглядит как sub $size_of_locals, %esp сразу после movl %esp, %ebp.

Тем не менее, вам не нужно использовать ebp для запоминания указателя стека, если вы гарантируете, что он имеет то же значение на выходе из функции, что и при входе. Последние версии gcc не используют ebp при включенной оптимизации, в противном случае для этого можно использовать -fomit-frame-pointer.

person Jester    schedule 22.11.2014