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

vso*_*oni 6 c++ inline-assembly

我正在研究一个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;
    }
Run Code Online (Sandbox Code Playgroud)

我使用stack,array来迁移这个"asm push val"但是没有用.虽然,它不会抛出任何编译错误,但逻辑没有奏效.

所以,我想问一下,我可以在C++中使用什么而不是"__asm push val".任何帮助将不胜感激.

Fra*_*kH. 13

这个问题一般无法解决; 那是因为评论,

// call the function & handle the return value.  use __stdcall calling convention
Run Code Online (Sandbox Code Playgroud)

表示依赖于32位呼叫约定.

在32位x86中,stdcall意味着所有参数都以相反的顺序在堆栈上传递(即最后一个arg被推送.也就是说,如果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
}
Run Code Online (Sandbox Code Playgroud)

实际上可以工作 - 因为它究竟是什么并不重要,参数是什么类型的; 所有相关的是它们中的每一个都在已知的存储位置,并且已知在内存中是连续的.如果你知道参数N是在addr,那么你可以知道参数N+1是在addr + sizeof(arg[N]).

这就是评论所说的" 它已经正确布局 " - 不幸的是,在64位模式下并非如此.因此代码不能"移植"; 没有相当于港口的.

调用至少部分基于寄存器的约定 - 就像x64(64位x86)上的Win64一样,表现不同.对于那些,它取决于被调用函数采用什么类型的参数(在Windows中,您可以在通用寄存器中传递四个整数类型的args,在寄存器中传递一些float类型的args XMM).因此,您需要了解更多关于您调用的函数的签名(原型),而不仅仅是"它需要N参数",以便能够正确地编组来自"anycall"类型包装器的args,如上所述.在64位模式下,对于您希望通过包装器调用的每个功能,您不仅需要知道总共有多少个args,还需要知道通用regs中有多少个,XMMreg中有多少个,以及有多少个在堆栈上.

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

你需要做什么更多地取决于上面的调用者/用户,而class ExternalFunctionCall不是你所展示的内联汇编的小样本.特别需要知道成员m_argsBeginm_argsEnd初始化的方式以及在何处.您能否提供一些关于类的外观(所有成员变量/函数)的详细信息,以及实际使用的示例?


evi*_*uff 0

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

如果你想通过普通的 C 来包装它,你必须定义几个函数原型(基于返回值)以便在你的 switch 上使用,但是你必须解决另一个更棘手的问题。

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

假设 func(char c) 肯定会推入堆栈 1 个字节(但由于数据对齐,也并不总是正确)..在您的情况下,您必须考虑具有与数据大小相同大小的参数集的解决方案你需要在堆栈上。乍一看这不是你可以立即完成的事情。

UPD。你可以用 func( char [] param ); 来做到这一点 但它也存在上面答案中解释的问题。