栈帧

概念

栈帧:也叫过程活动记录,是编译器用来实现过程/函数调用的一种数据结构,每次函数的调用,都会在调用栈(call stack)上维护一个独立的栈帧(stack frame)

栈帧的内容

  • 函数的返回地址和参数

  • 临时变量:包括函数的非静态局部变量,以及编译器自动生成的其他临时变量

  • 函数调用上下文

  • 两个指针:ebp又称帧指针(frame pointer),指向当前栈帧的底部; esp,又称栈针织(stack pointer),始终指向栈顶

函数调用

函数调用过程中分:函数调用者(caller)和被调用的函数(callee)

调用者需要知道被调用函数的返回值

被调用函数需要知道传入的参数和返回的地址

步骤:

  1. 参数入栈:将参数按照调用约定依次压入系统栈

  2. 返回地址入栈:将当前代码区调用指令的下一条指令地址压入栈,供函数返回时继续执行,也就是保护现场和恢复现场

  3. 代码跳转:处理器将代码区跳转到被调用函数的入口处

  4. 栈帧调整:

  5. 将调用者的ebp压入栈,保存指向栈底ebp地址(用于恢复现场),此时esp指向新的栈顶位置

  6. 将当前栈帧切换到新栈帧(将esp值装入ebp,跟新栈底),此时ebp指向栈顶,

  7. 给新栈帧分配空间

函数返回

步骤:

  1. 保存被调用函数的返回值到eax寄存器

  2. 恢复esp同时收回局部变量空间

  3. 将上一个栈帧底部位置恢复到ebp

  4. 弹出当前元素栈顶元素,从栈中取到返回地址,并跳转到该位置,也就是再回到调用者,执行后续代码

举例说明

c代码:

int sum(int x,int y){
int z=x+y;
return z;
}
int main(){
int a=1;
int b=3;
int c=sum(a,b);
}

汇编,且关闭编译器优化-O0

    .file    "test.c"
.text
.globl sum
.def sum; .scl 2; .type 32; .endef
.seh_proc sum
sum:
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 $1, -4(%rbp)
movl $3, -8(%rbp)
movl -8(%rbp), %edx
movl -4(%rbp), %eax
movl %eax, %ecx
call sum
movl %eax, -12(%rbp)
movl $0, %eax
addq $48, %rsp
popq %rbp
ret
.seh_endproc
.ident "GCC: (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0"

过程详解:

补充:整个过程中虽然看不到RIP,但它一直被使用,RIP每次都指向下一条将要运行的指令

每次取出一条指令时,RIP就会自动偏移,指向下一条指令,如图:

当发生函数调用时,也就是call时,callq 指令会自动将rip压入栈,并将rip指向被调用的函数,如

先RIP指向 callq f(),下一次执行就是调用函数f(),查看此时的rsp

接着执行该指令

ip跳到了f()内的第一条指令push %rbq,再查看rsp

rsp中存入了0x00401586,正是call的下一条指令的位置

且也可以查看变量在栈帧内的存储形式

将1赋值给变量b,即mov 1 -4(%bp),查看内存

就是在bp的偏移4字节处

再来看ret,ret是将之前存的RIP给出栈,经过sub分配空间然后再add释放空间,pop rbp后,rsp刚好在旧的rip处

此时执行ret,会自动执行pop rip,也就恢复了现场

也就是说:

call f的本质是:

push %rip
mov f,%rip

ret的本质是:

pop %rip

C温故补缺(十五):栈帧的更多相关文章

  1. Kinect for Windows SDK v2.0 开发笔记 (十五) 手势帧

     (转载请注明出处) 使用SDK: Kinect for Windows SDK v2.0 public preview1409 同前面,由于SDK未完毕,不附上函数/方法/接口的超链接. 这次最 ...

  2. “全栈2019”Java多线程第三十五章:如何获取线程被等待的时间?

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  3. “全栈2019”Java多线程第二十五章:生产者与消费者线程详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  4. “全栈2019”Java多线程第十五章:当后台线程遇到finally

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  5. “全栈2019”Java异常第十五章:异常链详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java异 ...

  6. “全栈2019”Java第九十五章:方法中可以定义静态局部内部类吗?

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  7. “全栈2019”Java第八十五章:实现接口中的嵌套接口

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  8. “全栈2019”Java第七十五章:内部类持有外部类对象

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  9. “全栈2019”Java第六十五章:接口与默认方法详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  10. “全栈2019”Java第五十五章:方法的静态绑定与动态绑定

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

随机推荐

  1. centos7设置时间和上海时区并进行同步

    1.设置时区(同步时间前先设置) timedatectl set-timezone Asia/Shanghai 2.安装组件 yum -y install ntp systemctl enable n ...

  2. 谣言检测——《MFAN: Multi-modal Feature-enhanced Attention Networks for Rumor Detection》

    论文信息 论文标题:MFAN: Multi-modal Feature-enhanced Attention Networks for Rumor Detection论文作者:Jiaqi Zheng, ...

  3. vue3中defineComponent 的作用

    vue3中,新增了 defineComponent ,它并没有实现任何的逻辑,只是把接收的 Object 直接返回,它的存在是完全让传入的整个对象获得对应的类型,它的存在就是完全为了服务 TypeSc ...

  4. Gimbal Lock欧拉角死锁问题

    技术背景 在前面几篇跟SETTLE约束算法相关的文章(1, 2, 3)中,都涉及到了大量的向量旋转的问题--通过一个旋转矩阵,给定三个空间上的欧拉角\(\alpha, \beta, \gamma\), ...

  5. Kubernetes 控制器

    在实际使用的时候并不会直接使用 Pod,而是会使用各种控制器来满足我们的需求,Kubernetes 中运行了一系列控制器来确保集群的当前状态与期望状态保持一致,它们就是 Kubernetes 的大脑. ...

  6. 图解 Kubernetes Ingress

    文章转载自:https://www.qikqiak.com/post/visually-explained-k8s-ingress/ 原文链接: https://codeburst.io/kubern ...

  7. Solutions:安全的APM服务器访问

    转载自: https://blog.csdn.net/UbuntuTouch/article/details/105527468 APM Agents 访问APM server如果不做安全的设置,那么 ...

  8. FastDFS与nginx配置使用的配置信息

    # 获取图片 location /group[1-9]/M0[0-9] { root /home/vdc1/fastdfs_storage/data; ngx_fastdfs_module; } # ...

  9. PostgreSQL 创建表格

    PostgreSQL 使用 CREATE TABLE 语句来创建数据库表格. 语法 CREATE TABLE 语法格式如下: CREATE TABLE table_name( column1 data ...

  10. CentOS8本地安装Redash中文版,并且配置为生产环境

    Centos8内置的Python为3.6.8版本,以下是在内置Python3.6.8基础上的安装步骤.由于安装多版本Python会导致系统底层库需要下载源码重新编译,比较麻烦,不建议在多版本Pytho ...