Go汇编中add函数栈帧24~32地址段未使用原因咨询
Go汇编中栈帧未使用段的原因解析
问题背景
Go代码
package main func add(a, b int) int { sum := 0 sum = a + b return sum } func main() { println(add(1, 2)) }
Go版本
$ go version go version go1.19.1 darwin/amd64
汇编生成命令
$ go tool compile -N -l -S main.go
生成的汇编代码:
main.add STEXT nosplit size=70 args=0x10 locals=0x18 funcid=0x0 align=0x0 0x0000 00000 (main.go:3) TEXT main.add(SB), NOSPLIT|ABIInternal, $24-16 0x0000 00000 (main.go:3) SUBQ $24, SP 0x0004 00004 (main.go:3) MOVQ BP, 16(SP) 0x0009 00009 (main.go:3) LEAQ 16(SP), BP 0x000e 00014 (main.go:3) FUNCDATA $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB) 0x000e 00014 (main.go:3) FUNCDATA $1, gclocals·g2BeySu+wFnoycgXfElmcg==(SB) 0x000e 00014 (main.go:3) FUNCDATA $5, main.add.arginfo1(SB) 0x000e 00014 (main.go:3) MOVQ AX, main.a+32(SP) 0x0013 00019 (main.go:3) MOVQ BX, main.b+40(SP) 0x0018 00024 (main.go:3) MOVQ $0, main.~r0(SP) 0x0020 00032 (main.go:4) MOVQ $0, main.sum+8(SP) 0x0029 00041 (main.go:5) MOVQ main.a+32(SP), AX 0x002e 00046 (main.go:5) ADDQ main.b+40(SP), AX 0x0033 00051 (main.go:5) MOVQ AX, main.sum+8(SP) 0x0038 00056 (main.go:6) MOVQ AX, main.~r0(SP) 0x003c 00060 (main.go:6) MOVQ 16(SP), BP 0x0041 00065 (main.go:6) ADDQ $24, SP 0x0045 00069 (main.go:6) RET 0x0000 48 83 ec 18 48 89 6c 24 10 48 8d 6c 24 10 48 89 H...H.l$.H.l$.H. 0x0010 44 24 20 48 89 5c 24 28 48 c7 04 24 00 00 00 00 D$ H.\$(H..$.... 0x0020 48 c7 44 24 08 00 00 00 00 48 8b 44 24 20 48 03 H.D$.....H.D$ H. 0x0030 44 24 (H.D$.H..$H.l$) 0x0040 10 48 83 c4 18 c3 .H.... main.main STEXT size=86 args=0x0 locals=0x20 funcid=0x0 align=0x0 0x0000 00000 (main.go:8) TEXT main.main(SB), ABIInternal, $32-0 0x0000 00000 (main.go:8) CMPQ SP, 16(R14) 0x0004 00004 (main.go:8) PCDATA $0, $-2 0x0004 00004 (main.go:8) JLS 79 0x0006 00006 (main.go:8) PCDATA $0, $-1 0x0006 00006 (main.go:8) SUBQ $32, SP 0x000a 00010 (main.go:8) MOVQ BP, 24(SP) 0x000f 00015 (main.go:8) LEAQ 24(SP), BP 0x0014 00020 (main.go:8) FUNCDATA $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB) 0x0014 00020 (main.go:8) FUNCDATA $1, gclocals·g2BeySu+wFnoycgXfElmcg==(SB) 0x0014 00020 (main.go:9) MOVL $1, AX 0x0019 00025 (main.go:9) MOVL $2, BX 0x001e 00030 (main.go:9) PCDATA $1, $0 0x001e 00030 (main.go:9) NOP 0x0020 00032 (main.go:9) CALL main.add(SB) 0x0025 00037 (main.go:9) MOVQ AX, main..autotmp_0+16(SP) 0x002a 00042 (main.go:9) CALL runtime.printlock(SB) 0x002f 00047 (main.go:9) MOVQ main..autotmp_0+16(SP), AX 0x0034 00052 (main.go:9) CALL runtime.printint(SB) 0x0039 00057 (main.go:9) CALL runtime.printnl(SB) 0x003e 00062 (main.go:9) NOP 0x0040 00064 (main.go:9) CALL runtime.printunlock(SB) 0x0045 00069 (main.go:10) MOVQ 24(SP), BP 0x004a 00074 (main.go:10) ADDQ $32, SP 0x004e 00078 (main.go:10) RET 0x004f 00079 (main.go:10) NOP 0x004f 00079 (main.go:8) PCDATA $1, $-1 0x004f 00079 (main.go:8) PCDATA $0, $-2 0x004f 00079 (main.go:8) CALL runtime.morestack_noctxt(SB) 0x0054 00084 (main.go:8) PCDATA $0, $-1 0x0054 00084 (main.go:8) JMP 0 0x0000 49 3b 66 10 76 49 48 83 ec 20 48 89 6c 24 18 48 I;f.vIH.. H.l$.H 0x0010 8d 6c 24 18 b8 01 00 00 00 bb 02 00 00 00 66 90 .l$...........f. 0x0020 e8 00 00 00 00 48 89 44 24 10 e8 00 00 00 00 48 .....H.D$......H 0x0030 8b 44 24 10 e8 00 00 00 00 e8 00 00 00 00 66 90 .D$...........f. 0x0040 e8 00 00 00 00 48 8b 6c 24 18 48 83 c4 20 c3 e8 .....H.l$.H.. .. 0x0050 00 00 00 00 eb aa ...... rel 33+4 t=7 main.add+0 rel 43+4 t=7 runtime.printlock+0 rel 53+4 t=7 runtime.printint+0 rel 58+4 t=7 runtime.printnl+0 rel 65+4 t=7 runtime.printunlock+0 rel 80+4 t=7 runtime.morestack_noctxt+0 go.cuinfo.producer.<unlinkable> SDWARFCUINFO dupok size=0 0x0000 2d 4e 20 2d 6c 20 72 65 67 61 62 69 -N -l regabi go.cuinfo.packagename.main SDWARFCUINFO dupok size=0 0x0000 6d 61 69 6e main main..inittask SNOPTRDATA size=24 0x0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x0010 00 00 00 00 00 00 00 00 ........ gclocals·g2BeySu+wFnoycgXfElmcg== SRODATA dupok size=8 0x0000 01 00 00 00 00 00 00 00 ........ main.add.arginfo1 SRODATA static dupok size=5 0x0000 00 08 08 08 ff .....
用户对栈布局的理解
以SP为add函数栈顶,用户梳理的栈区间用途:
- 0~8:
~r0,add函数的返回值 - 8~16:
sum,add函数的局部变量sum - 16~24:BP,用于返回至调用者main函数
- 32~40:
a,add函数的参数a - 40~48:
b,add函数的参数b
未使用段的原因解析
你提到的24~32地址段并非未被使用,而是存储了调用者main执行CALL main.add时自动压入栈的返回地址,这段内容看起来“无操作”的原因如下:
- RET指令自动处理返回地址:当add函数执行到
RET指令时,CPU会自动从栈顶弹出该返回地址,并跳转到该地址继续执行main函数,无需在汇编代码中显式读取或修改这段内容。 - 栈帧结构的规范要求:在x86-64架构的Go函数调用流程中,
CALL指令会先将返回地址(8字节)压入栈,之后add函数通过SUBQ $24, SP分配局部栈空间,此时返回地址恰好位于SP+24的位置(即24~32区间)。这段空间属于调用者栈帧的一部分,被调用者add不需要直接操作它。
另外从add函数的TEXT指令参数$24-16也能佐证:$24是当前函数的局部栈大小(包含BP存储位置),-16是调用者传递的参数总大小(两个int共16字节),而返回地址的8字节是调用过程中CPU自动压入的,不属于局部栈或参数的计数范围。
内容的提问来源于stack exchange,提问作者spongecaptain
相关产品推荐
相关产品推荐

