函数调用过程中的栈帧变化

发布于:2025-06-16 ⋅ 阅读:(14) ⋅ 点赞:(0)
int add(int a, int b) {
    int c = a + b;
    return c;
}

int main() {
    int result = add(1, 2);
    return 0;
}

生成汇编代码:g++ -S Cplus.cpp -o Cplus.s


	.file	"Cplus.cpp"
	.text
	.globl	_Z3addii
	.def	_Z3addii;	.scl	2;	.type	32;	.endef
	.seh_proc	_Z3addii
_Z3addii:
	pushq	%rbp
	.seh_pushreg	%rbp
	movq	%rsp, %rbp
	.seh_setframe	%rbp, 0
	subq	$16, %rsp
	.seh_stackalloc	16
	.seh_endprologue
	movl	%ecx, 16(%rbp)
	movl	%edx, 24(%rbp)
	movl	16(%rbp), %edx
	movl	24(%rbp), %eax
	addl	%edx, %eax
	movl	%eax, -4(%rbp)
	movl	-4(%rbp), %eax
	addq	$16, %rsp
	popq	%rbp
	ret
	.seh_endproc
	.def	__main;	.scl	2;	.type	32;	.endef
	.globl	main
	.def	main;	.scl	2;	.type	32;	.endef
	.seh_proc	main
main:
	pushq	%rbp	# 将寄存器的值压入栈中
	.seh_pushreg	%rbp # 上一个栈帧的基址
	movq	%rsp, %rbp # 当前栈帧的基址
	.seh_setframe	%rbp, 0
	subq	$48, %rsp # 预留空间
	.seh_stackalloc	48
	.seh_endprologue
	call	__main
	movl	$2, %edx
	movl	$1, %ecx
	call	_Z3addii # 调用函数
	movl	%eax, -4(%rbp)
	movl	$0, %eax
	addq	$48, %rsp
	popq	%rbp
	ret
	.seh_endproc
	.ident	"GCC: (x86_64-win32-sjlj-rev0, Built by MinGW-W64 project) 8.1.0"

  • 函数调用过程:
    初始状态: rbp = rsp = 0x1000
地址 栈中的内容 执行的操作 指令
0x0ff8 上一个函数的栈帧基址(rbp=0x1000) rsp-=8 0x0ff8->rsp pushq %rbp
rbp=rsp=0x0ff8,表示当前函数栈帧的基址 movq %rsp, %rbp
0x0ff0 eax值(0x0ff8,0x0ff4)
0x0fe8
0x0fe0
0x0fd8
0x0fd0
0x0fc8 rsp-=48 0x0fc8->rsp subq $48, %rsp
0x0fc0 返回地址 rsp-=8 0x0fc0->rsp call __main
0x0fb8 返回地址 rsp-=8 0x0fb8->rsp call _Z3addii
0x0fb0 main函数的栈帧基址(rbp=0x0ff8) rsp-=8 0x0fb0->rsp pushq %rbp
0x0fa8 eax值 (0x0fb0,0x0fa4) rbp=rsp=0x0fb0,表示当前函数栈帧的基址 movq %rsp, %rbp
0x0fa0 rsp-=16 0x0fa0->rsp subq $16, %rsp
0x0f98

注意:

  1. 参数传递规则​​
    ​​前 4 个整数参数​​:通过寄存器传递(顺序:rcx, rdx, r8, r9)。
    ​​多余参数​​:通过栈传递(从右向左压栈)。
    这是反汇编或编译器生成的代码,可能是调试版本或未优化的结果。实际参数应直接从寄存器读取:
  2. movl %ecx, 16(%rbp)的问题
    虽然 subq $48, %rsp 预留了 48 字节栈空间,但:
    实际只用了 4 字节(保存返回值);
    剩下的是 对齐 + 编译器保守行为 + SEH支持 的结果;
    并不是异常,是很正常且常见的行为。
  • 函数返回过程:
movl	%ecx, 16(%rbp)
movl	%edx, 24(%rbp)
movl	16(%rbp), %edx
movl	24(%rbp), %eax
addl	%edx, %eax
movl	%eax, -4(%rbp)
movl	-4(%rbp), %eax

addq	$16, %rsp   # 释放空间
popq	%rbp        # rbp = *(rsp), rsp+=8
ret                 # 读取rsp获得返回地址,rsp+=8
  • 返回到主函数后:
movl	%eax, -4(%rbp)  # 
movl	$0, %eax        # 寄存器置于0
addq	$48, %rsp       # 释放空间
popq	%rbp            # rbp = *(rsp), rsp+=8
ret                     # 读取rsp获得返回地址,rsp+=8,最终rsp=0x1000(初始值)

网站公告

今日签到

点亮在社区的每一天
去签到