Низкий уровень: как выглядят функции на ASM



Процессор умеет выполнять лишь простые машинные команды, как же тогда работают функции и классы языков высокого уровня?



Чтобы разобраться будем использовать Compiler Explorer который позволяет преобразовать конструкции высокого уровня в их представление на низком уровне (Assembler).



Начать предлагаю с того, что посмотреть какой код будет сгенерирован компилятором для следующего листинга:





int callme() {

return 1;

}



void main() {

callme();

}




в командной строке это можно сделать с помощью команды



gcc -g -o output.s -masm=intel -fno-verbose-asm -S -fdiagnostics-color=always example.c




но Compiler Expolrer делает это за нас, в результате получен следующий код:



callme:

push rbp

mov rbp, rsp

mov eax, 1

pop rbp

ret

main:

push rbp

mov rbp, rsp

mov eax, 0

call callme

nop

pop rbp

ret


Мы видим, что:



имена функций превратились в имена меток, на самом деле это реальные адреса по которым будут делаться переходы, представленные в виде меток.



для вызова функции используется специальная инструкция call



для возврата из функции используется специальная инструкция ret



чтобы вернуть значение из функции используется регистр eax - mov eax,1

в функции есть специальные части "пролог" и "эпилог"



Что такое "Пролог"



Это часть функции которая сохраняет текущие значения регистров, чтобы восстановить их при возврате из функции.





push rbp; инструкция push сохраняет в стеке значение rbp



mov rbp, rsp; копирует значение регистра указателя вершины стека (открытие кадра стека)



sub rsp, xx; выделяем память под локальные переменные


1. rbp используется для адресации локальных переменных, должен быть сохранен в стеке;

2. rsp используется для указания на вершину стека



Что такое "эпилог"



Этр код, который закрывает кадр стека и восстанавливаем значние rpb

mov rsp, rbp

pop rbp

ret




Red zone



Вероятно вы заметили, что у нас в прологе нет инструкции sub rsp, xx, все дело в том, что у процессоров есть оптимизация, которая называется red zone, в данном случае - область размером 128 байт которая находится за пределами RSP и не должна изменяться обработчиками сигналов и прерываний.



В качестве индивидуального задания можете попробовать добавить char a[128]; в код функции callme и посмотреть что будет.





Вывод:

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



Часто узнать функции в коде на ассемблере можно по следующим признакам:



для вызова функций используются инструкции call, ret



без оптимизаций компилятор добавит специальные куски кода "пролог" и "эпилог"



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



#asm #знания



SOER | PRO | Boosty