Plan9 & Golang
· 阅读需 13 分钟
在上篇文章说到了学习Plan 9基础可以为我们揭开底层的一些细节,从而通过实践去探究原理。接下来就以Plan 9为基础,从不同角度去探索Golang语言吧。
如果你还没有阅读Plan 9相关的知识,推荐阅读Plan9 & Go Assembler
环境说明:Mac m1 (ARM架构)、Golang v1.23.2
简单回顾
这里简单回顾几个比较重要的知识点吧
-
Plan 9汇编伪寄存器
- SB(Static base pointer)用于访问全局符号,比如函数、全局变量
- FP(Frame pointer)用于访问函数的参数和返回值
- PC(Program counter)保存CPU下一条要运行的指令
- SP(Stack pointer)指向当前栈帧的栈顶
-
Plan 9源操作数与目标操作数方向,源操作数在前,目的操作数在后
movl $0x2, %eax
将立即数0x2移动到eax寄存器
再次窥探函数
函数序言
在函数调用的时候,会经常看到这样一段的函数序言,主要的作用就是保存「调用者的」BP
pushq %rbp
movq %rsp, %rbp
subq %16, %rsp
大致步骤如下
- 初始状态:函数尚未被调用
此时函数只包含返回地址(即调用函数的下一条指令的地址)
pushq %rbp
将调用者的栈帧指针(%rbp
)压入栈
保存上一个栈帧的基地址,用于函数返回时恢复调用者的栈帧
moveq %rsp, %rbp
将当前栈指针%rsp的值赋给%rbp, 建立当前函数的栈帧基地址 标记当前函数的栈帧起点
-
subq $16, %rsp
将栈指针%rsp向下移动16字节,为局部变量分配空间 完成局部变量栈空间的分配
下面就来编写代码,验证一下吧
go tool compile -S -N -l add_func.go
// base/prologue/add_func.go
package main
func add(a, b int) int {
return a + b
}
func main() {
_ = add(1, 2)
}
MOVD.W R30, -32(RSP)
保存调用者的链接寄存器(R30)到栈中。MOVD R29, -8(RSP)
保存当前帧指针(R29)到栈中。SUB $8, RSP, R29
更新栈帧指针R29。
0x0000 00000 TEXT main.add(SB), NOSPLIT|LEAF|ABIInternal, $32-16
0x0000 00000 MOVD.W R30, -32(RSP)
0x0004 00004 MOVD R29, -8(RSP)
0x0008 00008 SUB $8, RSP, R29