当前位置: 首页 > news >正文

函数栈桢原理

手撕虚拟内存(8)——函数栈桢原理

原文: Hack the virtual memory Archives - Holberton
翻译:RobotCode俱乐部

正如我们在之前的文章中看到的,栈驻留在虚拟内存的高地址端,并向下增长。但它究竟是如何工作的呢?如何将其转换为汇编代码?使用什么寄存器?在这一章中,我们将进一步了解栈的工作方式,以及程序如何自动分配及释放本地变量。

一旦我们理解了这一点,我们就可以对它进行一些操作,从而控制程序的执行流程。

Note:我们将只讨论用户栈,而不是内核栈!

之前其他系列的文章也讨论了函数栈桢原理,可以一起服用,效果更好:

RobotCode俱乐部:函数调用之堆栈原理(一)

RobotCode俱乐部:函数调用之堆栈原理(二)

RobotCode俱乐部:函数调用之堆栈原理(三)

RobotCode俱乐部:函数调用之堆栈原理(终章)--缓冲区溢出

自动分配变量

让我们先来看一个非常简单的程序,它只使用一个本地变量:

#include <stdio.h>

int main(void)
{
    int a;
    a = 972;
    printf("a = %d\n", a);
    return (0);
}

让我们编译这个程序并使用objdump对其进行反汇编:(译者注:原作者为64位系统,译者为32位系统机器,但是为了和原文中图片示意保持一致,这里直接贴上原文的结果,不像之前的文章在本地环境实践过

main函数输出汇编程序如下:

000000000040052d <main>:
  40052d:       55                      push   rbp
  40052e:       48 89 e5                mov    rbp,rsp
  400531:       48 83 ec 10             sub    rsp,0x10
  400535:       c7 45 fc cc 03 00 00    mov    DWORD PTR [rbp-0x4],0x3cc
  40053c:       8b 45 fc                mov    eax,DWORD PTR [rbp-0x4]
  40053f:       89 c6                   mov    esi,eax
  400541:       bf e4 05 40 00          mov    edi,0x4005e4
  400546:       b8 00 00 00 00          mov    eax,0x0
  40054b:       e8 c0 fe ff ff          call   400410 <printf@plt>
  400550:       b8 00 00 00 00          mov    eax,0x0
  400555:       c9                      leave  
  400556:       c3                      ret    
  400557:       66 0f 1f 84 00 00 00    nop    WORD PTR [rax+rax*1+0x0]
  40055e:       00 00 

现在我们先来看看前三行:

000000000040052d <main>:
  40052d:       55                      push   rbp
  40052e:       48 89 e5                mov    rbp,rsp
  400531:       48 83 ec 10             sub    rsp,0x10

函数的第一行涉及rbp和rsp;这些是专用寄存器。rbp是指向当前栈桢底部的基指针,rsp是指向当前栈桢顶部的堆栈指针。(译者注:在很多翻译过来的书上,有些地方将Stack翻译为栈桢,有的地方叫堆栈,只要知道这里的堆栈是指Stack,Heap没关系就好)

Step1:

让我们一步一步地分解这里发生的事情。这是我们在进入main函数之前,准备运行其第一条指令时栈的状态:

Step2:

push rbp指令将寄存器rbp的值推入堆栈。因为它“push”到堆栈上,所以现在rsp的值是堆栈新顶部的内存地址。堆栈和寄存器现在看起来是这样的:

Step3:

mov rbp, rsp,将堆栈指针rsp的值复制到基指针rbp -> rbp和rsp现在都指向堆栈的顶部。

Step4:

sub rsp, 0x10创建一个空间来存储本地变量的值。rbp和rsp之间的空间就是这个空间。注意,这个空间足够大,可以存储类型为integer的变量。

我们刚刚在内存中为本地变量在堆栈上创建了一个空间。这个空间称为栈帧。每个具有局部变量的函数都将使用栈桢来存储这些变量。

使用局部变量(给局部变量赋值)

main函数的第4行汇编代码如下:

 400535:       c7 45 fc cc 03 00 00    mov    DWORD PTR [rbp-0x4],0x3cc

0x3cc实际上是十六进制中的值972。这行对应于C代码行:

a = 972;

mov DWORD PTR [rbp-0x4],0x3cc:将内存地址为rbp- 4的内容设置为972。[rbp - 4]是我们的局部变量a。计算机实际上并不知道我们在代码中使用的变量的名称,它只是引用栈上的内存地址。

这是这个操作之后堆栈和寄存器的状态:

leave,自动释放

如果我们看一下函数的末尾,我们会发现:

400555:       c9                      leave  

指令leave将rsp设置为rbp,然后将堆栈顶部弹出到rbp中。

因为我们在进入函数时将rbp的上一个值推入堆栈,所以rbp现在被设置为rbp的上一个值。这就是:

  • 局部变量释放
  • 在我们离开当前函数之前,将恢复上一个函数的栈桢。

堆栈和寄存器rbp和rsp的状态恢复到进入main函数时的状态。

相关文章:

  • JSP面试题(重要)
  • 华为FreeBuds pro2大风场景下降噪差原因
  • 网课搜题接口对接教程
  • ORM基本操作
  • 数据结构-压缩软件核心-C++(利用哈夫曼树进行编码,对文件进行压缩与解压缩)
  • SSM学生成绩管理系统毕业设计-附源码070942
  • springboot宴会预定平台毕业设计-附源码231718
  • springboot大学新生小助手小程序毕业设计-附源码060917
  • LeetCode50天刷题计划(Day 35—不同路径II (8.00-9.20)
  • C语言的输入和输出
  • 大数据环境下使用机器学习算法的入侵检测模型
  • Vue3 从入门到放弃 (第三篇.组件的使用)
  • 跑通DIMP(Windows10)
  • 02-Ajax入门
  • Linux系统中Qt安装
  • 2017年终总结、随想
  • Android框架之Volley
  • iOS 系统授权开发
  • js ES6 求数组的交集,并集,还有差集
  • Js基础知识(一) - 变量
  • leetcode378. Kth Smallest Element in a Sorted Matrix
  • vue从创建到完整的饿了么(11)组件的使用(svg图标及watch的简单使用)
  • webpack项目中使用grunt监听文件变动自动打包编译
  • 从0实现一个tiny react(三)生命周期
  • 从伪并行的 Python 多线程说起
  • 关键词挖掘技术哪家强(一)基于node.js技术开发一个关键字查询工具
  • 基于 Ueditor 的现代化编辑器 Neditor 1.5.4 发布
  • 简析gRPC client 连接管理
  • 精益 React 学习指南 (Lean React)- 1.5 React 与 DOM
  • 理解在java “”i=i++;”所发生的事情
  • 区块链分支循环
  • 三分钟教你同步 Visual Studio Code 设置
  • 使用Tinker来调试Laravel应用程序的数据以及使用Tinker一些总结
  • 看到一个关于网页设计的文章分享过来!大家看看!
  • 仓管云——企业云erp功能有哪些?
  • #{}和${}的区别是什么 -- java面试
  • #每日一题合集#牛客JZ23-JZ33
  • (1)(1.9) MSP (version 4.2)
  • (C语言)fread与fwrite详解
  • (LeetCode) T14. Longest Common Prefix
  • (动手学习深度学习)第13章 计算机视觉---微调
  • (深入.Net平台的软件系统分层开发).第一章.上机练习.20170424
  • (生成器)yield与(迭代器)generator
  • (四)模仿学习-完成后台管理页面查询
  • (学习日记)2024.03.12:UCOSIII第十四节:时基列表
  • (已解决)vue+element-ui实现个人中心,仿照原神
  • (转)Scala的“=”符号简介
  • (转)程序员技术练级攻略
  • (转)人的集合论——移山之道
  • (转)如何上传第三方jar包至Maven私服让maven项目可以使用第三方jar包
  • *(长期更新)软考网络工程师学习笔记——Section 22 无线局域网
  • .gitignore文件---让git自动忽略指定文件
  • .net 反编译_.net反编译的相关问题
  • .NET 命令行参数包含应用程序路径吗?
  • .net程序集学习心得