内联汇编

    技术2022-07-10  107

    什么是内联汇编

    内联汇编是指在 C/C++ 代码中嵌入的汇编代码,与全部是汇编的汇编源文件不同,它们被嵌入到 C/C++ 的大环境中。内联汇编方式两个作用,一是程序的某些关键代码直接用汇编语言编写,可提高代码的执行效率;二是有些操作无法通过高级语言实现,或者实现起来很困难,必须借助汇编语言达到目的。

    x86 内联汇编

    使用内联汇编要用到 __asm 关键字,它可以出现在任何允许 C/C++ 语句出现的地方。对 __asm 关键字的使用有两种方式:

    // 1. __asm { // 汇编代码 } // 2. __asm // 汇编代码

    显然,第一种方法与 C/C++ 的风格很一致,并且把汇编代码和 C/C++ 代码清楚地分开,还避免了重复输入 __asm 关键字,因此推荐使用第一种方法。

    不像在 C/C++ 中的“{ }”,__asm 块的“{ }”不会影响 C/C++ 变量的作用范围。同时,__asm 块可以嵌套,而且嵌套也不会影响变量的作用范围。

    为了与低版本的 Visual C++ 兼容,_asm 和 __asm 具有相同的意义。另外,Visual C++ 支持标准 C++ 的 asm 关键字,但是它不会生成任何指令,它的作用仅限于使编译器不会出现编译错误。要使用内联汇编,必须使用 __asm 而不是 asm 关键字。

    例子:

    void MyFunc(char *pszText) { printf("%s\n", pszText); } int _tmain(int argc, _TCHAR* argv[]) { char str1[] = "__asm{ }"; char str2[] = "__asm"; // 32位程序内联汇编 第一种方式 __asm { lea eax, str1 push eax mov eax, MyFunc call eax } // 32位程序内联汇编 第二种方式 __asm lea eax, str2 __asm push eax __asm mov eax, MyFunc __asm call eax system("pause"); return 0; }

    效果图:


    x64 内联汇编

    在 64 位程序中,已经不能使用关键字 __asm 来添加汇编代码,而应把汇编代码全部写在 .asm 文件中,然后,再将 .asm 包含到项目中编译链接。现在,我们就先来讲解如何使用 VS2013 添加并编译 .asm 文件的步骤。

    首先,我们在本地上新建一个 .asm 格式的文件 “myasm.asm”之后,右击项目工程并选择“添加” —> “现有项”,然后选择我们新创建的“myasm.asm”文件,添加到工程中:

    然后,我们选中“myasm.asm”文件,并右击选择“属性”: 在“myasm.asm属性页”中,设置 从生成中排除 为“否”,设置 项类型 为“自定义生成工具”,然后,点击“应用”。这时,在窗口左侧就会生成“自定义生成工具”的扩展栏。如果是从 x64 模式下设置的,在一步,会没有反应或者卡死。所以,一定要从 Win32 模式开始,再创建 x64 模式,并把 Win32 的设置复制到 x64 模式中,便可以解决这个问题。

    接着,我们开始新建 x64 模式,因为我们要开发的是 64 位程序。我们选中项目工程,以此选择 “属性” —> “配置属性” —> “配置管理器” —> “活动解决方案平台”选择“新建”。这时,就会来到“新建解决方案平台”页面。我们选择“x64”,并从 Win32 中复制设置,创建新的项目平台,点击“确定”。这时,就可以使用 x64 模式编译 64 位程序了。

    然后,我们继续对 .asm 文件进行设置,将其包含到项目工程中来编译链接。选中“myasm.asm”文件,右击选择“属性”,来到“myasm.asm”属性页进行设置。在 命令行 中输入“ml64 /c %(fileName).asm”,在 输出 中输入“%(fileName).obj”,其它保持默认即可,点击“确定”即可完成设置。

    经过上述几个步骤,我们成功为 x64 程序添加 .asm 文件并设置包含到项目工程中编译链接。接下来,我们就开始讲解如何在 .asm 文件中写汇编代码了。


    对于 64 位程序在 .asm 中写代码,需要遵循以下几个规则: 会变文件 .asm 文件必须以关键字 .CODE 开始,关键字 END 结束,大小写都可以。

    .code ; 此处写汇编指令代码 end

    所有的汇编代码以函数方式组织在一起。也就是说,我们要将汇编代码封装成一个个汇编函数。要注意 64 位汇编中的函数声明以及调用约定:

    .code ; _MyAdd是汇编函数 _MyAdd proc ; 此处写汇编函数的代码 _MyAdd endp end

    其中, _MyAsm 是汇编函数的名称,proc 是汇编函数的关键字,endp 是汇编函数的结尾关键字。

    要注意和 32 位汇编函数的区别:32 位汇编函数调用约定 __stdcall,所有参数从右到左依次入栈,通过压栈传递参数。64 位汇编函数的调用约定 __fastcall,前 4 个参数是从左至右依次存放于RCX、RDX、R8、R9寄存器里面,剩下的参数从左至右顺序入栈。

    代码: (myasm.asm)

    .code _MyAdd proc xor rax, rax mov rax, rcx add rax, rdx add rax, r8 add rax, r9 ret _MyAdd endp end

    (Test.cpp)

    extern "C" ULONGLONG _MyAdd(ULONGLONG a1, ULONGLONG a2, ULONGLONG a3, ULONGLONG a4); int _tmain(int argc, _TCHAR* argv[]) { ULONGLONG a1 = 1; ULONGLONG a2 = 2; ULONGLONG a3 = 3; ULONGLONG a4 = 4; ULONGLONG b = _MyAdd(a1, a2, a3, a4); printf("b=%d\n", b); system("pause"); return 0; }

    效果图:

    vs2019 x64 内敛汇编

    打开生成依赖项 —— 生成自定义

    勾选 masm :

    在创建的 asm 文件的属性里选择:

    Processed: 0.012, SQL: 9