【问题标题】:How to pass float as argument (inline-assembly)?如何将浮点数作为参数传递(内联汇编)?
【发布时间】:2018-03-31 16:45:29
【问题描述】:

如何将浮点数作为函数参数(外部调用)传递并在内联汇编中返回浮点数?下面的示例不起作用并且正在使应用程序崩溃。 cmets 是我的,所以它们也可能是错误的。

前两行是我自己添加的,只是为了这个例子。最初我从 st(0) 和 st(1) 上的两个浮点值开始,对此我无能为力。

fld     a               ; load float 'a' on st(0)
fld     b               ; load float 'b' on st(0), 'a' is now st(1)
sub     esp, 4          ; make room for float 
fstp    dword ptr [esp] ; push st(0) on stack, pop st(0)
mov     ecx, ebp        ; move 'this' on ecx
call    Class::ModValue ; returns float on st(0)
fcompp                  ; compare returned st(0) with st(1)    
fnstsw  ax
test    ah, 41h
jnz     Exit_label

上面的代码 sn-p 在asm{} 块内,前后还有更多不重要的代码。崩溃发生在此代码片段的第一行和ModValue 函数调用之间。

函数签名:

float Class::ModValue(float value)
{
    _LOG("ModValue") // doesn't show
    return value;
}

编译器:VisualStudio,架构:x86,调用约定:__thiscall

【问题讨论】:

  • IIRC, __thiscall 就像大多数 32 位调用约定一样,并在内存中传递 FP 参数(在 ESP 之上的同一个地方,你可以找到整数。调用堆栈,而不是 x87 寄存器堆)。 FP 值在st(0) 中返回。
  • 这不是minimal reproducible example。这个 inline 程序集是在 C++ 中的 asm{} 块内,还是在单独的 .asm 文件中?如果是后者,你怎么称呼它,显然它会崩溃,因为你的函数末尾没有ret。崩溃如何,在哪个指令上?使用你的调试器。
  • @PeterCordes 感谢您的建议,用更多信息编辑了问题。它在 C++ 中的 asm{} 块内
  • 显示minimal reproducible example 的周围代码,以及Class::ModValue 的原型。我认为__thiscall 是一个调用者弹出约定,在这种情况下,可以通过修改 ESP 的 inline-asm 块来解释崩溃。在函数调用后使用pop eax 从堆栈中删除参数。与其他寄存器不同(MSVC 在您破坏时会注意到),我认为 MSVC 假设您不修改 ESP。
  • 此外,如果您的代码将在本世纪制造的 CPU 上运行,您可以使用 fcomip 直接比较 EFLAGS。 (PPro 中的新功能)。不过,它只会弹出一次 x87 堆栈。此外,您真的需要使用旧版 x87 而不是 SSE 吗?通常您可以假设至少 SSE2 (Pentium4 / Athlon64),几乎可以肯定 SSE1 仅用于单精度 (Pentium III / Athlon XP)

标签: c++ assembly x86 inline-assembly calling-convention


【解决方案1】:

如果是__thiscall

根据msdn:

__thiscall 调用约定用于成员函数,是不使用可变参数的 C++ 成员函数使用的默认调用约定。在 __thiscall 下,被调用者清理堆栈,这对于 vararg 函数是不可能的。参数从右到左压入堆栈,this 指针通过寄存器 ECX 传递,而不是在 x86 架构上的堆栈上。

来源:https://msdn.microsoft.com/en-us/library/ek8tkfbw.aspx

然后是一个小例子:

float DoStuffs(void *pThis, float a)
{
    float flResult = 0.0f;

    __asm
    {
        push a;
        mov ecx, pThis;
        call Class::ModValue;
        fstp[flResult];
    }
    return flResult;
}

一些扩展示例:

class Test
{
public:
    float b;   
    float Add(float a);

};

float Test::Add(float a)
{
    return a + this->b;
}

float CallTest(void* pThis, float x)
{
    float flResult = 0.0f;

    __asm
    {
        push x;
        mov ecx, pThis;
        call Test::Add;
        fstp[flResult];
    }

    return flResult;
}

int main()
{
    void* m = malloc(4);

    *(float*)m = 2.0f;

    std::cout << CallTest(m, 2.1f) << std::endl;

    return 0;
}

【讨论】:

  • add esp, 4 在调用 __thiscall 函数后不正确,根据您引用的文档:“在 __thiscall 下,被调用者清理堆栈”
猜你喜欢
  • 2014-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-22
  • 1970-01-01
  • 2019-06-06
相关资源
最近更新 更多