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

6

我正在处理一个 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"。任何帮助都将不胜感激。


1
这肯定不是程序中唯一的汇编指令吧?你如何使用推送的值?目前还没有足够的信息来帮助你。 - Konrad Rudolph
1
你需要展示更多的代码,这样我们才能知道将值推送到堆栈上后正在执行什么操作。 - Michael Burr
2
标准C++不包括内联汇编语言或任何直接修改CPU堆栈的操作...如果您解释一下函数在堆栈上的操作,那么您将有更多有用的建议。如果它正在为函数调用或其他操作准备值,则可能有其他方法来提供参数。这很可能高度特定于您未说明的操作系统和/或编译器。 - Tony Delroy
1
这是一种“可变参数”函数调用吗?如果是,参数的范围有多广? - Mats Petersson
大家好,Konrad/Michael/Tony/Mats:现在你们可以看到完整的函数代码,其中包含汇编代码。 - vsoni
你是否能够控制通过 m_functionAddress 调用的函数? - StoryTeller - Unslander Monica
3个回答

13

这个问题通常无法解决;因为评论中说到,

// 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位模式下,这是正确的。因此,代码无法被"移植";没有等价的方法可供移植。

至少部分基于寄存器的调用惯例 - 如Win64在x64(64位x86)上 - 表现不同。对于这些,这取决于调用函数的参数类型(在Windows中,您可以在通用寄存器中传递四个整数类型的参数以及一些浮点类型的参数)。因此,您需要了解所调用的函数的签名(原型)比仅仅知道"它需要N个参数"更多,才能够正确地从上述的"anycall"类型包装器进行参数转换。在64位模式下,对于每个您希望通过包装器调用的函数,您不仅需要知道总共有多少个参数,还需要知道有多少个参数在通用寄存器中,有多少个参数在XMM寄存器中,在堆栈中有多少个参数。

"通过指针调用函数并将返回值复制到已知位置"部分是可移植的,并且可以用普通的C/C++表达。但是,正如上面所述,获取这些参数的部分在32位的stdcall和我所知道的任何64位x86调用惯例之间都不能以直观的方式进行转换(无论是Win64/x64还是UN*X的x86_64惯例,都不能仅仅根据参数数量和类型而不了解它们的顺序来预测所有参数的位置和总堆栈内存使用情况)。

您需要做什么取决于上述class ExternalFunctionCall的调用者/用户而不是您展示的行内汇编代码的小样本。尤其需要了解成员m_argsBeginm_argsEnd如何初始化以及它们在哪里。能否提供一些有关该类的外观(所有成员变量/函数)以及其实际使用的示例的更多细节?


谢谢Frank,我还想问一件事。我们不能将这个asm代码分离到asm/s文件中吗?一个单独的asm文件可以通过MASM64.exe在x64上编译。如果可能,请告诉我如何做到这一点? 我们已经将所有其他asm代码迁移到cpp,只剩下“__asm push val”了。您可以在上面看到完整函数的代码。 - vsoni
@user2118116:正如已经提到的,要做的不仅仅是简单地“用一些64位的东西替换__asm push val”。这个“更多”的工作在函数之外——它既涉及整个类,也涉及你最大的工作量的地方——在创建/使用该类实例的实际位置。这里没有简单的方法,抱歉。你关于将其移植到64位的问题有点像说“我的x86代码在内联汇编中使用了lcall,我需要将其移植到ARM,所以我想知道执行lcall操作的ARM指令”。 - FrankH.
@FrankH。完全同意这个观点。我在一个重复的帖子中建议了OP:「最好的做法是使用libffidyncall重新编写整个函数。」 - mirabilos

0

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

如果你想通过纯C来进行包装,你必须定义几个函数原型(基于返回值),然后在你的 switch 语句中使用它们,但接着你就会遇到另一个更棘手的问题。

将这样的东西移植到C中的问题在于,你无法预先知道你需要推入堆栈的参数数量(数据大小),因此你将难以定义原型。

假设 func(char c) 肯定会推入堆栈1字节(但也不总是正确的,因为存在数据对齐问题)... 在你的情况下,你必须考虑一种解决方案,该方案具有与你需要放入堆栈的数据大小相等的一组参数。这初看起来并不是你能立即完成的事情。

更新:你可以使用 func( char [] param ) 来实现它,但它还有一些问题,在上面的答案中已经说明了。


你知道吗,这不是一个糟糕的回答,但最后一段,特别是“你必须考虑解决方案”,让我有些失望。如果他们能想到什么,那么提问者就不会寻求帮助了。他们显然已经很努力地自己解决问题了。 - StoryTeller - Unslander Monica
很抱歉让你失望了。也许“你必须思考”确实有点难,但我有借口,因为我不是以英语为母语的人。我的意思是,我真的不知道最后一步的确切解决方案,我写的更像是大声思考而不是现成的答案。另外,从我在OP问题中读到的内容(实际上是两个问题),我认为“显然非常努力地解决”是适用的。 - evilruff

0

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

也许这就是你要找的东西?


嗨Nix, 我想要替换这个上下文中的汇编代码: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”。 谢谢 - vsoni
m_argsBegin和m_argsEnd是什么? - Geoff Nixon
@Nix- 这两个都是int*。 - vsoni

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接