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

时间:2016-08-15 11:09:04

标签: gcc assembly x86 inline-assembly

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

每当我尝试使用开关-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;
}

通过使用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' 

2 个答案:

答案 0 :(得分:2)

请注意,-masm=也是affects the default inline assembler语法:

  

使用选定的方言输出汇编指令。也影响   哪种方言用于基本的“ asm”和扩展的“ asm”。支持的   选项(按方言顺序)是att或intel。默认值为att。   达尔文不支持英特尔。

这意味着您的第一个.intel_syntax指令是多余的,而最后一个.att_syntax是错误的,因为您的GCC调用将C编译为Intel汇编代码。

IOW,要么坚持使用-masm=intel,要么在.intel_syntax noprefix.att_syntax prefix伪指令之间插入内联Intel汇编代码部分-但不要同时使用。

请注意,sandwich方法与所有内联汇编程序约束均不兼容-例如涉及m的约束(即内存操作数)将以ATT语法插入操作数,这将产生类似“错误:表达式后为垃圾(%rbp)”的错误。在这种情况下,您必须使用-masm=intel

答案 1 :(得分:0)

在您的嵌入式asm中使用-masm=intel,并且不要使用任何.att_syntax指令。这适用于GCC和我认为ICC,以及使用任何约束条件。其他方法没有。

我认为Clang不支持Intel语法GNU C嵌入式asm 。 Clang使用Intel语法asm(-masm=intel或等效的-mllvm --x86-asm-syntax=intel)的选项仅控制它打印 asm的方式,而不控制它如何组装内联asm的输入。例如https://godbolt.org/z/8BCzp-显示c -masm=inteladd %0, 1解释为add dword ptr [1], eax

Clang确实在MSVC样式的asm块中支持Intel语法,但这很糟糕(没有约束,因此输入/输出必须通过内存。

如果使用clang硬编码寄存器名称,则可以使用-masm=intel。但是它在Intel语法模式下阻塞了mov %eax, 5,因此您不能让%0扩展为AT&T语法寄存器名称。


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

“ mov的操作数太多”是因为在AT&T语法中,mov eax, ebx是从存储器操作数(符号名称为mov)到存储器操作数(符号为eax)的ebx名称.intel_syntax noprefix


有人建议在您的asm模板周围使用.att_syntax prefix-masm=intel。有时可以奏效,但这是有问题的。并且与-masm=的首选方法不兼容。

“三明治”方法存在问题:

当编译器将操作数替换为您的asm模板时,它将根据%eax进行操作。 这对于存储操作数总是会破坏的(寻址模式语法完全不同)。

即使对于寄存器,它也会与clang分开。 Clang的内置汇编器在Intel语法模式下不接受.intel_syntax prefix作为寄存器名称,并且不接受{{ 1}}(与英特尔语法通常使用的noprefix相反)。

考虑此功能:

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

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

        movl    %edi, %eax
        .intel_syntax noprefix 
         add %eax, 1                    # AT&T register name in Intel syntax
        .att_syntax

三明治方法取决于GAS即使在Intel语法模式下也接受%eax作为寄存器名称。来自GNU Binutils的GAS可以,但是clang的内置汇编器没有。

在Mac上,即使使用真正的GCC,asm输出也必须与基于c而不是GNU Binutils的as组装在一起。

在该源代码上使用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
            ^

(错误消息的第一行不能很好地处理多行字符串文字。如果您使用;而不是\n\t并将所有内容都放在一行,则clang错误消息有效更好,但来源是一团糟。)


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


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

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


PS:可以使用GNU C inline asm dialect alternatives编写与-masm=intelatt一起使用的内联汇编。例如

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

compiles with gcc -O2(默认为-masm=att

atomic_inc(int*):
    lock addl $1, (%rdi) 
    ret

或使用-masm=intel进行以下操作:

atomic_inc(int*):
    lock add DWORD PTR [rdi], 1
    ret

请注意,AT&T需要l后缀,而intel需要dword ptr后缀,因为立即数并不意味着操作数大小。并且编译器在两种情况下都填写了有效的寻址模式语法。

这适用于clang,但仅使用过AT&T版本。