Мы можем вызывать как в коде ассемблера функции C/C++, так и в коде С/С++ процедуры ассемблера. А также передавать параметры и получать результаты.
Согласно Windows ABI, функции (процедуры) возвращают целые числа и значения указателей (которые умещаются в 64 бита) в регистре RAX. Поэтому, если какой-то код С/C++ ожидает, что процедура ассемблера вернет целочисленный результат, то код на ассемблере должен проместить целочисленный результат в регистр RAX перед возвратом из процедуры.
Например, определим следующий файл hello.asm
option casemap:none .data text byte "Hello Asm", 0 .code ; определяем процедуру hello public hello hello proc lea rax, text ; в регистр rax загружаем адрес строки text ret hello endp end
Здесь функция hello формально возвращает строку text. Фактически же это выражается в загрузке в регистр rax адрес строки text.
Также определим файл app.c, где получим строку и выведим ее на консоль:
#include <stdio.h> extern char* hello(void); int main() { char* message = hello(); printf(message); }
Посольку в регистр rax загружаетс адрес строки, то для C/C++ возвращаемым типом функции будет тип char*
. Соответственно мы можем получить эту строку:
char* message = hello();
Скомпилируем и запустим программу
c:\asm>ml64 /c hello.asm Microsoft (R) Macro Assembler (x64) Version 14.36.32532.0 Copyright (C) Microsoft Corporation. All rights reserved. Assembling: hello.asm c:\asm>cl app.c hello.obj Microsoft (R) C/C++ Optimizing Compiler Version 19.36.32532 for x64 Copyright (C) Microsoft Corporation. All rights reserved. app.c Microsoft (R) Incremental Linker Version 14.36.32532.0 Copyright (C) Microsoft Corporation. All rights reserved. /out:app.exe app.obj hello.obj c:\asm>app Hello Asm c:\asm>
Рассмотрим передачу параметров в процедуру на ассемблере и для этого возьмем следующий пример. Пусть в файле hello.asm определен следующий ассемблерный код:
option casemap:none .code ; определяем функцию sum ; мы ожидаем два параметра - два числа ; по соглашениям первые два параметра в регистрах RCX и RDX ; возвращаемое значение - в регистре RAX public sum sum proc mov rax, rcx ; помещаем в rax первый параметр - из регистра rcx add rax, rdx ; складываем со вторым параметром - из регистра rdx ret sum endp end
Здесь определена процедура sum, которая должна складывать два числа и возращать их сумму. Согласно соглашениям первые два параметра передаются через регистры RCX и RDX, а результат помещается в регистр RAX. Поэтому функция sum получает из регистров RCX и RDX переданные значения и их сумму помещает в RAX.
Определим файл app.c, в котором вызовем данную процедуру и получим ее результат:
#include <stdio.h> #define int64 unsigned long long extern int64 sum(int64, int64); int main() { int64 a = 5; int64 b = 6; int64 result = sum(a, b); printf("result = %lld \n", result); // result = 11 }
Все параметры и результат у нас представдяют беззнаковый 64-разрядный целочисленный тип, то есть unsigned long long
, и для краткости для него определен псевдоним int64
В вызов функции sum передаются значения переменных a и b. В итоге значение переменной a будет помещено в регистр RCX, а значение переменной b - в регистр RDX. Значение из регистра RAX мы можем получить в качестве результата функции
int64 result = sum(a, b);
Результат работы программы:
c:\asm>ml64 /c hello.asm Microsoft (R) Macro Assembler (x64) Version 14.36.32532.0 Copyright (C) Microsoft Corporation. All rights reserved. Assembling: hello.asm c:\asm>cl app.c hello.obj Microsoft (R) C/C++ Optimizing Compiler Version 19.36.32532 for x64 Copyright (C) Microsoft Corporation. All rights reserved. app.c Microsoft (R) Incremental Linker Version 14.36.32532.0 Copyright (C) Microsoft Corporation. All rights reserved. /out:app.exe app.obj hello.obj c:\asm>app result = 11 c:\asm>