函数调用过程(栈帧)

2022-04-25 15:25:08 浏览数 (1)

开发环境
  • Ubuntu 14.04(32bits)
  • GCC
  • 编辑器 Cmd Markdown
  • 画图工具 Processon
1,函数调用过程

今天先介绍下基本的函数调用过程,即栈帧。

1.1栈帧

每个函数调用都对应一个栈帧。每个栈帧由ESP和EBP寄存器来确定。每个函数执行时,其局部变量都是在自己对应的栈帧内分配内存。假设A函数调用B函数,此时正在执行B函数,需要指出的是,当执行完当前函数B后,返回调用函数A,此时执行函数B时,为B函数的局部变量分配的的内存空间也就不存在了。也就是说,函数返回值不能是函数体内局部变量的地址,也不能是局部变量的引用。即如不能出现如下两种形式之一:

代码语言:javascript复制
int *test()
{
  int i=123;
  return &i;
}
或者
int &test()
{
  int i=123;
  return i;
}

1.2函数调用过程对应的汇编代码

代码语言:javascript复制
#include<stdio.h>
int main()
{
	int i=10;
	int j=11;
	int k=test(i,j);
	k-=1;
	return 0;
}
int test(int a,int b)
{
	int sum=a b;
	return sum;
}

以上C程序对应的汇编代码如下:

代码语言:javascript复制
.file	"c0.c"
	.text
	.globl	main
	.type	main, @function
main:
.LFB0:
	.cfi_startproc
	pushl	�p //将ebp压栈,即old ebp
	.cfi_def_cfa_offset 8
	.cfi_offset 5, -8
	movl	%esp, �p   //ebp=esp
	.cfi_def_cfa_register 5
	andl	$-16, %esp  //esp=esp&0xFFFFFFF0,即保证esp的最低4位为0
	subl	$32, %esp   //栈向低地址生长,esp-=32
	movl	$10, 20(%esp) //esp 20即局部变量i的地址
	movl	$11, 24(%esp)//esp 24即局部变量j的地址
	movl	24(%esp), �x //将j变量的值赋给eax寄存器
	movl	�x, 4(%esp) //j为test函数的实参,此处将j的值压栈
	movl	20(%esp), �x //将变量i的值赋给eax寄存器
	movl	�x, (%esp) //将变量i的值压栈
	call	test  //调用test函数,其中将下条指令(即movl �x, 28(%esp))
	的地址压栈,即ret addr,然后转到test函数执行,即eip=test
	movl	�x, 28(%esp) //eax即为test函数的返回值,
	将它赋给k,esp 28级为变量k的地址
	subl	$1, 28(%esp) //k=k-1
	movl	$0, �x //eax寄存器保存函数的返回值
	leave
	.cfi_restore 5
	.cfi_def_cfa 4, 4
	ret
	.cfi_endproc
.LFE0:
	.size	main, .-main
	.globl	test
	.type	test, @function
test:
.LFB1:
	.cfi_startproc
	pushl	�p //ebp压栈,即old ebp
	.cfi_def_cfa_offset 8
	.cfi_offset 5, -8
	movl	%esp, �p //ebp=esp
	.cfi_def_cfa_register 5
	subl	$16, %esp //esp-=16
	movl	12(�p), �x  //形参b的值赋给eax寄存器,即eax=11
	movl	8(�p), �x //形参a的值赋给edx寄存器,edx=10
	addl	�x, �x   //将a和b相加,结果赋给eax寄存器
	movl	�x, -4(�p) //ebp-4为局部变量sum的地址,sum=eax
	movl	-4(�p), �x //eax来保存函数的返回值
	leave //等价于 esp=ebp,pop ebp.其中pop ebp即ebp=old ebp
	.cfi_restore 5
	.cfi_def_cfa 4, 4
	ret //等价于pop eip,即eip=ret addr,esp-=4
	.cfi_endproc
.LFE1:
	.size	test, .-test
	.ident	"GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
	.section	.note.GNU-stack,"",@progbits

当main函数调用test函数时,对应的栈帧见下图

当函数test返回后,main函数的栈帧如下图

0 人点赞