如何正确学习GNU汇编?

How should I correctly learn GNU assembly?

最近在ubuntu学习汇编,发现了一些assemble,比如NASM、MASM、GAS。它们的语法不同(特别是伪指令),NASM 和 MASM 支持 Intel 语法,GAS 支持 AT&T 语法。

现在,我想学习 AT&T 汇编,所以,我使用 GAS assemble,我找到了一系列关于 GAS 的手册,但对我来说读起来很痛苦,比如 缺少具体例子或者简单介绍基本语法.

我找到了有代表性的部分“指南”:

像这个例子https://sourceware.org/binutils/docs/as.html#Scl,我想知道我们有什么storage-class valuegccs-assembly-output-of-an-empty-program-on-x86-win32 告诉我 .scl 2 表示外部存储 class,但我找不到关于此值的任何其他信息。等等。

可能是我学习方向不对,或者是我找资料的方式不对。所以我想问你有一些关于 AT&T 的指南或手册,有例子和清晰的解释给初学者。

我对伪指令感到困惑,标准汇编程序结构是什么样的?

学习汇编时,您可以主要关注指令,以及.section.globl指令.除非你想了解 GAS 指令如何生成元数据,包括调试信息和其他对调试有用的东西 high-level 语言比 hand-written asm.

很多 debug-info 指令,显然 .scl 只记录了它们的语法,没有关于值的含义或其他可能关心你放在那里的值的真正细节。

您可以编写工作 hand-written asm 来玩弄几乎只使用 .section.globl。 (对于静态数据,.byte/.short/.long/.quad.ascii/.asciz用于初始化,.space在BSS,如果需要的话 .p2align

这就是为什么 Matt Godbolt 的“编译器浏览器”站点默认过滤掉指令,除了数据初始值设定项,因为当然数据在 .data.rdata 中,而代码在 [=25= 中],关于编译器 asm 输出的有趣部分是实际指令(和静态数据)。参见


完全符合 Windows 对堆栈展开的 SEH 元数据的期望(尤其是在 64 位代码中)可能需要一些额外的指令,对于 x86-64 SysV 也是如此 .cfi stack-unwind 元数据。但是,如果您需要在稳健的 production-quality 上下文中使用 hand-written asm,而不是仅仅作为了解指令工作原理的 one-off 实验。


https://whosebug.com/tags/x86/info has some links to tutorials (and manuals), and Programming from the Ground Up is a good free book for 32-bit x86 with AT&T syntax. (And GAS directives). It's aimed at running on Linux, so it can teach some OS / computing concepts along the way, the kind of background knowledge necessary for assembly (and system calls) to make sense. Online HTML version

要在现代 64 位 GNU/Linux 发行版上遵循它,您可能需要 as --32ld -m elf_i386 将默认值覆盖为 32 位。并且 gcc -m32 -fno-pie -no-pie 书中说 gcc.

的任何地方

如果您正在比较有关 C 编译方式的书籍示例,您可能还希望 -fno-stack-protector 进一步简化 C 的 asm 输出。但请注意,不同的 GCC 版本将以不同的方式编译,尤其是默认调整选项多年来发生了变化。 -mtune=pentium-mtune=pentium3 也可能让 GCC 选择 code-gen 更像是一本旧书的策略。当然,当前GCC的选择也是正确的,更适合较新的CPU,只是与旧的GCC不同!

此外,Linux 上使用的 i386 SysV ABI 已更改为需要在 call 指令之前进行 16 字节堆栈对齐,这是由于 GCC 不小心生成了 32 位代码,例如使用依赖于 GCC 选择执行的性能优化的 movaps。调用 libc 函数通常仍然碰巧在只有 4 字节堆栈对齐的 32 位代码中工作,但是如果您看到现代 GCC 保留的 space 比它需要的多,这通常就是原因。


吹毛求疵:

I found some of assemblers, like NASM, MASM, and GAS. Their syntax is different(particularly pseudo-directives), NASM and MASM support Intel syntax, GAS support AT&T Syntax.

指令与指令语法正交,英特尔语法有不同的风格,尤其是在 MASM 与 NASM 之间。 (以及指令的 MASM 和 NASM 之间的主要区别)。

此外,GAS 还支持 .intel_syntax noprefix 使用某种 MASM-like 指令语法,但仍然是 GAS 指令。

(同样,YASM 有一个 AT&T 模式使用 AT&T 指令语法,但仍然 NASM/YASM 预处理器和指令。)