C/C++ 内联汇编不正确的操作数类型

Posted

技术标签:

【中文标题】C/C++ 内联汇编不正确的操作数类型【英文标题】:C/C++ Inline asm improper operand type 【发布时间】:2016-06-15 21:30:06 【问题描述】:

我有以下代码,应该对一块内存进行异或:

void XorBlock(DWORD dwStartAddress, DWORD dwSize, DWORD dwsKey)

DWORD dwKey;
__asm

    push eax
    push ecx
    mov ecx, dwStartAddress          // Move Start Address to ECX
    add ecx, dwSize                  // Add the size of the function to ECX
    mov eax, dwStartAddress          // Copy the Start Address to EAX

    crypt_loop:                         // Start of the loop
        xor byte ptr ds:[eax], dwKey     // XOR The current byte with 0x4D
        inc eax                         // Increment EAX with dwStartAddress++
        cmp eax,ecx                     // Check if every byte is XORed
    jl crypt_loop;                      // Else jump back to the start label

    pop ecx // pop ECX from stack
    pop eax // pop EAX from stack


但是,参数 dwKey 给了我一个错误。例如,如果将 dwKey 替换为 0x5D,则代码可以完美运行。

【问题讨论】:

是什么让你认为这比用 C 或 C++ 编写更快?而且它有多快并不重要,因为凯撒密码可以在几毫秒内破解。 DWORD dwsKey 是错字。似乎额外的s 是一个错误?然后删除局部变量DWORD dwKey; ? 这段代码有什么意义?您是否故意编写慢速 asm 以阻止编译器使用 4 字节异或或 SSE2 PXOR? 【参考方案1】:

我认为你有两个问题。

首先,“xor”不能取两个内存操作数(ds:[eax]是内存位置,dwKey是内存位置);其次,您使用“byte ptr”表示您需要一个字节,但您尝试使用 DWORD 并且程序集无法自动转换它们。

因此,您可能必须将值加载到 8 位寄存器中,然后再执行此操作。例如:

void XorBlock(DWORD dwStartAddress, DWORD dwSize, DWORD dwsKey)

    DWORD dwKey;
    __asm
    
        push eax
        push ecx
        mov ecx, dwStartAddress          // Move Start Address to ECX
        add ecx, dwSize                  // Add the size of the function to ECX
        mov eax, dwStartAddress          // Copy the Start Address to EAX
        mov ebx, dwKey                   // <---- LOAD dwKey into EBX

        crypt_loop :                         // Start of the loop
            xor byte ptr ds : [eax], bl     // XOR The current byte with the low byte of EBX
            inc eax                         // Increment EAX with dwStartAddress++
            cmp eax, ecx                     // Check if every byte is XORed
            jl crypt_loop;                      // Else jump back to the start label

        pop ecx // pop ECX from stack
        pop eax // pop EAX from stack
    

虽然,看起来 dwKey 在您的代码中未初始化;也许你应该只是“mov bl,0x42”。我也不确定您是否需要推送和弹出寄存器;我不记得允许使用 MSVC++ 内联汇编器破坏哪些寄存器。

但是,最后,我认为 Alan Stokes 在他的评论中是正确的:在这种情况下,汇编实际上不太可能比 C/C++ 代码更快。编译器可以轻松地自行生成此代码,并且您可能会发现编译器实际上进行了意想不到的优化,使其运行速度比“明显”的程序集运行得更快(例如,loop unrolling)。

【讨论】:

我相信__asm 会在需要时负责保存和恢复__asm 语句中使用的寄存器。 MSVC 分析 __asm 以确定寄存器使用情况。关于唯一需要手动处理(保留)的寄存器(除了选择器)是 ESP 和 EBP。 GCC 不分析内联汇编语句,但 MSVC 可以。 "当使用 __asm 在 C/C++ 函数中编写汇编语言时,您不需要保留 EAX、EBX、ECX、EDX、ESI 或 EDI 寄存器" (msdn.microsoft.com/en-us/library/k1a8ss06.aspx)。该页面还暗示它会执行 Michael 提到的分析,以优化需要自动保存/恢复的寄存器。

以上是关于C/C++ 内联汇编不正确的操作数类型的主要内容,如果未能解决你的问题,请参考以下文章

高级CGNU C/C++ 内联汇编——Intel与ATT汇编语法对比

(C/C++) 内联汇编“add dword ptr [address]”增加地址而不是它的值

C/C++ 中的简单“Hello World”内联汇编语言程序

x86平台转x64平台关于内联汇编不再支持的解决

高级CGNU C/C++ 内联汇编——实例参考

高级CGNU C/C++ 内联汇编——实例参考