c语言 栈帧大小,C语言下栈帧结构与函数调用过程

讲解之前首先明确一下call与ret指令到底做了什么

再一个,push pop指令也会影响esp指针

我们知道,函数调用是通过栈来实现的,那么这个过程具体是如何呢?

不妨先忘掉栈,假如我们来实现函数调用会怎么做。

函数调用这个过程要满足一下几点:将调用参数放在某处

调用者保存当前函数执行断掉的位置

跳转到被调用函数

函数取得参数,进行运算

运算结果保存在某处

被调用者跳转到调用者断掉的位置

取得被调用者的运算结果

从上面的过程大致可以看出,被调用者与调用者要约定:参数的存放位置

结果的存放位置

调用者断掉的地址的存放位置

再一个,被调用函数运算过程中还可能继续进行函数调用,于是上述三个位置需要多对,但是呢同一时刻只有一个函数调用结果等待交付,因此结果的存放位置可以复用,而其他的要可扩展,于是自然想到用一块连续的内存来依次存储:

stack_frame_1.png

可以看到每一个函数对应一组数据,姑且称之为一帧数据好了。

假设我们的机器数据总线与地址总线都是32位,那么断点地址占用4个字节,参数类型包括整型、浮点数和指针,那么一个参数也占用4个字节,CPU的当前指令位置用PC表示,并且连续内存的存储从高地址向低地址扩展,并用指针LP表示最后一帧数据的结束地址,那么我们模拟一下函数调用过程:

stack_frame_2.pngLAST_LP

LP

存入参数 LP

如果还有参数,重复步骤3 。否则到步骤5。

调用函数,在调用函数之前断点位置存入步骤2的预留空间 PC

被调用者从LP开始读取参数

咦?好像出问题,被调用者怎么知道从哪儿开始读取参数,又怎么知道读取到哪儿结束呢,又怎么知道参数类型的呢?

对于第一个问题,读取参数直到内存地址为LAST_LP-4。这就是为啥要保存LP的初始值的原因了。

那么保存LAST_LP是在调用函数的上下文中,被调用函数又是怎么知道的呢?简单,把这个也放入一帧数据中,这个地址也占用4个字节,

相应的,前面的步骤改为:LAST_LP = LP

LP = LP - 8 预留断点位置和LAST_LP的存储位置

进一步,为了避免预留位置这一步有点绕的操作,我们将一帧数据中的参数放在最前面,断点位置放后面,LAST_LP放在中间。

于是我们的存储结构变成了:

stack_frame_3.pngLAST_LP

LP

如果还有参数,重复步骤2 。否则到步骤4。

LP

调用函数。在调用函数之前, LP

被调用者取出LAST_LP LAST_LP

被调用者执行运算,结果放入约定的结果寄存器

接下来就要回到调用者步骤了,注意调用者也需要根据LP寻址自己的参数,因此需要恢复调用者的LP

LAST_PC

LP

PC

从约定寄存器中取出结果

接下来继续根据文章完善本文,然后用小程序和gdb验证

注意说明一个小点,编译器优先使用寄存器传参数,寄存器装不下才会使用栈

以及是在帧指针指向帧顶的情况下是如何确定参数界限的

https://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/

主要讲了x86-64下的栈帧结构,与x86基本相同。提到了AMD64 ABI定义的red zone–一种叶子函数的优化。另外提到了rbp寄存器对于编译器来讲,其实不需要,编译器可以根据rsp来推算。

http://www.unixwiz.net/techtips/win32-callconv-asm.html

从函数调用规范(function-call conventions)方面来看栈,其中提到__cdecl与__stdcall两种的区别,前者可传可变长的参数,由调用者来释放参数内存,后者不能,因此可以有被调用者来

释放参数内存。理论上讲,可变长参数下,调用者和被调用者同时编译的情况下,编译器知道传入了几个参数,因此释放内存的代码放在被调用者下似乎也没什么问题,但要想到函数可以被多处调用,可变长的情况下除非被调用者知道有几个参数,否则被调用者不能清理,因为函数编译的时候,它的调用者还是未知的。另外,参数由被调用者释放真的合理吗,如果是引用传参显然是会出现问题的。

https://www.cnblogs.com/bangerlee/archive/2012/05/22/2508772.html

结合gdb实际验证了函数调用栈的结构。注意该例是在x86-64体系下,叶子函数利用了red zone,注意leave指令的语义:恢复调用函数的rsp与rbp寄存器。

https://en.wikipedia.org/wiki/Call_stack blog comments powered by Disqus

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值