TGViewer
S0ER S0ER @softwareengineervlog · 10.4K subscribers
Post #2281 6.28K

Forwarded from S0ER.Code

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

В языках высокого уровня, таких как C или C++, часто используются указатели на функции. Это позволяет динамически выбирать, какую функцию вызвать в runtime. Но как это выглядит на уровне ассемблера? Давайте разберемся.

Для начала рассмотрим простой пример на C, где используется указатель на функцию:

int callme() { 
return 1;
}

void main() {
int (*func_ptr)() = callme;
func_ptr();
}

Здесь мы создаем указатель на функцию func_ptr, который указывает на функцию callme, и затем вызываем функцию через этот указатель.

Как это выглядит в ассемблере?

Используем Compiler Explorer, чтобы преобразовать этот код в ассемблер. Вот что получилось:

callme: 
push rbp
mov rbp, rsp
mov eax, 1
pop rbp
ret
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov QWORD PTR [rbp-8], OFFSET FLAT:callme
mov rax, QWORD PTR [rbp-8]
call rax
nop
leave
ret

Что здесь происходит?

Создание указателя на функцию:

В функции main мы видим, что адрес функции callme сохраняется в памяти по адресу [rbp-8]:

mov QWORD PTR [rbp-8], OFFSET FLAT:callme

Здесь OFFSET FLAT:callme — это адрес функции callme в памяти.

Загрузка указателя в регистр:

Затем этот адрес загружается в регистр rax:

mov rax, QWORD PTR [rbp-8]

Вызов функции по указателю:

После этого происходит вызов функции через регистр rax:

call rax

Инструкция call использует значение в регистре rax как адрес функции, на которую нужно перейти.

Пролог и эпилог

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

Пролог:

push rbp
mov rbp,
rsp sub rsp, 16

Здесь сохраняется значение rbp, устанавливается новый кадр стека и выделяется место для локальных переменных.

Эпилог:

leave ret

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

Пример с массивом в функции

Давайте добавим массив в функцию callme и посмотрим, как это повлияет на ассемблерный код:

int callme() { 
char a[128];
return 1;
}

В ассемблере это будет выглядеть так:

callme:
push rbp
mov rbp, rsp
sub rsp, 8 ; <-- обратите внимание, тут сработала Red Zone
mov eax, 1
leave
ret

Здесь видно, что в прологе добавилась инструкция sub rsp, 128, которая выделяет место на стеке для массива a[128].

Вывод

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

Основные моменты:

Указатель на функцию — это просто адрес функции в памяти.
Вызов функции по указателю осуществляется через регистр, в котором хранится адрес функции.

Пролог и эпилог присутствуют как при обычном вызове функции, так и при вызове через указатель.

Таким образом, даже такие высокоуровневые конструкции, как указатели на функции, имеют свое прямое отражение в ассемблерном коде.
  • 🔥 35
  • 👍 14
  • 🤯 7
More from @softwareengineervlog
  1. Sep 3, 2026Google выпустила Gemini 3.8 Flash — новая быстрая модель уже обходит гигантов в кодинге. П…
  2. Sep 3, 2026Сейчас использую DeesSeek v4 Flash, на готовой кодовой базе решать стандартные таски из тр…
  3. Aug 6, 2026Полезное: разраб сделал интерактивный мануал, который по шагам показывает, что происходит…
  4. Aug 5, 2026Ты ненастоящий программист! Это происходит снова и снова, и каждый раз одинаково. Я начал…
  5. Jul 27, 2026Внимание, скам! Появился бот который выдает себя за бота моего канала. Я НИЧЕГО ЧЕРЕЗ БОТО…
  6. Jul 25, 2026У Сбера есть интересный whitepaper где он описывает технологию разработки продукта с помощ…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →