我可以使用Intel x86汇编语法与GCC一起使用吗?

84
我想编写一个小的低级程序,其中某些部分需要使用汇编语言,但其余代码将用C/C++编写。
所以,如果我要使用GCC将C/C++与汇编代码混合使用,我需要使用AT&T语法还是可以使用Intel语法?或者说有其他方式可以混合C/C++和asm(Intel语法)?
我意识到可能没有选择而必须使用AT&T语法,但我想确认一下。
如果最终没有选择,我在哪里可以找到有关AT&T语法的完整/官方文档?
谢谢!

如果您在汇编语言中编写了一些完整的函数,它们可以放在一个单独的编译文件中。如果您不介意在YASM或NASM上构建依赖项,那么您可以使用任何您喜欢的语法。 (但是,您的汇编代码必须处理Windows和Linux的不同ABI,可能需要使用汇编器宏。)GNU汇编器手册在线,通常也与gcc / binutils一起安装。(info as)。 - Peter Cordes
1
要小心使用Intel语法,因为Clang的集成汇编器会出现问题。另请参见LLVM Issue 24232:内联汇编操作数与.intel_syntax不兼容。该错误报告显示Clang在简单的否定操作上存在问题。 - jww
2个回答

98
如果您正在使用单独的汇编文件,gas具有一个指令来支持Intel语法:
.intel_syntax noprefix      # not recommended for inline asm

这里使用Intel语法,不需要在寄存器名称前加上%前缀。

(你也可以通过运行as命令并使用-msyntax=intel -mnaked-reg来将其作为默认选项,而不是使用att。这样做的话,你就不必在文件顶部加上.intel_syntax noprefix了.)


内联汇编:使用-masm=intel进行编译

对于内联汇编,你可以使用gcc -masm=intel编译你的C/C++源代码(有关详细信息,请参见如何永久设置gcc使用intel语法?)。编译器自身的汇编输出(内联汇编插入其中)将使用Intel语法,并且它将使用类似于[rdi + 8]而不是8(%rdi)的Intel语法替换asm模板字符串中的操作数。

这适用于GCC本身和ICC,但对于clang只有clang 14及更高版本
(尚未发布,但补丁已经在当前主干中。)


在内联汇编的开头使用.intel_syntax noprefix,并使用.att_syntax切换回来可能会起作用,但如果你使用任何m约束条件,则会出现问题。内存引用仍将以AT&T语法生成。对于寄存器,它可以工作,因为GAS即使在intel-noprefix模式下也接受%eax作为寄存器名称。

asm()语句的结尾处使用.att_syntax也会破坏使用-masm=intel进行编译;在这种情况下,GCC自己的asm模板之后(和之前)将采用Intel语法。(与GCC不同,Clang的每个asm模板字符串都是本地的,而不像GCC那样真正成为GCC发送到as分别组装的文本文件的一部分。)

相关内容:


10
-masm=intel 可以让编译器生成 Intel 语法。对于内嵌汇编,您确实需要它,否则 "m" 内存约束将无法正常工作。 - ninjalj
2
需要使用.att_syntax noprefix来撤销.intel_syntax noprefix。我相信在AT&T语法中也可以使用“m”。据我所知,这只是一种方便的问题。 - ugoren
5
如果你在没有使用-masm=intel的情况下使用.intel_syntax,那么 "m" 将以 AT&T 语法给出内存引用。如果你使用了-masm=intel,那么你不需要(也不应该)回到 AT&T 语法,因为编译器输出的是 Intel 语法。 - ninjalj
3
我认为混合它们听起来不是个好主意。要么接受AT&T语法(虽然有些奇怪,但你可以学会适应),要么全部使用Intel语法。 - Per Lundberg
1
@ugoren请考虑删除您之前的评论。在内联asm语句中使用intel_syntax noprefix.att_syntax通常是一个坏主意。只在独立的.S文件中使用它们。在命令行上使用-masm=intel更好,并且将使GCC正确填充asm模板(例如,使用RDI而不是%RDI,或者更重要的是使用[rdi+8]而不是8(%rdi)用于“m”操作数)。如果您希望代码无论命令行选项如何都能正常工作,请使用方言替代品:“mov {%1,%0 | %0,%1}”。 - Peter Cordes
显示剩余3条评论

9
你可以使用-masm=intel选项来使用内联汇编,就像ninjalj所写的那样。但是,如果你在内联汇编中包含C/C++头文件,可能会导致错误。以下是在Cygwin上重现这些错误的代码。
sample.cpp:
#include <cstdint>
#include <iostream>
#include <boost/thread/future.hpp>

int main(int argc, char* argv[]) {
    using Value = uint32_t;
    Value value = 0;
    asm volatile (
        "mov  %0, 1\n\t"   // Intel syntax
//      "movl $1, %0\n\t"  // AT&T  syntax
        :"=r"(value)::);

    auto expr = [](void) -> Value { return 20; };
    boost::unique_future<Value> func { boost::async(boost::launch::async, expr) };
    std::cout << (value + func.get());
    return 0;
}

当我构建这段代码时,出现了以下错误信息。
g++ -E -std=c++11 -Wall -o sample.s sample.cpp
g++ -std=c++11 -Wall -masm=intel -o sample sample.cpp -lboost_system -lboost_thread
/tmp/ccuw1Qz5.s: Assembler messages:
/tmp/ccuw1Qz5.s:1022: Error: operand size mismatch for `xadd'
/tmp/ccuw1Qz5.s:1049: Error: no such instruction: `incl DWORD PTR [rax]'
/tmp/ccuw1Qz5.s:1075: Error: no such instruction: `movl DWORD PTR [rcx],%eax'
/tmp/ccuw1Qz5.s:1079: Error: no such instruction: `movl %eax,edx'
/tmp/ccuw1Qz5.s:1080: Error: no such instruction: `incl edx'
/tmp/ccuw1Qz5.s:1082: Error: no such instruction: `cmpxchgl edx,DWORD PTR [rcx]'

为了避免这些错误,需要将内联汇编(代码的上半部分)与需要boost::future等C/C++代码(代码的下半部分)分开。使用-masm = intel选项来编译包含英特尔语法内联汇编的.cpp文件,而不是其他.cpp文件。
sample.hpp:
#include <cstdint>
using Value = uint32_t;
extern Value GetValue(void);

sample1.cpp: compile with -masm=intel
#include <iostream>
#include "sample.hpp"
int main(int argc, char* argv[]) {
    Value value = 0;
    asm volatile (
        "mov  %0, 1\n\t"   // Intel syntax
        :"=r"(value)::);
    std::cout << (value + GetValue());
    return 0;
}

sample2.cpp: compile without -masm=intel
#include <boost/thread/future.hpp>
#include "sample.hpp"
Value GetValue(void) {
    auto expr = [](void) -> Value { return 20; };
    boost::unique_future<Value> func { boost::async(boost::launch::async, expr) };
    return func.get();
}

优质的头文件将使用方言替代,以便可以编译为-masm=intel或不带此选项。 - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接