如何设置gcc或clang以使内联asm()语句永久使用Intel语法?

blpfk2vs  于 2022-11-13  发布在  其他
关注(0)|答案(2)|浏览(211)

我有下面的代码,它可以很好地编译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'
xa9qqrwz

xa9qqrwz1#

**在内联asm中使用-masm=intel并且 * 不要 * 使用任何.att_syntax指令。**这适用于GCC,我认为是ICC,以及您使用的任何约束。其他方法不适用。(请参见Can I use Intel syntax of x86 assembly with GCC?以获得一个简单的答案:此答案将探究到底是什么地方出了问题,包括Clang 13及更早版本。)

这也适用于clang 14和以后的版本。(虽然还没有发布,但是这个补丁是当前 Backbone.js 的一部分;参见https://reviews.llvm.org/D113707)。
Clang 13和更早的版本在替换操作数和汇编为op src, dst时总是使用AT&T语法进行内联asm。但更糟糕的是,clang -masm=intel甚至在使用方言替代项(如asm ("add {att | intel}“)获取asm模板的Intel端时也会这样做:......)'!
clang -masm=intel在其内置汇编程序将asm()语句转换为指令的某种内部表示后,仍然控制它如何 * 打印 * asm。例如,Godbolt显示clang 13 -masm=inteladd %0, 1转换为add dword ptr [1], eax,但clang trunk产生add eax, 1

此答案中有关叮当声的部分内容尚未针对此新的叮当声修补程序进行更新。

Clang确实支持MSVC风格的asm块中的Intel语法,但这很糟糕(没有约束,所以输入/输出必须通过内存)。
如果你用clang硬编码寄存器名,-masm=intel是可用的(或者等价的-mllvm --x86-asm-syntax=intel),但是在Intel语法模式下它会阻塞mov %eax, 5,所以你不能让%0扩展为AT& T语法寄存器名。
-masm=intel使编译器在其asm输出文件的顶部使用.intel_syntax noprefix,并在inline-asm语句之外从C生成asm时使用英特尔语法。在asm模板的底部使用.att_syntax会破坏编译器的asm,因此,类似PTR [rbp-4]的错误消息在汇编程序(预期使用AT&T语法)看来像垃圾一样。
“mov操作数过多”是因为在AT&T语法中,mov eax, ebx是从内存操作数(符号名为eax)到内存操作数(符号名为ebx)的mov
有些人建议在asm模板周围使用.intel_syntax noprefix.att_syntax prefix。这有时可以工作,但是有问题。并且与首选的-masm=intel方法不兼容。

“三明治”方法的问题:

当编译器将操作数替换到asm模板中时,它将根据-masm=执行此操作。这将始终中断内存操作数(寻址模式语法完全不同)。

**即使是寄存器,它也会以clang中断。**Clang的内置汇编程序在英特尔语法模式下不接受%eax作为寄存器名称,也不接受.intel_syntax prefix(与通常用于英特尔语法的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接受%eax作为寄存器名,即使在Intel语法模式下也是如此。来自GNU Binutils的GAS接受%eax,但clang的内置汇编程序不接受。
在Mac上,即使使用真实的的GCC,asm输出也必须使用基于clang的as进行汇编,而不是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
            ^

(The错误消息的第一行没有很好地处理多行字符串文字。如果您使用;而不是\n\t,并将所有内容放在一行上,则叮当声错误消息的效果会更好,但源代码会很混乱。)
我没有检查当编译器选择立即数时"ri"约束会发生什么;如果GAS在Intel语法模式下也忽略了它,它仍将使用$修饰它,但使用IDK。
PS:你的asm语句有一个bug:您在输出操作数上忘记了一个早期乱码,所以没有什么可以阻止编译器为%0输出和%2输入选择相同的寄存器,您直到第二条指令才读取这些寄存器。然后mov将销毁一个输入。
但是使用mov作为asm模板的第一条或最后一条指令通常也是一个遗漏的优化bug。在这种情况下,你可以并且应该只使用lea %0, [%1 + %2]让编译器将结果写入第三个寄存器,而不是破坏性的。或者只 Package add指令(使用一个"+r"操作数和一个"r",让编译器来考虑数据移动。)如果无论如何都要从内存中加载值,它可以将其放入正确的寄存器中,这样就不需要mov
附言:使用GNU C内联asm方言替代,可以编写与-masm=intelatt一起工作的内联asm。例如。

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

使用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后缀,因为memory,immediate并不意味着操作数大小,而且编译器在这两种情况下都填充了有效的寻址模式语法。
这适用于叮当声,但只有AT&T版本得到使用。

dgenwo3n

dgenwo3n2#

请注意,-masm=也会影响预设的内嵌组译器语法:
使用所选方言输出汇编指令。还影响基本“asm”和扩展“asm”使用的方言。支持的选项(按方言顺序)为att或intel。默认值为att。达尔文不支持intel。
这意味着您的第一个.intel_syntax指令是多余的,最后的.att_syntax是错误的,因为您的GCC调用将C编译为英特尔汇编程序代码。
IOW,要么坚持使用-masm=intel,要么将内联英特尔汇编程序代码部分夹在.intel_syntax noprefix.att_syntax prefix指令之间--但不要同时使用这两个指令。
请注意,三明治方法并不与所有内联汇编程序约束兼容-例如,涉及m(即内存操作数)的约束将在ATT语法中插入操作数,这将产生类似于“Error:expression '后面的junk(%rbp)。在这些情况下,您必须使用-masm=intel

相关问题