【问题标题】:Convert inline assembly code to C++将内联汇编代码转换为 C++
【发布时间】:2013-04-15 01:40:56
【问题描述】:

我正在做一个 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”。任何帮助将不胜感激。

【问题讨论】:

  • 这不是程序中唯一的汇编指令吗?你如何使用推送的值?目前没有足够的信息来帮助您。
  • 您需要显示更多代码,以便我们知道对压入堆栈的值做了什么。
  • 标准 C++ 不包括内联汇编语言或任何用于直接修改 CPU 堆栈的操作....如果它正在为函数调用或其他东西准备值,则可能有其他方式来提供参数。它可能高度特定于您的操作系统和/或编译器,您没有说明。
  • 这是某种“可变参数”函数调用吗?如果是这样,论点的范围有多大?
  • 您好 Konrad/Michael/Tony/Mats,现在您可以看到完整的函数代码,其中包含 asm 代码。

标签: c++ inline-assembly


【解决方案1】:

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

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

表示依赖32位调用约定

在 32 位 x86 中,stdcall 表示 所有 参数以相反的顺序在堆栈上传递(即最后一个参数首先被压入。也就是说,如果 arg[0] 位于 @987654326 @ 然后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 位模式下不是正确的。因此代码不能被“移植”;没有等价于端口。

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

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

您究竟需要做什么取决于更多上述class ExternalFunctionCall 的调用者/用户,而不是您展示的内联汇编的小样本。尤其需要知道成员m_argsBeginm_argsEnd 是如何初始化的以及在哪里初始化的。您能否提供更多关于该类的外观(所有成员变量/函数)的详细信息,以及其实际使用的示例?

【讨论】:

  • 谢谢弗兰克,我想再问一件事。我们不能在 asm/s 文件中分离这个 asm 代码吗?一个单独的 asm 文件可以通过 MASM64.exe 在 x64 上编译。如果可能的话,请告诉我我们该怎么做?我们已将所有其他 asm 代码迁移到 cpp,只剩下“__asm push val”。上面可以看到完整函数的代码。
  • @user2118116:如前所述,除了简单地“将__asm push val 替换为一些 64 位”之外,还有更多要做。这个“更多”在函数之外 - 它在整个类中以及both,这就是你最大的工作所在,在实际位置创建/使用此类实例的位置。没有简单的出路,对不起。您关于将其移植到 64 位的问题有点像说“我的 x86 代码在内联汇编中使用 lcall,我需要将它移植到 ARM,所以我想知道执行 lcall 的 ARM 指令”。
  • @FrankH。对此完全确认。我在此副本中建议了 OP:「你能做的最好的事情是改用 libffidyncall 重写整个函数。」
【解决方案2】:

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

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

在 C 中移植此类内容的问题是,您事先不知道必须推入堆栈的参数数量(数据大小),因此您将难以定义原型。

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

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

【讨论】:

  • 你知道,答案还不错,但最后一段,特别是“你必须考虑解决方案”,对我来说有点毁了它。如果他们能想到一些事情,OP就不会寻求帮助。他们显然非常努力地尝试自己解决。
  • 很抱歉让你失望了。可能是“你必须思考”确实有点难,但我有借口说我不是以英语为母语的人。我的意思是我真的不知道最后一步的确切解决方案,我写的更多的是一种赞美的想法,而不是现成的答案。另一件事是,从我在 OP 问题中读到的内容(实际上是两者),我认为“显然非常努力地解决”是适用的。
【解决方案3】:

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

也许你在找什么?

【讨论】:

  • 嗨 Nix,我想从这个上下文中替换 asm 代码:​​ int* begin = m_argsBegin; int* ptr = m_argsEnd; int arr[1000], i=0; while (ptr > begin) { int val = *(--ptr); __asm push val } 基本上我想用 Cpp 代码替换语句“__asm push val”。谢谢
  • 什么是 m_argsBegin 和 m_argsEnd?
  • @Nix- 这两个都是 int* 谢谢
猜你喜欢
  • 2021-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-01
  • 1970-01-01
  • 2020-11-04
  • 2011-05-04
相关资源
最近更新 更多