【问题标题】:GCC inline assembly error: Error: junk `(%esp)' after expressionGCC 内联汇编错误:错误:表达式后出现垃圾 `(%esp)'
【发布时间】:2024-05-01 20:25:02
【问题描述】:

GCC 内联汇编错误:Error: junk `(%esp)' after expression

我正在研究 gcc 内联汇编。我的环境是Win 7 32bit,mingw-gcc 4.6.1。

我遇到了关于“m”约束的问题。这是我的c函数代码:

static int asm_test(int a, int b)
{

    int c = 0;
    __asm__ __volatile__(".intel_syntax\n"
            "mov eax, %1\n" //error
            "mov edx, %2\n" //error
            "add eax, edx\n"
            "mov %0, eax\n" //error
            ".att_syntax"
            :"=m"(c)\
            :"m"(a),"m"(b)\
            :"eax","edx"
            );
    return c;
}

at&t 代码是这样的:

static int asm_test(int a, int b)
{

    int c = 0;
    __asm__ __volatile__(
            "movl %1, $eax\n" //error
            "movl %2, $edx\n" //error
            "addl $edx, $eax\n"
            "movl $eax, %0\n" //error
            :"=m"(c)\
            :"m"(a),"m"(b)\
            :"eax","edx"
            );
    return c;
}

对于操作输入/输出操作数的三行中的每一行,gcc在编译时都会产生一个错误,如下所示:

C:\Users\farta\AppData\Local\Temp\cc99HxYj.s:22: Error: junk `(%esp)' after expression

如果我使用 'r' 作为输入/输出约束,代码将起作用。但我无法理解它为什么起作用以及错误代表什么。谁能告诉我?据我所知,'m' 只是告诉 gcc 不要分配寄存器,而是直接在内存中访问它们,如果内联 asm 代码尝试访问输入/输出操作数。这是正确的吗?

非常感谢。

【问题讨论】:

  • 我不确定您是否可以在其中使用 Intel 语法。试试 AT&T。
  • 谢谢。我尝试过等效的 at&t asm 代码。错误信息都是一样的。我认为这不是由 Intel 语法引起的。
  • 很好地展示了 AT&T 语法。大多数做 GCC 内联汇编的人会比 Intel 更熟悉这一点。
  • AT&T 语法是:asm __volatile__( "movl %1, $eax" "movl %2, $edx" "addl $edx, $eax" "movl $eax , %0" :"=m"(c)\ :"m"(a),"m"(b)\ :"eax","edx" )
  • 请编辑您的问题以添加代码,在 cmets 中很难阅读。通常,当在 cmets 中请求澄清时,您应该始终编辑您的问题,以便每个人都能直接看到额外的信息。 (您可以使用的标签下方有一个“编辑”链接。)

标签: gcc assembly inline-assembly


【解决方案1】:

这里的问题是 GCC 为 %0%1%2 生成 AT&T 语法结构。如果您查看生成的程序集,它看起来像:

.intel_syntax
mov eax, 8(%ebp)
mov edx, 12(%ebp)
add eax, edx
mov -4(%ebp), eax

这不是有效的 Intel 语法。

通常,您不需要在内联汇编中包含显式加载/存储操作 - 只需指定寄存器约束,编译器将自行生成加载/存储。这样做的好处是,即使您的变量(参数、本地变量)根本不驻留在内存中,而是在寄存器中,您的代码仍然是正确的 - 与您显式将内存加载/存储放在那里的情况不同。

对于您的示例,请尝试以下代码,查看程序集 (gcc -S) 并注意编译器将如何自行从参数区域(例如 x86 上的堆栈)执行移动。

int asm_test(int a, int b)
{
  __asm__ __volatile__ (
                        ".intel_syntax\n"
                        "add %0, %1 \n"
                        ".att_syntax \n"
                        :"+r"(a)
                        :"r"(b));
  return a;

}

【讨论】:

  • 是的,您的代码有效。所以你的意思是大多数时候我们应该使用'r'?但是如果我们的 asm 代码稍微复杂一点,并且需要使用一些通用寄存器,我们需要在混合之前知道 gcc 为每个操作数使用了哪个寄存器。所以在这种情况下,我们应该使用'a', 'b', 'c', 'd' 来明确告诉gcc,这样对吗?
  • @farta,如果您使用需要特定寄存器(如imul)的insns,可以,但通常让GCC自行分配寄存器。
  • 另外,如果您想要 intel 语法内联汇编,请使用 -masm=intelgcc 切换到 intel。