Подтвердить что ты не робот

Что происходит на языке ассемблера при вызове метода/функции?

Если у меня есть программа в С++/C, то (язык не имеет большого значения, просто необходимо проиллюстрировать концепцию):

#include <iostream>    

void foo() {
    printf("in foo");
}

int main() {
    foo();
    return 0;
}

Что происходит в сборке? Я на самом деле не ищу ассемблерный код, так как я еще не получил его еще, но какой основной принцип?

4b9b3361

Ответ 1

В общем, это то, что происходит:

  • Аргументы функции хранятся в стеке. В специфическом для платформы порядке.
  • Местоположение для возвращаемого значения "выделено" в стеке
  • Обратный адрес для функции также сохраняется в стеке или в регистре CPU специального назначения.
  • Функция (или, фактически, адрес функции) вызывается либо с помощью специальной команды call, либо с помощью обычной команды jmp или br (переход/ветвь)
  • Функция считывает аргументы (если есть) из стека и запускает код функции
  • Возвращаемое значение из функции хранится в указанном месте (стек или регистр CPU специального назначения)
  • Выполнение возвращается к вызывающему, и стек очищается (путем восстановления указателя стека до его начального значения).

Подробности выше варьируются от платформы к платформе и даже от компилятора к компилятору (см., например, соглашения STDCALL и CDECL). Например, в некоторых случаях регистры CPU используются вместо хранения материала в стеке. Общая идея такая же, хотя

Ответ 2

Вы можете увидеть это сами:

Под Linux "скомпилируйте" свою программу с помощью:

gcc -S myprogram.c

И вы получите список программ в ассемблере (myprogram.s).

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

  • поместить переменную a в стек
  • положить переменную b в стек
  • поместить переменную n в стек
  • перейти к адресу функции
  • загрузить переменные из стека
  • делать вещи в функции
  • чистый стек
  • вернуться к главному

Ответ 3

Что происходит в сборке?

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

Ответ 4

Аргументы вставляются в стек и выполняется команда "вызов"

Вызов - это простой "jmp" с нажатием адреса инструкции в стек ( "ret" в конце метода, который выталкивает его и прыгает на него)

Ответ 5

Я думаю, вы хотите взглянуть на стек вызовов, чтобы лучше понять, что происходит во время вызова функции: http://en.wikipedia.org/wiki/Call_stack

Ответ 7

Что происходит?

C имитирует, что произойдет в сборке...

Это так близко к машине, что вы можете понять, что произойдет

void foo() {
    printf("in foo");

/*

db mystring 'in foo'
mov eax, dword ptr mystring
mov edx , dword ptr _printf
push eax
call edx
add esp, 8
ret
//thats it
*/

}

int main() {
    foo();
    return 0;
}

Ответ 8

Общая идея заключается в том, что регистры, которые используются в вызывающем методе, помещаются в стек (указатель стека находится в регистре ESP), этот процесс называется "нажимать регистры". Иногда они также обнуляются, но это зависит. Сборочные программисты стремятся освободить больше регистров, чем обычные 4 (EAX, EBX, ECX и EDX на x86), чтобы иметь больше возможностей внутри функции.

Когда функция заканчивается, то же происходит в обратном порядке: стек восстанавливается до состояния перед вызовом. Это называется "всплыванием регистров".

Обновление: этот процесс не обязательно должен произойти. Компиляторы могут оптимизировать его и встроить ваши функции.

Обновление: обычно параметры функции помещаются в стек в обратном порядке, когда они извлекаются из стека, они выглядят как в обычном порядке. Этот порядок не гарантируется C. (ref: Inner Loops Риком Бутом)

Ответ 9

1- вызывающий контекст устанавливается в стеке

2- параметры помещаются в стек

3 "вызов" выполняется для метода

Ответ 10

Что происходит? В x86 первая строка вашей основной функции может выглядеть примерно так:

call foo

Команда call будет выталкивать адрес возврата в стек, а затем jmp в местоположение foo.

Ответ 11

Общая идея заключается в том, что вам нужно

  • Сохранить текущее локальное состояние
  • Передайте аргументы функции
  • Вызов фактической функции. Это связано с тем, что где-то помещается адрес возврата, поэтому инструкция RET знает, где продолжить.

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

Довольно полезной отправной точкой является статья Википедии о соглашениях о вызовах. Например, на x86 стек почти всегда используется для передачи аргументов в функции. Однако во многих архитектурах RISC регистры используются в основном, тогда как стек необходим только в исключительных случаях.