如何设置 gcc 以永久使用 intel 语法?

Den*_*nis 5 x86 assembly gcc inline-assembly intel-syntax

我有以下代码,可以用 gcc 命令很好地编译gcc ./example.c。程序本身调用函数“add_two”,它只是将两个整数相加。要在扩展汇编指令中使用 intel 语法,我需要首先切换到 intel,然后再切换回 AT&T。根据 gcc 文档,可以使用gcc -masm=intel ./exmaple.

每当我尝试使用 switch 编译它时,-masm=intel它都不会编译,我不明白为什么?我已经尝试删除该指令,.intel_syntax但它仍然无法编译。

#include <stdio.h>

int add_two(int, int);

int main(){
     int src = 3;
     int dst = 5;
     printf("summe = %d \n", add_two(src, dst));
     return 0;
}

int add_two(int src, int dst){

    int sum;

    asm (
        ".intel_syntax;"  //switch to intel syntax
        "mov %0, %1;"
        "add %0, %2;"

        ".att_syntax;"  //switch to at&t syntax
        : "=r" (sum) //output
        : "r" (src), "r" (dst) //input
    );

    return sum;
}
Run Code Online (Sandbox Code Playgroud)

通过编译上述程序的错误消息gcc -masm=intel ./example.c是:

tmp/ccEQGI4U.s: Assembler messages:
/tmp/ccEQGI4U.s:55: Error: junk `PTR [rbp-4]' after expression
/tmp/ccEQGI4U.s:55: Error: too many memory references for `mov'
/tmp/ccEQGI4U.s:56: Error: too many memory references for `mov' 
Run Code Online (Sandbox Code Playgroud)

Pet*_*des 9

在您的内联汇编中使用-masm=intel使用任何指令。.att_syntax 这适用于 GCC,我认为 ICC,以及您使用的任何约束。其他方法则不然。(请参阅 我可以在 GCC 中使用 x86 汇编的 Intel 语法吗?以获得一个简单的答案;这个答案探讨了到底出了什么问题,包括 clang 13 及更早版本。)

这也适用于clang 14及更高版本。(尚未发布,但该补丁是当前主干的一部分;请参阅https://reviews.llvm.org/D113707)。

Clang 13 及更早版本始终使用 AT&T 语法进行内联 asm,无论是替换操作数还是汇编为op src, dst。但更糟糕的是,即使使用像“: ... )`clang -masm=intel这样的方言替代品来获取 asm 模板的 Intel 端,也会这样做!asm ("add {att | intel}

clang -masm=intel在其内置汇编器将语句转换为指令的某种内部表示之后,仍然控制着它如何打印asm。asm()例如,Godbolt显示 clang13-masm=intel转动add %0, 1add dword ptr [1], eax,但 clang trunk 产生add eax, 1

这个答案中有关 clang 的其余部分尚未针对这个新的 clang 补丁进行更新。

Clang 确实支持 MSVC 风格的 asm 块内的 Intel 语法,但这很糟糕(没有限制,因此输入/输出必须通过内存。

如果您使用 clang 对寄存器名称进行硬编码,-masm=intel则可以使用(或等效的-mllvm --x86-asm-syntax=intel)。但它mov %eax, 5在 Intel 语法模式下会阻塞,因此您不能%0扩展为 AT&T 语法寄存器名称。


-masm=intel使编译器.intel_syntax noprefix在其 asm 输出文件的顶部使用,并在 inline-asm 语句之外从 C 生成 asm 时使用 Intel 语法。 在 asm 模板的底部使用.att_syntax会破坏编译器的 asm ,因此错误消息PTR [rbp-4]对于汇编器来说看起来像垃圾(需要 AT&T 语法)。

“mov 的操作数太多”是因为在 AT&T 语法中,mov eax, ebxmov从内存操作数(带有符号名称eax)到内存操作数(带有符号名称ebx


有些人建议使用.intel_syntax noprefixand .att_syntax prefixaround 你的 asm 模板。这有时可行,但有问题。并且与首选方法不兼容-masm=intel

“三明治”法的问题:

当编译器将操作数替换为 asm 模板时,它将根据-masm=. 对于内存操作数来说,这总是会中断(寻址模式语法完全不同)。

即使对于寄存器来说,它也会与 clang 中断。 Clang 的内置汇编器不接受%eaxIntel 语法模式下的寄存器名称,并且不接受.intel_syntax prefix(与noprefix通常与 Intel 语法一起使用的相反)。

考虑这个函数:

int foo(int x) {
    asm(".intel_syntax noprefix \n\t"
        "add  %0, 1  \n\t"
        ".att_syntax"
         : "+r"(x)
        );
    return x;
}
Run Code Online (Sandbox Code Playgroud)

它与 GCC ( Godbolt )的组装如下:

        movl    %edi, %eax
        .intel_syntax noprefix 
         add %eax, 1                    # AT&T register name in Intel syntax
        .att_syntax
Run Code Online (Sandbox Code Playgroud)

即使在 Intel 语法模式下,三明治方法也依赖于 GAS 接受%eax作为寄存器名称。GNU Binutils 的 GAS 可以,但 clang 的内置汇编器则不能。

在 Mac 上,即使使用真正的 GCC,asm 输出也必须使用as基于 clang 的汇编,而不是基于 GNU Binutils。

在该源代码上使用 clang 会抱怨:

<source>:2:35: error: unknown token in expression
    asm(".intel_syntax noprefix \n\t"
                                  ^
<inline asm>:2:6: note: instantiated into assembly here
        add %eax, 1
            ^
Run Code Online (Sandbox Code Playgroud)

(错误消息的第一行不能很好地处理多行字符串文字。如果您使用;代替\n\t并将所有内容放在一行上,则 clang 错误消息效果更好,但源代码很混乱。)


我没有检查"ri"当编译器选择立即数时约束会发生什么;$如果 GAS 在 Intel 语法模式下也默默地忽略它,它仍然会用 but IDK 来装饰它。


PS:您的 asm 语句有一个错误:您忘记了输出操作数上的早期破坏,因此没有什么可以阻止编译器为输出%0和输入选择相同的%2寄存器,直到第二条指令为止。然后mov将销毁一个输入。

但用作movasm 模板的第一条或最后一条指令通常也是一个错过优化的错误。在这种情况下,您可以而且应该使用lea %0, [%1 + %2]让编译器将结果非破坏性地添加到第三个寄存器。或者只是包装add指令(使用"+r"操作数和"r",让编译器担心数据移动。)如果无论如何都必须从内存加载值,它可以将其放入正确的寄存器中,因此不需要mov


PS:可以使用GNU C 内联 asm方言替代品-masm=intel来编写与或 一起使用的内联 asm 。例如att

void atomic_inc(int *p) {
    asm( "lock add{l $1, %0 | %0, 1}"
       : "+m" (*p)
       :: "memory"
    );
}
Run Code Online (Sandbox Code Playgroud)

编译为gcc -O2-masm=att默认)到

atomic_inc(int*):
    lock addl $1, (%rdi) 
    ret
Run Code Online (Sandbox Code Playgroud)

或者与-masm=intel

atomic_inc(int*):
    lock add DWORD PTR [rdi], 1
    ret
Run Code Online (Sandbox Code Playgroud)

请注意,lAT&T 需要后缀,而dword ptrintel 需要后缀,因为内存、立即数并不意味着操作数大小。并且编译器为这两种情况填写了有效的寻址模式语法。

这适用于 clang,但只有 AT&T 版本被使用。