Go语言学习笔记(二十九)汇编函数

0 阅读1分钟

1.基本用法:

函数标识符通过TEXT汇编指令定义,表示从该行开始的指令定义在TEXT内存段.TEXT语句后的指令一般对应函数的实现.但是对TEXT指令本身来说并不关心后面是否有指令.因此TEXT和LABEL定义的符号是类似的,区别只是LABEL是用于跳转标号.但是本质上它们都是通过标识符映射一个内存地址.

2.函数定义:

TEXT symbol(SB),[flags,] $framesize[-argsize]

函数的定义由五部分组成.TEXT指令 函数名 可选的flags标志 函数帧大小和可选的参数大小.

TEXT用于定义函数符号,函数名中当前包的路径可以省略.函数的名字后面是(SB),表示是函数名符号相对于伪寄存器SB的偏移量.二者组合在一起最终是绝对的地址.作为全局标识符的全局变量和全局函数的名字一般都是基于伪寄存器SB的相对地址.标志部分用于指示函数的一些特殊行为,常见的NOSPLIT主要用于叶子函数不进行栈分裂.framesize部分表示函数的局部变量需要多大栈空间.其中包含调用其他函数时准备调用参数的隐式栈空间.最后可以省略的参数大小,之所以可以省略是因为编译器可以从go语言的函数声明中推导出函数的参数大小.

2.1Go函数:

func Swap(a,b int) (int,int)

2.2汇编格式:

TEXT ·Swap(SB),NOSPLIT,$0-32

TEXT ·Swap(SB),NOSPLIT,$0

3.函数参数和返回值:

Go汇编中引入了一个伪寄存器FP,表示函数的前帧地址,也就是第一个参数的地址,可以通过+0(FP) +8(FP) +16(FP) 和+24(FP)分别来引用a b ret0和ret1这四个参数.

在汇编代码中,并不能直接以0+(FP)的方式来使用参数.任何通过伪寄存器FP访问的变量必须和一个临时标识符前缀组合在一起才有效.

4.函数中的局部变量:

从Go语言函数角度看,局部变量是函数明确定义的变量,同时也包含函数的参数和返回值变量,但从Go汇编角度看,局部变量是指函数运行时在当前函数栈帧所对应的内存变量,不包含参数的参数和返回值.函数栈帧的空间主要由函数参数和返回值 局部变量和其他调用函数的参数和返回值空间组成.

为了便于访问局部变量,Go语言汇编引入了伪寄存器SP,对应当前栈帧的底部.由于在当前栈帧时栈的底部是固定不变的.因此局部变量相对于伪寄存器SP也是固定的.可以简化局部变量的维护工作.真伪寄存器SP的区分只有一个原则,如果使用SP时有一个临时标识符前缀就是伪寄存器SP,否则就是真寄存器SP.例如a(SP)和b+8(SP)有a和b临时前缀,这里就是伪寄存器SP,而前缀部分一般用于表示局部变量的名字.(SP)和+8(SP)没有临时标识符就是真寄存器.

在X86平台,函数调用是从高地址向低地址增长的,因此伪寄存器SP对应栈帧的底部其实是对应更大的地址.当前栈的顶部对应真实存在的寄存器SP,对应当前函数的栈顶.对应更小的内存地址.如果整个内存用Memory数组表示,那么Memory[0(SP):end-0(SP)]就是对应当前栈帧的切片.其中开头位置是真寄存器SP,结尾位置是尾寄存器SP.真寄存器SP一般用于表示其他函数时的参数和返回值,真寄存器SP对应内存较低的地址,所以被访问变量的偏移量是整数.而伪寄存器SP对应高地址.对应的局部变量是负数.

4.1Go函数:

func Foo(){

var c []byte

var b int16

var a bool

}

4.2Go汇编:

TEXT ·Foo(SB),$32-0

MOVQ a-32(SP),AX

MOVQ b-30(SP),BX

MOVQ c_data-24(SP)CX

MOVQ c_len-16(SP)DX

MOVQ c_cap(SP),DI

RET

Foo函数有三个局部变量,但是没有调用其他函数.由于对齐和填充的问题导致函数的栈帧大小为32字节.因为Foo函数没有参数和返回值,所以参数和返回值大小为0字节.