⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 在 visual c++ 中使用内联汇编.txt

📁 在 Visual C++ 中使用内联汇编
💻 TXT
📖 第 1 页 / 共 2 页
字号:
                                    ; to arguments on the stack
            MOV     EAX, [EBP+4]    ; Get first argument
            MOV     ECX, [EBP+6]    ; Get second argument
            SHL     EAX, CL         ; EAX = EAX * (2 ^ CL)
            POP     EBP             ; Restore EBP
            RET                     ; Return with sum in EAX
        GetPowerAsm ENDP
        _TEXT       ENDS
                    END

    C/C++ 函数一般用堆栈来传递参数,所以上面的函数中需要通过堆栈位置来访问它的参数(在 MASM 或其它一些汇编工具中,也允许通过名称来访问堆栈参数和局部堆栈变量)。

    下面的程序是使用内联汇编写的:

        // PowerC.c

        #include <Stdio.h>

        int GetPowerC(int iNum, int iPower);

        int main()
        {
            printf("3 times 2 to the power of 5 is %d\n", GetPowerC( 3, 5));
        }

        int GetPowerC(int iNum, int iPower)
        {
            __asm
            {
                MOV EAX, iNum   ; Get first argument
                MOV ECX, iPower ; Get second argument
                SHL EAX, CL     ; EAX = EAX * (2 to the power of CL)
            }
            // Return with result in EAX
        }

    使用内联汇编写的 GetPowerC 函数可以通过参数名称来引用它的参数。由于 GetPowerC 函数没有执行 C 的 return 语句,所以编译器会给出一个警告信息,我们可以通过 #pragma warning 禁止生成这个警告。

    内联汇编的其中一个用途是编写 naked 函数的初始化和结束代码。对于一般的函数,编译器会自动帮我们生成函数的初始化(构建参数指针和分配局部变量等)和结束代码(平衡堆栈和返回一个值等)。使用内联汇编,我们可以自己编写干干净净的函数。当然,此时我们必须自己动手做一些有关函数初始化和扫尾的工作。例如:

        void __declspec(naked) MyNakedFunction()
        {
            // Naked functions must provide their own prolog.
            __asm
            {
                PUSH EBP
                MOV ESP, EBP
                SUB ESP, __LOCAL_SIZE
            }

            .
            .
            .

            // And we must provide epilog.
            __asm
            {
                POP EBP
                RET
            }
        }

6. 调用 C/C++ 函数

    内联汇编中调用声明为 __cdecl 方式(默认)的 C/C++ 函数必须由调用者清除参数堆栈,下面是一个调用 C/C++ 函数例子:

        #include <Stdio.h>

        char szFormat[] = "%s %s\n";
        char szHello[] = "Hello";
        char szWorld[] = " world";

        void main()
        {
            __asm
            {
                MOV     EAX, OFFSET szWorld
                PUSH    EAX
                MOV     EAX, OFFSET szHello
                PUSH    EAX
                MOV     EAX, OFFSET szFormat
                PUSH    EAX
                CALL    printf

                // 压入了 3 个参数在堆栈中,调用函数之后要调整堆栈
                ADD     ESP, 12
            }
        }

    * 提示:参数是按从右往左的顺序压入堆栈的。

    如果调用 __stdcall 方式的函数,则不需要自己清除堆栈。因为这种函数的返回指令是 RET n,会自动清除堆栈。大多数 Windows API 函数均为 __stdcall 调用方式(仅除 wsprintf 等几个之外),下面是一个调用 MessageBox 函数的例子:

        #include <Windows.h>

        TCHAR g_tszAppName[] = TEXT("API Test");

        void main()
        {
            TCHAR tszHello[] = TEXT("Hello, world!");

            __asm
            {
                PUSH    MB_OK OR MB_ICONINFORMATION
                PUSH    OFFSET g_tszAppName         ; 全局变量用 OFFSET
                LEA     EAX, tszHello               ; 局部变量用 LEA
                PUSH    EAX
                PUSH    0
                CALL    DWORD PTR [MessageBox]      ; 注意这里不是 CALL MessageBox,而是调用重定位过的函数地址
            }
        }

    * 提示:可以不受限制地访问 C++ 成员变量,但是不能访问 C++ 的成员函数。

7. 定义 __asm 块为 C/C++ 宏

    使用  C/C++ 宏可以方便地把汇编代码插入到源代码中。但是这其中需要额外地注意,因为宏将会扩展到一个逻辑行中。
