夜owl

困到睡不着
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

栈、栈帧和函数调用

Posted on 2023-05-16 17:11  夜owl  阅读(101)  评论(0编辑  收藏  举报

堆栈(stack)又称为栈或堆叠

一种计算机最常用的数据结构之一,有以下几个相关概念:

  1. 栈顶和栈底:允许元素插入与删除的一端称为栈顶,另一端称为栈底
  2. 压栈POP:栈的插入操作,叫做进栈,也称压栈、入栈。
  3. 弹栈PUSH:栈的删除操作,也叫做出栈。
  4. 栈操作原则(First In Last Out,后进先出)。
  5. 栈增长方向,C语言默认是从高到低,也就是逆增长

需要注意的是,栈顶和栈底容易和增长方向混淆,高到低的增长方向,那么底部是栈顶,也就是“逆”向的。

栈帧

栈操作频繁的就是函数调用,而栈帧就是每个函数独有的栈。

而一个栈中按照函数调用顺序有多个栈帧按增长方向顺序排列,不同的函数的栈帧在一个栈中的位置可以相同,也就是栈帧存在覆盖的可能性,详细的

参考

crash实战:手把手教你使用crash分析内核dump

4.3.2 栈帧格式

栈相关指针

从技术上说,栈就是CPU寄存器里的某个指针所指向的一片内存区域。了解指针是必要的,而且需要知道栈指针由特定寄存器保存,不同平台命名不同,而通用的规则有ATPCS。

SP寄存器-ARM平台(x86/x64平台的ESP寄存器/RSP寄存器

sp, Stack Pointer ,寄存器是栈顶指针,特点为

随着栈操作一直变化

按照栈增长方向增加机器对应位数的数值,以从高到低增长为例,入栈则指针减去4(32位)或8(64位)

BP寄存器-ARM平台(x86/x64平台的EBP寄存器/RBP寄存器

bp寄存器是栈底指针,也是帧指针,指向的是栈帧的底部,特点为

栈操作栈帧指针不会变化

帧指针每次都会随函数调用是指向新的函数栈帧的底部

这里说的可能不清楚,具体可以看函数调用时ebp的变化后回来看,

帧指针当前是函数的栈帧的栈底,里面保存的是上一个函数栈帧的 栈底,每次调用子函数之后都会入栈,以便子函数执行完后,跳回原来的地址,在当前函数返回时恢复上个函数的现场

函数调用

栈操作频繁的就是函数调用,而函数调用在栈的操作有不同的约定,如c语言默认调用约定cdecl为

  函数参数从右向左进栈

  然后返回地址进栈

进入子函数后

  栈帧指针进栈

  临时变量进栈

调用者清栈

详细描述可以参考

堆栈、栈帧、函数调用过程_堆栈和栈帧_Lemonbr的博客-CSDN博客

另外的调用约定

(2)_stdcall:按从右至左的顺序压参数入栈,由被调用者把参数弹出栈。调用约定在输出
函数名前加上一个下划线前缀,后面加上一个“@”符号和其参数的字节数。
(3)_fastcall:主要特点就是快,因为它是通过寄存器来传送参数的,和__stdcall很
象,唯一差别就是头两个参数通过寄存器传送。注意通过寄存器传送的两个参数是从左向右的,
即第一个参数进ECX,第2个进EDX,其他参数是从右向左的入stack。返回仍然通过EAX。

实例

现在再来看栈帧的一个图

int main(int argc, char *args[])
{
    sum(3, 4, 5, 6);  
  return 0;
}

参考

栈和栈帧 - 知乎

va_list 可变长参数原理 - 简书

crash实战:手把手教你使用crash分析内核dump