golang汇编学习(寄存器)

发布时间 2023-07-02 22:33:51作者: LuoTian

好久没有写博客了,上一篇写的汇编只是简单的调试了一下,这段时间又看了下,做一个简单的汇总。

两个代码

汇编代码,1_amd64.s

TEXT ·add(SB),$0-0
MOVQ a+0(FP),AX
MOVQ b+8(FP),BX
ADDQ AX,BX
MOVQ BX,c+16(FP)
RET

主函数1.go

package main

func add(a, b int) int
func main() {
    sum := add(1, 2)
    println(sum)
}

主要对这两个简单的代码进行调试,实现的功能也比较简单:计算两个数之和。

main函数的初始化

 

注意:上面的汇编代码是使用未优化的。

main函数是被系统调用的,首先将SP拉伸到40字节,然后将BP寄存器入栈,接着再调整BP到该位置,当前BP所在的位置应该算是栈空间的开始!

main函数的一些变量如何分配?

 在go语言汇编里面,当A调用B时,在A的栈上准备参数(和返回值,如果有),图示展示了这一点。main函数调用add,因此它准备了两个实参和一个返回值,一共24字节,同时还有一个局部变量sum。

可见它也没有把sum优化,也就是说本来是什么就是什么,按部就班的将各个参数和局部变量排布好。

在执行call调用时,会将PC压栈,同时SP再往下前进8字节。

1_amd64.s分析

分析完了main.go,接着看1_amd64.s文件,这部分代码是由人工写的,所以考虑的比较简单。

FP寄存器

FP伪寄存器按照书本的说法就是提供一个快捷的方式访问函数参数,它的位置应该在:

 由图可见,伪FP直接对着函数参数,因此a+0(FP)就对应实参a,b+8(FP)就对应实参b,同理,c+16(FP)就对应返回值c。

因此高亮的汇编代码意思是:

1和2搬到栈中,然后执行call调用,将结果搬到add返回值空间,再接着将该返回值复制一份到局部变量sum!

伪SP寄存器

按书本上说,伪SP寄存器对应当前栈帧的底部。此时,请读者注意,在1_amd64.s文件中,我没有进行SP的拉伸和BP的移动,因为这个代码是我写的,不是编译器写的。

因此当前相当于只移动了真SP,原因是真SP会随着call的执行必须移动。

那么这样说,意思就是进入到add函数时,当前栈的分布还是像上图一样没变化。因此当前的栈底就是真SP所在的位置。

 由于伪SP对应当前栈的栈底,因此它的一个特性是固定不变,可以用做计算局部变量的位置。

BP到底会不会压栈?

我们在X86的汇编中知道,一般情况下BP会入栈的,但是GO中似乎提到可以简化,这个问题先搁置在这里,后面再说明。

当前的1_amd64.s并没有将BP入栈,因为这是人工写的。

下面我们在程序中再写一个简单的减法函数,然后编译查看一下。

func sub(a, b int) int {
    c := a - b
    return c
}

编译之后得到:

"".sub STEXT nosplit size=52 args=0x18 locals=0x10
        0x0000 00000 (1.go:4)   TEXT    "".sub(SB), NOSPLIT|ABIInternal, $16-24
        0x0000 00000 (1.go:4)   SUBQ    $16, SP
        0x0004 00004 (1.go:4)   MOVQ    BP, 8(SP)
        0x0009 00009 (1.go:4)   LEAQ    8(SP), BP
        0x000e 00014 (1.go:4)   FUNCDATA        $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
        0x000e 00014 (1.go:4)   FUNCDATA        $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
        0x000e 00014 (1.go:4)   MOVQ    $0, "".~r2+40(SP)
        0x0017 00023 (1.go:5)   MOVQ    "".a+24(SP), AX
        0x001c 00028 (1.go:5)   SUBQ    "".b+32(SP), AX
        0x0021 00033 (1.go:5)   MOVQ    AX, "".c(SP)
        0x0025 00037 (1.go:6)   MOVQ    AX, "".~r2+40(SP)
        0x002a 00042 (1.go:6)   MOVQ    8(SP), BP
        0x002f 00047 (1.go:6)   ADDQ    $16, SP
        0x0033 00051 (1.go:6)   RET
        0x0000 48 83 ec 10 48 89 6c 24 08 48 8d 6c 24 08 48 c7  H...H.l$.H.l$.H.
        0x0010 44 24 28 00 00 00 00 48 8b 44 24 18 48 2b 44 24  D$(....H.D$.H+D$
        0x0020 20 48 89 04 24 48 89 44 24 28 48 8b 6c 24 08 48   H..$H.D$(H.l$.H
        0x0030 83 c4 10 c3  

从结果可以看到:BP被入栈了,这是未优化的代码

其实BP入不入栈并没有什么影响,只要计算相对位置就行

总结

这篇文章详细说明了伪SP,伪FP,BP是否入栈,以及各个参数的空间安排,了解这些细节对于常见的汇编阅读已经足够。