【问题标题】:Why passing 4-bytes arguments to a function expecting 2-bytes parameters works?为什么将 4 字节参数传递给需要 2 字节参数的函数有效?
【发布时间】:2014-02-09 01:02:26
【问题描述】:

查看使用 VC++ 2003/2005 编译的 C 代码。

#include <stdio.h>
#include <windows.h>

void WINAPI SomeFunction(WORD a, WORD b)
{
    printf("%d + %d = %d\n", a, b, a + b);
}

int main()
{
    __asm {
        MOV EAX, 5
        MOV EBX, 6
        PUSH EBX
        PUSH EAX
        CALL SomeFunction
    }

    return 0;
}

在此 ASM 代码中,我将两个 DWORD(4 字节)参数(据我所知默认情况下)传递给 SomeFunction() 函数,该函数需要两个 WORD(2 字节)参数,并且可以正常工作完美(输出5 + 6 = 11)。

1) 为什么会这样?

知道该函数需要两个 WORD 参数,我会这样做:

__asm {
    MOV WORD PTR [EAX], 5
    MOV WORD PTR [EBX], 6
    PUSH EBX
    PUSH EAX
    CALL SomeFunction
}

在调试时会引发分段错误错误。

2) 为什么这不起作用

提前致谢!

【问题讨论】:

  • “完美工作”对不同的人可能有不同的含义。

标签: c visual-c++ assembly x86 arguments


【解决方案1】:

第一个有效,因为 Win32 ABI 说任何大小小于或等于 4 字节的参数都作为 4 字节传递,必要时进行填充。所以 16 位字实际上是作为 32 位传递的。这就是你正在做的事情。

第二个不起作用,因为它做了不同的事情:

MOV WORD PTR [EAX], 5

该行将5 移动到从EAX 指向的内存位置开始的16 位字中。但是EAX 之前没有加载有效的内存地址。此外,您正在推动指针(WORD*?)。

为了在堆栈中传递一个 16 位的值,您可以使用:

MOV AX, 5
MOV BX, 6
PUSH AX
PUSH BX

但这又与 Win32 ABI 相悖,因为堆栈始终是 32 位对齐的。

有趣的是,如果你通过这个结构按值(未经测试),这将起作用:

struct WW
{
    WORD a, b;
};

void WINAPI SomeFunction(WW w)
{
    printf("%d + %d = %d\n", w.a, w.b, w.a + w.b);
}

int main()
{
    __asm {
        MOV BX, 6 // the parameters are reversed, methinks
        MOV AX, 5
        PUSH BX
        PUSH AX
        CALL SomeFunction
    }

    return 0;
}

这是因为结构的字段被打包成 4 个字节 (sizeof(WW)==4),所以这就是复制到堆栈中的内容。

当然,玩 16 位寄存器并不好玩。这样做可能更好:

MOV EAX 0x00060005
PUSH EAX

并一次性复制整个 32 位结构。

【讨论】:

  • 所以我的第一个 ASM 调用是更好的方法,即使参数是 2 字节?
  • @cdonts:确实!该代码有效,不是偶然,而是因为它是正确的。如果你编译SomeFunction(5,6)你会看到编译器生成了类似的代码。
猜你喜欢
  • 2015-02-26
  • 1970-01-01
  • 2013-02-21
  • 1970-01-01
  • 2012-07-06
  • 2016-08-07
  • 2023-01-12
  • 1970-01-01
相关资源
最近更新 更多