【问题标题】:Calling a function that can be either cdecl or stdcall调用可以是 cdecl 或 stdcall 的函数
【发布时间】:2013-07-11 15:18:57
【问题描述】:

我需要编写调用外部函数的代码,该函数可以是 32 位 Windows 应用程序中的 stdcall 调用或 cdecl。
我的代码,调用者,不能提前知道它会是哪一个。 现在,如果我尝试从定义为 stdcall 的调用站点调用 cdecl 函数,我会得到一个 checkEsp 异常对话框,我猜这是有充分理由的。
有什么办法吗?

【问题讨论】:

  • 尝试使用外部函数接口 (FFI) 库。
  • FFI 仍然需要知道调用约定
  • @HansPassant 参数顺序相同

标签: assembly x86 calling-convention stdcall cdecl


【解决方案1】:

可以通过以下方式完成:

          mov     esi, esp

          push    arg3
          push    arg2
          push    arg1
          call    [SomeExternalProc]

          mov     esp, esi   ; now the stack is always properly cleaned 

外部过程将保留 esi。或者,您可以使用由外部过程甚至内存变量保留的任何其他寄存器 - 本地或全局。

好的,CDECL 和 STDCALL 的参数顺序是相同的 - 顺序相反。 (最左边的 arg 在最低地址。)所以它们是兼容的,除了 ESP 在返回时指向的位置。两种约定都同意哪些寄存器是调用保留的还是调用破坏的。

【讨论】:

    【解决方案2】:

    你也可以使用alloca(),它具有保存和恢复堆栈指针的副作用:

    {
        alloca( (uintptr_t)callback & 2 );
        callback();
    }
    

    【讨论】:

      【解决方案3】:

      cdecl 和 stdcall 在定义上是不兼容的。在 cdecl 中,调用者清理堆栈,在 stdcall 中,被调用者清理堆栈。如果你假设stdcall,但实际上是cdecl,没有人清理堆栈。这意味着您的 ESP(堆栈指针)在调用后将被搞砸。也许如果您提供更多详细信息,可能会有一种解决方法,但是没有办法在不知道它的调用约定的情况下调用一个函数而不会弄乱您的堆栈。

      有关差异的定义,请参阅:http://en.wikipedia.org/wiki/X86_calling_conventions

      【讨论】:

      • 嗯,有没有一种可靠的方法来检查这种情况并在需要时修复 ESP? checkEsp 到底做了什么?
      • ESP 是你的堆栈指针。它跟踪您的堆栈当前所在的位置。从理论上讲,您可以将 ESP 保存到堆上的变量中并在调用后检索它,但这会非常混乱......而且我从未尝试过,所以我什至不确定这是否可行。我不确定 ESP 用于确保 ESP 在通话前后得到正确维护的机制检查。更好的策略是检查调用约定并在此基础上调整调用,尽管我还没有看到在运行时以编程方式检查调用约定的一致方法。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-13
      • 1970-01-01
      • 2023-03-20
      • 1970-01-01
      • 1970-01-01
      • 2011-04-06
      相关资源
      最近更新 更多