Go 汇编小记
-
程序代码是通过 CPU 执行相关汇编指令
运行的。计算机内部布局划分为三个部分,内存 、寄存器
、指令 。本篇内容主要是通过这三个部分介绍 Go 汇编,作为学习总结
-
Go 内存组成:heap、stack、frame、local、data(read、roread)、text
- frame: 一个函数对应一个 frame
- stack: 一个 stack 中至少有一个 frame,管理每个函数(frame)被调用时相关的数据
- local: 局部变量,一般在函数内定义,只有在函数被执行才会在 stack 上被创建,
当函数调用完后被回收(进阶:闭包中对局部变量捕获问题)
- data: 数据段,一般用于存放全局数据,roread 只读数据数据段
- text: 代码段,用于存储要执行的指令数据,一般是只读
-
Go 几个常见寄存器:SB、FP、SP、PC
-
SB: Staic Base Pointer,静态内存 的开始地址
-
FP: Frame Pointer,对应函数的帧指针,一般用来访问函数的参数和返回值
-
SP: Stack Pointer,分伪SP 与真SP
- 伪 SP:位于当前栈帧(frame)底部,用于
定位局部变量 ,形如 tmp-2(SP),即相对与真 SP 地址偏移量
- 真 SP:位于当前栈帧(frame)顶部,用于
定位调用其他函数的 参数 和 返回值
-
常见指令:CALL、RET、LEA、PUSH、POP
类别 |
指令名称 |
解释 |
控制流 |
CMP |
比较指令 |
|
JMP / JMP-if-x |
跳转指令 |
|
CALL |
调用函数 |
|
RET |
函数返回 |
|
|
|
其他 |
LEA |
取地址,将内存地址加载到寄存器 |
|
PUSH |
压栈 |
|
POP |
出栈 |
- Go 汇编常见指令
-
注意:
- 在汇编中,没有函数类型,GO 自动回收会报错,所以在引用汇编定义的内容前,需要在 go 文件前指定
变量或函数的类型
- Go 语言函数的调用参数和返回值均是通过栈传输
-
变量定义
// symbol: 对应汇编的变量名
// width,该符号对应内存大小,内存宽度必须是 2 的指数倍,单位是字节(byte)
GLOBL symbol(SB), width
// symbol: 对应汇编的变量名,offset(SB),符号开始地址的偏移量
// width:要初始化的内存宽度大小,初始化始必须是 1,2,4,8几个宽度之一,即对应到机器的整数倍,单位是字节(byte)
// value: 变量初始化值,可以是具体值,也可以是一个已经声明变量(symbol)
DATA symbol+offset(SB) /width,value
//TEXT 用于定义函数符号
// symbol: 函数名,是包含包路径的,可省略包路径
/** flags:用于指示函数的一些特殊行为,
NOSPLIT 函数不进行栈分裂,一般用于没有其他调用的叶子函数
WRAPPER包装函数 ,在panic 或 runtime.caller等某些处理函数帧的地方不会增加函数帧计数
NEEDCTXT 表示需要一个上下文参数,一般用于闭包函数
*/
//$framesize 表示函数的局部变量需要多大的栈空间,其中包含调用其他函数时准备调用参数的隐式栈空间
TEXT symbol(SB), [flags] $framesize[-argsize]
- 函数帧
func main() {
printsum(a,b)
}
func printsum(a,b int){
var ret = sum(a,b)
fmt.Println(ret)
}
func sum(a,b int)int{
return a + b
}
|
请发表评论