为了不会出现问题,请按以下规则编写宏:

    * 使用花括号把 __asm 块包围住;
    * 把 __asm 关键字放在每条汇编指令之前;
    * 使用经典 C 风格的注释(“/* comment */”),不要使用汇编风格的注释(“; comment”)或单行的 C/C++ 注释(“// comment”);

    举个例子,下面定义了一个简单的宏:

        #define PORTIO __asm        \
        /* Port output */           \
        {                           \
            __asm MOV AL, 2         \
            __asm MOV DX, 0xD007    \
            __asm OUT DX, AL        \
        }

    乍一看来,后面的三个 __asm 关键字好像是多余的。其实它们是需要的,因为宏将被扩展到一个单行中:

        __asm /* Port output */ {__asm MOV AL, 2  __asm MOV DX, 0xD007  __asm OUT DX, AL}

    从扩展后的代码中可以看出,第三个和第四个 __asm 关键字是必须的(作为语句分隔符)。在 __asm 块中,只有 __asm 关键字和换行符会被认为是语句分隔符,又因为定义为宏的一个语句块会被认为是一个逻辑行,所以必须在每条指令之前使用 __asm 关键字。

    括号也是需要的,如果省略了它,编译器将不知道汇编代码在哪里结束,__asm 块后面的 C/C++ 语句看起来会被认为是汇编指令。

    同样是由于宏展开的原因,汇编风格的注释(“; comment”)和单行的 C/C++ 注释(“// commen”)也可能会出现错误。为了避免这些错误,在定义 __asm 块为宏时请使用经典 C 风格的注释(“/* comment */”)。

    和 C/C++ 宏一样 __asm 块写的宏也可以拥有参数。和 C/C++ 宏不一样的是,__asm 宏不能返回一个值,因此,不能使用这种宏作为 C/C++ 表达式。

    不要不加选择地调用这种类型的宏。比如,在声明为 __fastcall 的函数中调用汇编语言宏可能会导致不可预料的结果(请参看前文的说明)。

8. 转跳

    可以在 C/C++ 里面使用 goto 转跳到 __asm 块中的标号处,也可以在 __asm 块中转跳到 __asm 块里面或外面的标号处。__asm 块内的标号是不区分大小写的(指令、指示符等也是不区分大小写的)。例如:

        void MyFunction()
        {
            goto C_Dest;    /* 正确 */
            goto c_dest;    /* 错误 */

            goto A_Dest;    /* 正确 */
            goto a_dest;    /* 正确 */

            __asm
            {
                JMP C_Dest  ; 正确
                JMP c_dest  ; 错误

                JMP A_Dest  ; 正确
                JMP a_dest  ; 正确

        a_dest:             ; __asm 标号
            }

        C_Dest:             /* C/C++ 标号 */
            return;
        }

    不要使用函数名称当作标号,否则将转跳到函数中执行,而不是标号处。例如,由于 exit 是 C/C++ 的函数,下面的转跳将不会到 exit 标号处:

        ; 错误:使用函数名作为标号
        JNE exit
        .
        .
        .
        exit:
        .
        .
        .

    美元符号“$”用于指定当前指令位置,常用于条件跳转中,例如:

        JNE $+5     ; 下面这条指令的长度是 5 个字节
        JMP _Label
        NOP         ; $+5,转跳到了这里
        .
        .
        .
        _Label:
        .
        .
        .


五、在 Visual C++ 工程中使用独立汇编

    内联汇编代码不易于移植,如果你的程序打算在不同类型的机器(比如 x86 和 Alpha)上运行,你可能需要在不同的模块中使用特定的机器代码。这时候你可以使用 MASM(Microsoft Macro Assembler),因为 MASM 支持更多方便的宏指令和数据指示符。

    这里简单介绍一下在 Visual Studio .NET 2003 中调用 MASM 编译独立汇编文件的步骤。

    在 Visual C++ 工程中,添加按 MASM 的要求编写的 .asm 文件。在解决方案资源管理器中,右击这个文件,选择“属性”菜单项,在属性对话框中,点击“自定义生成步骤”,设置如下项目:

    命令行:ML.exe /nologo /c /coff "-Fo$(IntDir)\$(InputName).obj" "$(InputPath)"
    输出:$(IntDir)\$(InputName).obj

    如果要生成调试信息,可以在命令行中加入“/Zi”参数,还可以根据需要生成 .lst 和 .sbr 文件。

    如果要在汇编文件中调用 Windows API,可以从网上下载 MASM32 包(包含了 MASM 汇编工具、非常完整的 Windows API 头文件/库文件、实用宏以及大量的 Win32 汇编例子等)。相应地,应该在命令行中加入“/I X:\MASM32\INCLUDE”参数指定 Windows API 汇编头文件(.inc)的路径。MASM32 的主页是:http://www.masm32.com,里面可以下载最新版本的 MASM32 包。

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -