2013-07-07 4 views
12

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

Мой вопрос: как вызываемая функция знает, как вернуться к ее вызывающему абоненту? Есть ли механизм, работающий за кулисами через компилятор?

+1

Вы должны прочитать [это] (http://en.wikipedia.org/wiki/Call_stack#Structure) Это очень хороший ответ: [Глава 11 - Процедуры] (http: //pages.cs.wisc. edu/~ smoler/x86text/lect.notes/procedure.html) И если вам нравится Видео: [Сборочный праймер для хакеров (часть 11) Стеки функций] (http://www.progamercity.net/code-tut/168- assembly-language-primer-hackers-video-series.html) вам понравятся все видео здесь. –

ответ

12

Компилятор подчиняется определенному «соглашению о вызове», который определяется как часть объекта ABI, на который вы нацеливаетесь. Эта конвенция о вызове будет включать в себя способ для системы узнать, какой адрес для возврата. Вызывающее соглашение обычно использует поддержку аппаратного обеспечения для вызовов процедур. На Intel, например, адрес возврата помещается в стек:

... процессор выталкивает значение EIP регистра (который содержит смещение инструкции после CALL инструкции) в стек (для последующего использования в качестве указателя команды возврата).

Возврат из функции осуществляется с помощью ret инструкции:

... процессор выскакивает указатель возврата команд (смещение) от вершины стека в регистр EIP и начинается выполнение программы с помощью нового указателя инструкции.

Для контраста, на ARM, обратный адрес помещается в регистр ссылка:

BL и BLX инструкции скопируйте адрес следующей команды в lr (r14, регистр ссылок).

Возврат обычно делается путем выполнения movs pc, lr, чтобы скопировать адрес из регистра ссылок обратно в регистр счетчика программ.

Ссылки:

  1. Intel Software Developers Manual
  2. ARM Information Center
4

Это стало возможным благодаря стека (особенно на Intel-системах). Предположим, что у нас есть метод caller, который включает, скажем, int, что он хранится локально.

Когда caller( звонит target(, что int должен быть сохранен. Он помещается в стек, а также адрес, из которого сделан вызов. target( может выполнять свою логику, создавать свои собственные локальные переменные и вызывать другие методы. Его локальные переменные будут помещены в стек вместе с адресом вызова.

Когда target( заканчивается, стек «разворачивается».Удаляется верхняя часть стека, содержащая локальные переменные target(.

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

8
  1. Компилятор знает, как вызвать функцию и какое соглашение о вызовах используется. Например, в C аргументы для функции помещаются в стек. Вызывающий отвечает за очистку стека, поэтому вызываемой функции не нужно удалять аргументы. Другие соглашения о вызовах могут включать нажатие аргументов в стеке, и вызываемая функция должна очистить его. В этом случае сгенерированный код таков, что функция исправляет стек до его возврата. Соглашения вызова Ohter могут передавать аргументы в регистрах, поэтому в таком случае вызываемой функции также не нужно заботиться.

  2. У CPU есть механизм для вызова подпрограммы. Это сохранит текущий адрес выполнения в стеке, а затем перенесет обработку на новый адрес. Когда функция выполняется, она выполняет оператор return, который будет извлекать адрес вызывающего абонента и возобновлять его выполнение.

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

4

Это требует сотрудничества между вызываемым абонентом и вызывающим абонентом.

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

Смежные вопросы