我有以下代码,可以使用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'
答案 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=intel
将add %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=intel
或att
一起使用的内联汇编。例如
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版本。