[译] Golang 汇编器快速指南

本文档概述了Go编译器使用的非通用形式的汇编语言,包括常量、符号、指令和运行时的协调。汇编器基于Plan 9的输入风格,但与之有所不同,例如常量解析遵循Go的运算符优先级。此外,文章还介绍了不同体系架构的特殊细节,如386、amd64、ARM等,并强调了汇编器如何与Go的垃圾回收和运行时交互。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文档是 Go编译器 GC 使用的非通用形式的汇编语言快速大纲。文档并不完整。

汇编器基于 Plan 9 汇编器的输入风格。详细文档在这里。如果你准备写一些汇编语言,那么虽然此文档是基于 Plan 9 的,你也应该通读。本文提供了语法摘要和与其解释内容的区别,并且描述了编写汇编与 Go 交互时所适用的特性。

最重要的是,Go 的汇编器并不是底层的直接表示。有一些是直接的映射,有一些不是。这是因为编译套件在常规流程中并不需要汇编器。相反,编译器针对一种半抽象的指令集操作,而且指令选择发生在一部分发生在代码生成之后。汇编器工作在半抽象状态下,所以当你看到类似指令 MOV,工具链实际生成的操作也许并不是移动,而是清除指令或者加载指令。或者也可能与实际指令完全对应。一般来说,特定机器的操作倾向于它们自身,而更通用的概念(如内存移动、子程序调用、返回等)则更加抽象。细节根据体系架构会有不同,我们非常抱歉关于这种不精确,情况还没有完全定义。

汇编器程序是一种解析半抽象指令集的描述并将其转换为输入给链接器指令的方法。如果想要看到给定体系的汇编指令(如:amd64),在标准库源码中有很多实例(比如 runtime math/big)。你可以测试编译器生成的汇编代码(实际输出可能和这里有出入):

$ cat x.go
package main

func main() {
    println(3)
}
$ GOOS=linux GOARCH=amd64 go tool compile -S x.go        # or: go build -gcflags -S x.go
"".main STEXT size=74 args=0x0 locals=0x10
    0x0000 00000 (x.go:3)   TEXT    "".main(SB), $16-0
    0x0000 00000 (x.go:3)   MOVQ    (TLS), CX
    0x0009 00009 (x.go:3)   CMPQ    SP, 16(CX)
    0x000d 00013 (x.go:3)   JLS 67
    0x000f 00015 (x.go:3)   SUBQ    $16, SP
    0x0013 00019 (x.go:3)   MOVQ    BP, 8(SP)
    0x0018 00024 (x.go:3)   LEAQ    8(SP), BP
    0x001d 00029 (x.go:3)   FUNCDATA    $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x001d 00029 (x.go:3)   FUNCDATA    $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x001d 00029 (x.go:3)   FUNCDATA    $2, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x001d 00029 (x.go:4)   PCDATA  $0, $0
    0x001d 00029 (x.go:4)   PCDATA  $1, $0
    0x001d 00029 (x.go:4)   CALL    runtime.printlock(SB)
    0x0022 00034 (x.go:4)   MOVQ    $3, (SP)
    0x002a 00042 (x.go:4)   CALL    runtime.printint(SB)
    0x002f 00047 (x.go:4)   CALL    runtime.printnl(SB)
    0x0034 00052 (x.go:4)   CALL    runtime.printunlock(SB)
    0x0039 00057 (x.go:5)   MOVQ    8(SP), BP
    0x003e 00062 (x.go:5)   ADDQ    $16, SP
    0x0042 00066 (x.go:5)   RET
    0x0043 00067 (x.go:5)   NOP
    0x0043 00067 (x.go:3)   PCDATA  $1, $-1
    0x0043 00067 (x.go:3)   PCDATA  $0, $-1
    0x0043 00067 (x.go:3)   CALL    runtime.morestack_noctxt(SB)
    0x0048 00072 (x.go:3)   JMP 0
...
复制代码

FUNCDATAPCDATA 包含了垃圾回收使用的信息,会在编译器中介绍。

想要查看链接后放入二进制文件中的内容,使用 go tool objdump 来查看:

$ go build -o x.exe x.go
$ go tool objdump -s main.main x.exe
TEXT main.main(SB) /tmp/x.go
  x.go:3        0x10501c0       65488b0c2530000000  MOVQ GS:0x30, CX
  x.go:3        0x10501c9       483b6110        CMPQ 0x10(CX), SP
  x.go:3        0x10501cd       7634            JBE 0x1050203
  x.go:3        0x10501cf       4883ec10        SUBQ $0x10, SP
  x.go:3        0x10501d3       48896c2408      MOVQ BP, 0x8(SP)
  x.go:3        0x10501d8       488d6c2408      LEAQ 0x8(SP), BP
  x.go:4        0x10501dd       e86e45fdff      CALL runtime.printlock(SB)
  x.go:4        0x10501e2       48c7042403000000    MOVQ $0x3, 0(SP)
  x.go:4        0x10501ea       e8e14cfdff      CALL runtime.printint(SB)
  x.go:4        0x10501ef       e8ec47fdff      CALL runtime.printnl(SB)
  x.go:4        0x10501f4       e8d745fdff      CALL runtime.printunlock(SB)
  x.go:5        0x10501f9       488b6c2408      MOVQ 0x8(SP), BP
  x.go:5        0x10501fe       4883c410        ADDQ $0x10, SP
  x.go:5        0x1050202       c3          RET
  x.go:3        0x1050203       e83882ffff      CALL runtime.morestack_noctxt(SB)
  x.go:3        0x1050208       ebb6            JMP main.main(SB)
复制代码

常量

尽管汇编器遵循 Plan 9 的规范,但这是一个独立程序,会有一些不同。其中一个就是常量评估。汇编器中的常量表达式是按照 Go 的操作符优先级来解析的,而不是类 C 的优先级。所以, 3&1<<2 是 4 而不是 0。它按照 (3&1)<<2 来解析,而不是 3&(1<<2)。而且,常量会被解析为 64位无符号整形。所以 -2 不是整形数减2,而是具有同样位信息的64位无符号整形。这个区别基本没什么影响,除了要注意避免在对右操作数高位置位的情况下进行歧义操作、除法、右移。

符号

某些符号,例如 R1 或者 LR,已经预定义,并且指向了寄存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值