NASM与GAS的实际区别

60

我并不试图引发Intel vs AT&T之争(无论如何,现在他们都支持Intel语法),也不想询问哪一种“更好”,我只想知道选择其中一种的实际区别。

基本上,几年前我学习了一些基本的x86汇编语言,我使用NASM,只是因为我读的书也是用它的 - 这使我无意中成为NASM阵营的一员。自那以后,我很少使用汇编语言,所以没有机会尝试GAS。

请记住,它们都支持Intel语法(我个人也喜欢),理论上至少应该产生相同的二进制代码(我知道它们可能不会,但意义不应该改变),那么有什么理由支持其中一个呢?

是命令行选项吗?宏?非助记符关键字?或其他什么原因?

谢谢:)


1
@Fermat2357的“真实汇编项目”?你具体指的是什么?我认为GAS的目的是成为一个“真正”的可移植汇编器,在大多数架构上,这一点已经成功实现了。NASM是一个备受尊敬且非常成熟的汇编器,但只适用于英特尔架构。因此,它们之间唯一实际的区别在于架构和开发支持,而GAS则处于首位。 - Memos Electron
2
最初的“as”汇编器诞生于Unix早期。它比Gnu早至少十年。GAS只是as的GNU版本,就像GCC是cc的GNU版本一样。我希望我能够记录评论。但事实是,GAS本身就是一个优秀的编译器... - paulsm4
4
如果你对汇编语言感兴趣并且在使用Linux系统,我强烈推荐你学习GAS。这里有两本推荐的书籍:《Programming from the Ground Up》(http://savannah.nongnu.org/projects/pgubook/)和《Professional Assembly Language》(http://www.amazon.com/Professional-Assembly-Language-Programmer/dp/0764579010)。它们都使用GAS :) - paulsm4
我不会强加个人偏好。我认为了解两者可能会更有收获。快速搜索给了我几页网址,包括[这个](http://www.imada.sdu.dk/Courses/DM18/Litteratur/IntelnATT.htm)。 - motoku
AT&T语法的一个缺点是x87 FP操作数顺序设计缺陷。我认为历史是第一个实现弄错了,从那时起一切都是兼容缺陷的。有一段时间,objdump的Intel语法反汇编保留了兼容缺陷的操作数翻转,因此它实际上并没有生成正确的FP代码的Intel语法反汇编。除此之外,我通常更喜欢AT&T语法。Intel语法对于某些向量洗牌操作更友好。此外,在AVX中,目标是第一个操作数,而不是有时在长行末尾丢失的最后一个操作数。 - Peter Cordes
@paulsm4 加一分,因为提到了《从零开始的编程》。这本书值得所有层次的人阅读。 - smwikipedia
5个回答

39

NASM使用自己变化的Intel语法,与Intel官方文档中使用的MASM语法不同。操作码和操作数顺序与Intel相同,因此乍一看指令看起来相同,但任何重要的程序都会有所不同。例如,对于MASM,由MOV ax,foo使用的指令取决于foo的类型,而NASM没有类型,并且始终组装为移动立即数的指令。当无法隐含确定操作数大小时,MASM需要使用类似于DWORD PTR的东西,而NASM使用DWORD表示相同的东西。除了指令助记符和基本操作数格式和排序之外,大多数语法都不同。

在功能方面,NASM和GAS几乎相同。两者都具有汇编程序宏工具,尽管NASM的更加全面和成熟。许多GAS源代码文件使用C预处理器而不是GAS自己的宏支持。

这两个汇编器之间最大的区别是它们对16位代码的支持。GAS没有支持定义x86段的任何支持。使用GAS,您只能创建简单的单段16位二进制映像,基本上只是引导扇区和.COM文件。NASM完全支持段,并支持可以与适当的链接器一起使用以创建分段16位可执行文件的OMF格式对象文件。

除了OMF对象文件格式外,NASM还支持许多GAS不支持的格式。GAS通常仅支持其运行所在机器的本机格式,基本上是ELF、PE-COFF或MACH-O。如果要支持不同的格式,则需要为该格式构建“交叉编译”版本的GAS。

另一个显着的区别是,GAS支持创建DWARF和Windows 64位取消信息(后者是Windows x64 ABI所需的),而对于NASM,您必须自己创建部分并填充数据。


34

Intel Syntax: mov eax, 1 (指令的目标,源)

AT&T Syntax: movl $1, %eax (指令的源,目标)

Intel语法相当直观。在上面的例子中,移动的数据量是从寄存器的大小推断出来的(在eax的情况下为32位)。所使用的寻址模式是从操作数本身推断出来的。

在使用AT&T语法时会有一些怪癖。首先,请注意mov指令末尾的l后缀,它代表long,表示32位的数据。其他指令后缀包括w表示一个字(16位 - 不要与CPU的字长混淆!),q表示四个字(64位),b表示一个单字节。虽然不总是必需的,但通常您会看到使用AT&T语法的汇编代码明确说明指令所操作的数据量。

在涉及源和目标操作数使用的寻址模式时需要更多的明确性。符号$表示立即寻址,即使用指令本身中的值。在上面的例子中,如果没有这个$,将会使用直接寻址,即CPU会尝试获取内存地址1处的值(这很可能会导致分段错误)。符号%表示寄存器寻址,如果在上面的例子中没有包含它,则eax将被视为一个符号,即一个带标签的内存地址,这很可能会在链接时导致未定义引用。因此,您必须明确地说明源和目标操作数所使用的寻址模式,这是强制性的

指定内存操作数的方法也不同:

Intel: [基础寄存器 + 索引 * 索引的大小 + 偏移量]

AT&T: offset(基础寄存器, 索引, 索引的大小)

Intel语法更清楚地说明了计算寻找内存地址的过程。使用AT&T语法时,结果相同,但您需要知道正在进行的计算。

理论上应该产生相同的二进制文件

这完全取决于您的工具链。

有什么理由支持其中之一吗?

当然,这是个人偏好。在我看来,关键在于你在处理内存时感觉哪种语法更舒适。你喜欢AT&T语法的强制明确性吗?还是你喜欢汇编器为你处理这些低级细节?

是命令行选项吗?宏?非助记符关键字?

这与汇编器(GAS,NASM)本身有关。同样,这也是个人偏好。


1
GAS的好处在于它不会尝试做各种花哨的映射。它强制你声明所需的确切内存和处理能力,从而避免了错误。NASM也不错,但如果你想要效率,Tiny C和fasm是更好的选择。

1
GAS强制你声明关于空间或速度的任何东西,而NASM却不是这样吗? - Peter Cordes
1
在最后一句话中,我认为你谈论的是构建时间,而不是生成代码的性能。所有汇编器在生成机器代码的效率上大致相等(尽管NASM在长NOP方面稍微差一些,特别是如果您不使用%use smartalign,并且有时需要nosplit避免某些CPU的lea pessimization)。但是TCC不是优化编译器,因此生成的代码比gcc或clang-O3要低得多。 - Peter Cordes

1

为什么不看看这个帖子呢?

NASM和GAS之间最大的区别之一是语法。GAS使用AT&T语法,这是一种相对古老的语法,只适用于GAS和一些旧汇编器,而NASM使用Intel语法,被大多数汇编器支持,如TASM和MASM。(现代版本的GAS确实支持一个名为.intel_syntax的指令,允许在GAS中使用Intel语法。)

它涵盖了:

  • NASM和GAS之间的基本语法差异
  • 常见的汇编级别结构,如变量、循环、标签和宏
  • 有关调用外部C例程和使用函数的一些内容
  • 汇编助记符的差异和用法
  • 内存寻址方法

一个好的做法是在两种方言中都写hello_world并获得具体的感觉。


0

我认为关键点在于NASM仅适用于英特尔架构,而GAS适用于许多架构,例如ARM等。


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