将内联汇编代码转换为c++

Convert inline assembly code to C++

本文关键字:转换 c++ 代码 汇编      更新时间:2023-10-16

我正在做一个cpp项目。项目需要迁移到64位。它包含一些不能在x64上编译的内联汇编代码。这个函数包含汇编代码:

void ExternalFunctionCall::callFunction(ArgType resultType, void* resultBuffer)
{
#if defined(_NT_) || defined(__OS2__)
    // I386
    // just copy the args buffer to the stack (it's already layed out correctly)
    int* begin = m_argsBegin;
    int* ptr = m_argsEnd;
    int arr[1000], i=0;
    while (ptr > begin) {
        int val = *(--ptr);
        __asm push val
    }
    void* functionAddress = m_functionAddress;
    // call the function & handle the return value.  use __stdcall calling convention
    switch (resultType) {
    case voidType:
        __asm {
            call functionAddress
        }
        break;
    case pointerType:
    case int32Type:
        __asm {
            call functionAddress
            mov ebx, resultBuffer
            mov dword ptr [ebx],eax
        }
        break;
    case floatType:
        __asm {
            call functionAddress
            mov ebx, resultBuffer
            fstp dword ptr [ebx]
        }
        break;
    case doubleType:
        __asm {
            call functionAddress
            mov ebx, resultBuffer
            fstp qword ptr [ebx]
        }
        break;
    }

我使用堆栈,数组来迁移这个"asm push val",但没有工作。虽然,它没有抛出任何编译错误,但逻辑没有工作。

所以,我想问,我可以在c++中使用什么来代替"__asm push val"。

这个问题一般是无法解决的;这是因为注释

// call the function & handle the return value.  use __stdcall calling convention

表示依赖32位调用约定

在32位x86中,stdcall意味着所有参数都以相反的顺序在堆栈上传递(即首先推送最后一个参数)。也就是说,如果arg[0]addr,那么arg[1],不管它是什么类型,都在addr + sizeof(arg[0]))。这就是为什么示例中的代码如下:

// just copy the args buffer to the stack (it's already layed out correctly)
int* begin = m_argsBegin;
int* ptr = m_argsEnd;
int arr[1000], i=0;
while (ptr > begin) {
    int val = *(--ptr);
    __asm push val
}

实际上是可以工作的——因为它根本不关心那里到底是什么,参数是什么类型;所有相关的是它们每一个都在一个已知的内存位置,并且在内存中是连续的。如果你知道参数Naddr处,那么你可以知道参数N+1addr + sizeof(arg[N])处。

这就是注释所说的,"它已经正确地布局了"-不幸的是,在64位模式下,不是为真。因此代码不能被"移植";没有对应的port to。

至少部分基于寄存器的调用约定-如Win64在x64(64位x86)上的行为不同。对于这些,它取决于被调用函数接受的参数类型(在Windows中,您可以在通用寄存器中传递四个整型参数,在XMM寄存器中传递一些浮点型参数)。因此,您需要了解更多关于您调用的函数的签名(原型),而不仅仅是"它需要N参数",以便能够从上面的"anycall"类型包装器中正确封送参数。在64位模式下,对于您希望通过包装器调用的每个函数,您不仅需要知道总共有多少个参数,还需要知道有多少个在通用regs中,有多少在XMM regs中,以及有多少在堆栈中。

"通过指针调用函数并将返回值复制到已知位置"部分是可移植的,可以用普通的C/c++表示。但是得到参数的部分,如上文所述,不能以任何直接的方式在32位stdcall和任何64位x86调用约定之间移植 (Win64/x64和UN*X x86_64约定都不允许预测函数所有参数的位置和总堆栈内存使用情况,只给出参数的数量和类型,而不是它们的顺序)。

具体需要做什么更多地取决于上述class ExternalFunctionCall的调用者/用户,而不是您所展示的内联汇编的小样本。特别有必要知道成员m_argsBeginm_argsEnd是如何初始化的,以及在哪里初始化的。

您能提供更多关于该类(所有成员变量/函数)的细节,以及其实际使用的示例吗?

有几件事你需要解决。(我在另一个问题上查找了你的代码)。正如我所理解的,这段代码是一个包装器,调用位于指定地址的相当抽象的函数,该函数期望堆栈中有一定数量的数据,并且可以根据ArgType返回不同的东西。

如果你想用普通的C语言包装它,你必须定义几个函数原型(基于返回值),然后在你的开关上使用它们,但是你必须解决另一个问题,这是更棘手的。

在C中移植这些东西的问题是,你不知道你必须在堆栈中推入的参数的数量(数据大小),所以你将在定义原型时遇到麻烦。

让我们说func(char c)肯定会在堆栈中推入1字节(但也不总是正确的,因为数据对齐)。在你的情况下,你必须考虑一个解决方案,它将有一组参数与你需要在堆栈上的数据大小相等。乍一看,这不是你可以马上做到的。

乌利希期刊指南。你可以用func(char [] param);但它也有问题,在回答上面解释。

我知道这是一个有点老的问题,但我只是偶然发现:xbyak.

也许你在找什么?