Go 语言汇编快速入门

2020-07-09 00:00:00 代码 参数 调用 寄存器 汇编

在 Go 的源码中包含大量汇编语句,的示例代码位于 math/big, runtimecrypto 这些库中,但是从这里入门的话实在太过于痛苦,这些示例都是着力于系统操作和性能的运行代码。

对于没有经验的 Go 语言爱好者来说,这样会使通过库代码的学习过程遇到很大困难 。这也是撰写本文的原因所在。

Go ASM ( 译者注:ASM 是汇编的简写 ) 是一种被 Go 编译器使用的特殊形式的汇编语言,而且它基于 Plan 9 (译者注:来自贝尔实验室的概念网络操作系统 )输入风格,所以先从 文档 开始是一个不错的选择。

注意:本文的内容是基于 x86_64 架构,但大多数示例也能兼容 x86 架构。

一些例子是从原始文档中选取出来的,主要目的是建立一个综合的统一标准摘要,涵盖那些重要/有用的主题。

Go ASM 和标准的汇编语法( NASM 或 YASM )不太一样,首先你会发现它是架构独立的,没有所谓的 32 或 64 位寄存器,如下图所示:

NASM x86NASM x64Go ASMeaxraxAXebxrbxBXecxrcxCX………

大部分寄存器符号都依赖于架构。

另外, Go ASM 还有四个预定义的符号作为伪寄存器。它们不是真正意义上的寄存器,而是被工具链维持出来的虚拟寄存器,这些符号在所有架构上都完全一样:

  • FP: 帧指针 –参数和局部变量–
  • PC: 程序计数器 –跳转和分支–
  • SB: 静态基址指针 –全局符号–
  • SP: 栈指针 –栈的顶端–.

这些虚拟寄存器在 Go ASM 中占有了重要地位,并且被广泛使用,其中重要的就要属 SB 和 FP了。

伪寄存器 SB 可以看作是内存的起始地址,所以 foo(SB) 就是 foo 在内存中的地址。语法中有两种修饰符,<> 和 +N (N是一个整数)。种情况 foo<>(SB) 代表了一个私有元素,只有在同一个源文件中才可以访问,类似于 Go 里面的小写命名。第二种属于对相对地址加上一个偏移量后得到的地址,所以 foo+8(SB) 就指向 foo 之后 8 个字节处的地址。

伪寄存器 FP 是一个虚拟帧指针,被用来引用过程参数,这些引用由编译器负责维护,它们将指向从伪寄存器处偏移的栈中参数。在一台 64 位机器上, 0(FP) 是个参数, 8(FP) 就是第二个参数。为了引用这些参数,编译器会强制它们的命名使用,这是出于清晰和可读性的考虑。所以 MOVL foo+0(FP), CX 会把虚拟的 FP 寄存器中的个参数放入到物理上的 CX 寄存器,以及 MOVL bar+8(FP), DX 会把第二个参数放入到 DX 寄存器中。

读者可能已经注意到这种 ASM 语法类似 AT&T 风格,但不完全一致:

IntelAT&TGomov eax, 1movl $1, %eaxMOVQ $1, AXmov rbx, 0ffhmovl $0xff, %rbxMOVQ $(0xff), BXmov ecx, [ebx+3]movl 3(%ebx), %ecxMOVQ 2(BX), CX

另一处显著的差异就是全局源码文件结构, NASM 中的代码结构是用 section 清晰的定义出来:

global start

section .bss
    …

section .data
    …

section .text
start:
    mov     rax, 0x2000001
    mov     rdi, 0x00
    syscall

相关文章