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

时间:2013-04-22 08:11:01

标签: 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;
    }

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

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

3 个答案:

答案 0 :(得分:13)

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

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

表示依赖 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
}

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

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

调用至少部分基于寄存器的约定 - 就像x64(64位x86)上的Win64一样,行为也不同。对于那些,它取决于被调用函数采用何种类型的参数(在Windows中,您可以在通用寄存器中传递四个整数类型的args,在XMM寄存器中传递一些float类型的args)。因此,您需要知道 more 关于您调用的函数的签名(原型),而不仅仅是“它需要N个参数”,以便能够正确地编组“anycall”中的args像上面这样的类型包装器。在64位模式下,对于您希望通过包装器调用的每个函数,您不仅要知道总共有多少个args,还要知道通用regs中有多少个,XMM中有多少个regs,堆栈中有多少。

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

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

答案 1 :(得分:0)

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

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

在C语言中移植此类内容的问题在于,您不必预先知道必须推入堆栈的参数(数据大小),因此您将无法定义原型。

假设func(char c)肯定会推入堆栈1字节(但由于数据对齐也不一定正确)..在你的情况下你必须考虑解决方案,它将具有相同大小的参数集您需要在堆栈上的数据大小。乍一看,你不能马上做什么。

UPD。你可以用func(char [] param)来做;但它也有上面回答中解释的问题。

答案 2 :(得分:0)

我意识到这是一个有点老问题,但我偶然发现:xbyak

也许你在寻找什么?