【问题标题】:How to access local variables in GCC inline assembly while changing sp?如何在更改 sp 时访问 GCC 内联汇编中的局部变量?
【发布时间】:2015-02-11 21:51:22
【问题描述】:

考虑以下简化的示例函数:

void foo(void) {
    int t;
    asm("push %0\n\t"
        "push %0\n\t"
        "call bar"
        :
        : "m" (t)
        :
        );
}

如果我在没有优化的情况下使用 Cygwin x86 gcc 4.8.3 编译它,推送指令变为:

push -4(%ebp)
push -4(%ebp)

这很好。问题发生在 -O:

push 12(%esp)
push 12(%esp)

这显然是错误的。第一次推送更改 esp,然后第二次推送访问错误的位置。我有read that adding "%esp" to the clobber list should fix it,但它没有帮助。如何让 GCC 使用帧指针或正确考虑 esp 更改?

(假设从 bar 函数返回会将 esp 设置为它在 asm 语句之前的值。我只需要调用一个 thiscall 函数并为此使用内联汇编。)

【问题讨论】:

  • "r" (&t) 怎么样?这给了我push %rax\npush %rax
  • 这是“错误的”,因为 gcc 不对 asm 主体执行任何类型的语义分析。 gcc asm 不接受 %ebp%esp 作为约束 IIRC,也不接受 ELF (PIC) 平台上的 %ebx。我认为@DavidWohlferd 应该提交一个答案——这是一个不错的选择,因为%eax 无论如何都经常作为返回值被破坏。而是"a" (&t)
  • 好吧,如果我们想假设 %eax 正在被返回,那么我们可能应该做一些更像 "=a" (retval) : "0" (&t) 的事情。毕竟,我们必须让 gcc 知道 eax 的内容正在改变。我会说回调到 c++ 代码会很棘手。您是否在调用之前推送了被调用者可能更改的每个寄存器(并在返回时弹出它们)?您确定所有 bar 可能需要 gcc 存储在寄存器中的所有内容在调用之前都被刷新回内存吗?这里可能没有选项,但为此使用内联 asm 不是我的首选。
  • 使用“r”会起作用,但它有点混乱并导致其他问题。这只是一个简化的例子。我实际调用的函数有更多参数,并且破坏了一些寄存器。 GCC 不允许用于输入的寄存器在 clobber 列表中。我将不得不为一些被破坏的寄存器引入虚拟输出变量,这样我就不会用完寄存器。
  • 我看不出在这里使用“r”本质上是“混乱”的,尽管它可能会限制其他地方可用的寄存器数量(这可能会导致混乱)。虽然使用虚拟输出在美学上是不纯的,但我不知道使用它们对性能有任何实际影响。因此,虽然这可能确实是最好的解决方案,但如果不查看实际代码、了解调用约定等,就很难说。

标签: gcc inline-assembly gcc4


【解决方案1】:

我可以简单地使用__attribute__((optimize("-fno-omit-frame-pointer"))) 来防止在这一功能中引起问题的优化。这可能是最好的解决方案。 – 追梦者

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-05-19
    • 2019-12-31
    • 2012-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-20
    相关资源
    最近更新 更多