Go 栈调用汇编示例

39 阅读3分钟

使用环境

系统架构语言版本
Amd64Go 1.8

示例代码

package main

func main() {
	var a, b int
	a, b = 1, 2
	swap(a, b)
}

func swap(a, b int) {
	a, b = b, a
}

go tool compile -S -N -l main.go | grep -v PCDATA |grep -v FUNCDATA >main.s

汇编代码

"".main STEXT size=88 args=0x0 locals=0x28 funcid=0x0 align=0x0
    0x0000 00000 (main.go:3)   TEXT   "".main(SB), ABIInternal, $40-0
    0x0000 00000 (main.go:3)   CMPQ   SP, 16(R14)
    0x0004 00004 (main.go:3)   JLS    81
    // 阶段1
    0x0006 00006 (main.go:3)   SUBQ   $40, SP
    0x000a 00010 (main.go:3)   MOVQ   BP, 32(SP)
    0x000f 00015 (main.go:3)   LEAQ   32(SP), BP
    0x0014 00020 (main.go:4)   MOVQ   $0, "".a+24(SP)
    0x001d 00029 (main.go:4)   MOVQ   $0, "".b+16(SP)
    0x0026 00038 (main.go:5)   MOVQ   $1, "".a+24(SP)
    0x002f 00047 (main.go:5)   MOVQ   $2, "".b+16(SP)
    0x0038 00056 (main.go:6)   MOVQ   "".a+24(SP), AX
    0x003d 00061 (main.go:6)   MOVL   $2, BX
    // 阶段2
    0x0042 00066 (main.go:6)   CALL   "".swap(SB)
    // 阶段3
    0x0047 00071 (main.go:7)   MOVQ   32(SP), BP
    0x004c 00076 (main.go:7)   ADDQ   $40, SP
    0x0050 00080 (main.go:7)   RET
    0x0051 00081 (main.go:7)   NOP
    0x0051 00081 (main.go:3)   CALL   runtime.morestack_noctxt(SB)
    0x0056 00086 (main.go:3)   JMP    0
    0x0000 49 3b 66 10 76 4b 48 83 ec 28 48 89 6c 24 20 48  I;f.vKH..(H.l$ H
    0x0010 8d 6c 24 20 48 c7 44 24 18 00 00 00 00 48 c7 44  .l$ H.D$.....H.D
    0x0020 24 10 00 00 00 00 48 c7 44 24 18 01 00 00 00 48  $.....H.D$.....H
    0x0030 c7 44 24 10 02 00 00 00 48 8b 44 24 18 bb 02 00  .D$.....H.D$....
    0x0040 00 00 e8 00 00 00 00 48 8b 6c 24 20 48 83 c4 28  .......H.l$ H..(
    0x0050 c3 e8 00 00 00 00 eb a8                          ........
    rel 67+4 t=7 "".swap+0
    rel 82+4 t=7 runtime.morestack_noctxt+0
"".swap STEXT nosplit size=57 args=0x10 locals=0x10 funcid=0x0 align=0x0
    0x0000 00000 (main.go:10)  TEXT   "".swap(SB), NOSPLIT|ABIInternal, $16-16
    0x0000 00000 (main.go:10)  SUBQ   $16, SP
    0x0004 00004 (main.go:10)  MOVQ   BP, 8(SP)
    0x0009 00009 (main.go:10)  LEAQ   8(SP), BP
    0x000e 00014 (main.go:10)  MOVQ   AX, "".a+24(SP)
    0x0013 00019 (main.go:10)  MOVQ   BX, "".b+32(SP)
    0x0018 00024 (main.go:11)  MOVQ   AX, ""..autotmp_2(SP)
    0x001c 00028 (main.go:11)  MOVQ   "".b+32(SP), AX
    0x0021 00033 (main.go:11)  MOVQ   AX, "".a+24(SP)
    0x0026 00038 (main.go:11)  MOVQ   ""..autotmp_2(SP), AX
    0x002a 00042 (main.go:11)  MOVQ   AX, "".b+32(SP)
    0x002f 00047 (main.go:12)  MOVQ   8(SP), BP
    0x0034 00052 (main.go:12)  ADDQ   $16, SP
    0x0038 00056 (main.go:12)  RET
    0x0000 48 83 ec 10 48 89 6c 24 08 48 8d 6c 24 08 48 89  H...H.l$.H.l$.H.
    0x0010 44 24 18 48 89 5c 24 20 48 89 04 24 48 8b 44 24  D$.H.\$ H..$H.D$
    0x0020 20 48 89 44 24 18 48 8b 04 24 48 89 44 24 20 48   H.D$.H..$H.D$ H
    0x0030 8b 6c 24 08 48 83 c4 10 c3                       .l$.H....
go.cuinfo.packagename. SDWARFCUINFO dupok size=0
    0x0000 63 6f 6d 6d                                      comm
gclocals?33cdeccccebe80329f1fdbee7f5874cb SRODATA dupok size=8
    0x0000 01 00 00 00 00 00 00 00                          ........
"".swap.arginfo1 SRODATA static dupok size=5
    0x0000 00 08 08 08 ff                                   .....


调用过程

这段代码是汇编代码,是 Go 语言的编译输出。Go 语言编译器使用 SSA (Static Single Assignment) 形式的中间表示来优化和生成机器代码。

让我们分阶段逐步解析这段代码:

  1. 函数定义:

    • "".main(SB): 这是一个名为 main 的函数。(SB) 表示该函数在文本段中。
    • "".swap(SB): 这是一个名为 swap 的函数。
  2. 函数体:

    • TEXT "".main(SB), ABIInternal, $40-0: 这是 main 函数的开始,40是声明这个函数需要的栈空间的大小,一般来说就是局部变量需要的空间,单位是位。 0是声明函数传入参数和返回值需要的栈空间的大小,单位也是位。
    • TEXT "".swap(SB), NOSPLIT|ABIInternal, $16-16 :这是swap 函数的开始,16是声明这个函数需要的栈空间的大小,一般来说就是局部变量需要的空间,单位是位。16是声明函数传入参数和返回值需要的栈空间的大小,单位也是位。`

    阶段1

    汇编指令代码说明用途说明
    SUBQ $40, SPSP=SP-40从栈指针 SP 减去 40,为局部变量分配空间
    MOVQ BP, 32(SP)BP=SP+32将基指针 BP 的值移动到SP+32的位置
    LEAQ 32(SP), BP将SP+32的地址传输给 BP
    MOVQ $0, "".a+24(SP)将立即数 0 存储到局部变量 a(SP+24),初始化a=0
    MOVQ $0, "".b+16(SP)将立即数 0 存储到局部变量 b(SP+16),初始化b=0
    MOVQ $1, "".a+24(SP)将立即数 1 存储到局部变量 a(SP+24),赋值a=1
    MOVQ $2, "".b+16(SP)将立即数 2 存储到局部变量 b(SP+16),赋值b=2
    MOVQ "".a+24(SP), AX将局部变量 a 的值加载到寄存器 AX
    MOVL $2, BX将立即数 2 加载到寄存器 BX

    阶段2

    汇编指令代码说明用途说明
    SUBQ $16, SPSP=SP-16从栈指针 SP 减去16,为局部变量分配空间
    MOVQ BP, 8(SP)BP=SP+8将基指针 BP 的值移动到SP+8的位置
    LEAQ 8(SP), BP将SP+8的地址传输给 BP
    MOVQ AX, "".a+24(SP)将AX寄存器的值存储到局部变量 a(SP+24),a=AX
    MOVQ BX, "".b+32(SP)将BX寄存器的值存储到局部变量 b(SP+32),b=BX
    MOVQ AX, ""..autotmp_2(SP)将AX寄存器的值存储到一个临时变量autotmp_2
    MOVQ "".b+32(SP), AXb(SP+32) 的值赋值给AX
    MOVQ AX, "".a+24(SP)将AX的值赋值给 a(SP+24)
    MOVQ ""..autotmp_2(SP), AX将临时变量autotmp_2的值赋值给AX
    MOVQ AX, "".b+32(SP)将AX的值赋值给 b(SP+32)
    MOVQ 8(SP), BP(SP+8)的值存储在BP寄存器
    ADDQ $16, SPSP=SP+16SP 增加 16

    阶段3

    汇编指令代码说明用途说明
    MOVQ 32(SP), BP(SP+32)的值存储在BP寄存器
    ADDQ $40, SPSP=SP+40SP增加40,以释放栈帧空间
    RET函数返回
    NOP无操作

未命名文件.png