NASM 与 GAS(实际差异)
Posted
技术标签:
【中文标题】NASM 与 GAS(实际差异)【英文标题】:NASM Vs GAS (Practical differences) 【发布时间】:2012-11-27 10:23:34 【问题描述】:我并不是要引发一场英特尔与 AT&T 的战争(无论如何都是有争议的,因为它们都支持英特尔语法)或者问哪一个本身“更好”,我只是想知道选择一个的实际差异或其他。
基本上,几年前我在学习一些基本的 x86 程序集时,除了我正在阅读的那本书之外,我无缘无故地使用了 NASM——这让我坚定但不由自主地加入了 NASM 阵营。从那以后,我很少有使用汇编的理由,所以我没有机会尝试 GAS。
请记住,它们都支持 Intel 语法(我个人更喜欢),并且至少在理论上应该生成相同的二进制文件(我知道它们可能不会,但含义不应该改变),什么是偏爱其中一个的理由?
是命令行选项吗?宏?非助记词?还是别的什么?
谢谢:)
【问题讨论】:
@Fermat2357 '真正的汇编程序' ?你到底是什么意思?我认为 GAS 的目的是成为一个“真正的”便携式汇编器,并且在大多数架构上,这已成功实现。 NASM 是一个受人尊敬且非常成熟的汇编程序,但仅适用于英特尔架构。那么它们之间唯一的实际区别是架构和 GAS 的开发支持放在首位 最初的“as”汇编程序从 Unix 早期就已经存在。它比 Gnu 早了至少十年。 GAS 只是 Gnu 版本,就像 GCC 是 cc 的 GNU 版本一样。我希望我可以降价 cmets。但事实上,GAS 本身就是一个优秀的编译器…… @Elliott - 如果您对汇编程序感兴趣,并且正在使用 Linux,我强烈建议您学习 GAS。强烈推荐的两本书:Programming from the Ground Up 和 Professional Assembly Language。两者都使用 GAS :) 我不会推动偏好。我认为两者都知道是值得的。快速搜索给了我几页,包括this。 AT&T 语法的一个缺点是 x87 FP ops 设计错误的操作数顺序。我认为历史是第一个实现出错了,此后的一切都是错误兼容的。有一段时间,objdump
的英特尔语法反汇编保留了错误兼容操作数反转,因此它实际上并没有产生正确的英特尔语法反汇编 FP 代码。除此之外,我通常更喜欢 AT&T 语法。 Intel 语法对于一些向量 shuffle 操作更好。同样对于 AVX,dest 是第一个操作数,而不是有时会丢失在长行的末尾。
【参考方案1】:
英特尔语法:mov eax, 1(指令目标,源)
AT&T 语法:movl $1, %eax(指令源,目标)
Intel 语法很容易解释。在上面的示例中,移动的数据量是根据寄存器的大小(在 eax 的情况下为 32 位)推断出来的。使用的寻址模式是从操作数本身推断出来的。
AT&T 语法有一些怪癖。首先,注意mov
指令末尾的l
后缀,它代表long
,表示32位数据。其他指令后缀包括
w
一个单词(16 位 - 不 与您的单词大小混淆
CPU!),q
用于四字(64 位),b
用于单字节。虽然并不总是需要,但通常您会看到使用 AT&T 语法的汇编代码明确说明指令正在操作的数据量。
当涉及到源操作数和目标操作数上使用的寻址模式时,需要更加明确。 $
表示immediate
寻址,就像使用指令本身中的值一样。在上面的示例中,如果在没有 $
的情况下写入,则将使用 direct
寻址,即 CPU 将尝试获取内存地址 1 处的值(这很可能会导致分段错误)。 %
表示register
寻址,如果您没有在上面的示例中包含它,eax
将被视为symbol
,即标记的内存地址,这很可能导致undefined reference
在链接时间。因此,强制您必须明确了解源操作数和目标操作数上使用的寻址模式。
内存操作数的指定方式也不同:
英特尔:[基址寄存器 + 索引 * 索引大小 + 偏移量]
AT&T:偏移量(基址寄存器、索引、索引大小)
英特尔语法使查找内存地址的计算更加清晰。使用 AT&T 语法,结果是相同的,但您应该知道正在发生的计算。
至少理论上应该产生相同的二进制文件
这完全取决于您的工具链。
偏爱其中一个的原因是什么?
当然,个人偏好,在我看来,这归结为您在处理内存时感觉更舒服的语法。你更喜欢 AT&T 语法的强制明确性吗?还是您更喜欢您的汇编程序为您找出这些低级细节?
是命令行选项吗?宏?非助记词?
这与汇编程序(GAS、NASM)本身有关。再说一次,个人喜好。
【讨论】:
【参考方案2】: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 完全支持分段并支持 OMF 格式的目标文件,您可以使用合适的链接器来创建分段的 16 位可执行文件。
除了 OMF 对象文件格式之外,NASM 还支持 GAS 不支持的许多格式。 GAS 通常只支持其运行所在机器的本机格式,基本上是 ELF、PE-COFF 或 MACH-O。如果您想支持不同的格式,您需要为该格式构建 GAS 的“交叉编译”版本。
另一个显着的区别是 GAS 支持创建 DWARF 和 Windows 64 位展开信息(Windows x64 ABI 要求后者),而使用 NASM 您必须自己创建部分并填写数据。
【讨论】:
【参考方案3】:为什么不检查这个post?
NASM 和 GAS 之间最大的区别之一是语法。 GAS 使用 AT&T 语法,这是一种相对古老的语法,专用于 GAS 和一些较旧的汇编程序,而 NASM 使用 Intel 语法,得到大多数汇编程序(如 TASM 和 MASM)的支持。 (现代版本的 GAS 确实支持名为 .intel_syntax 的指令,它允许在 GAS 中使用 Intel 语法。)
它涵盖:
NASM 和 GAS 之间的基本句法差异 常见的汇编级结构,例如变量、循环、标签和宏 关于调用外部 C 例程和使用函数的一点点 汇编助记符的区别及用法 内存寻址方法一个好的做法是用两种方言写hello_world,并有具体的感觉。
GAS tutorial NASM tutorial【讨论】:
以上是关于NASM 与 GAS(实际差异)的主要内容,如果未能解决你的问题,请参考以下文章
如何将此代码从 Intel(nasm) 转换为 AT&T(gas) 语法?