Архитектура x86-64 допускает три основные формы вызова процедуры: через имя процедуры, путем косвенной адресации через 64-битный регистр общего назначения и через переменную-указатель
call имя_процедуры ; прямой вызов call регистр ; косвенный вызов через регистр call указатель ; косвенный вызов через указатель-адрес процедуры
При вызове процедуры через регистр процессор x86-64 сначала помещает адрес возврата в стек, а затем начинает выборку следующего байта кода операции (инструкции) с адреса, указанного значением в регистре. При этом регистр должен быть 64-разрядным. Третья форма инструкции call извлекает адрес первой инструкции процедуры из переменной-указателя. Вычислить адрес процедуры можно либо с помощью оператора offset, либо с помощью инструкции lea.
Пример вызова через регистр:
.code sum proc mov rax, rcx add rax, rdx ret sum endp main proc mov rcx, 22 mov rdx, 24 mov rax, offset sum ; в RAX помещаем адрес процедуры sum call rax ; вызываем процедуру sum ret main endp end
Здесь с помощью оператора offset получаем адрес процедуры sum и загружаем его в регистр RAX. Затем для вызова процедуры передаем регистр RAX инструкции
call
. Также можно было бы использовать инструкцию lea
lea rax, sum ; загружаем адрес процедуры sum
При сохранении адреса в переменную она должна представлять тип qword:
.data ptrToSum qword offset sum ; указатель содержит адрес процедуры sum .code sum proc mov rax, rcx add rax, rdx ret sum endp main proc mov rcx, 22 mov rdx, 25 call ptrToSum ; вызываем процедуру через указатель ret main endp end
Здесь получаем адрес сразу при определении переменной-указателя. Но можно также динамически получать адрес процедуры в программе:
lea rax, sum mov ptrToSum, rax