通用的栈帧结构

C语言在调用过程(函数)的时候使用了栈数据结构提供的后进先出的内存管理原则。

当Q 在执行时, p 以及所有在向上追溯到P 的调用链中的过程,都是暂时被挂起的。

当x86-64 过程需要的存储空间超出寄存器能够存放的大小时,就会在栈上分配空间,这个部分称为过程的栈帧(stack fram)。

下图给出了运行时栈的通用结构,包括把它划分为栈帧。当前正在执行的过程的帧总是在栈顶。

我们可以看到P调用过程Q时,两过程的栈帧时相邻的,且P到Q地址依次减小(往下)。

 

                                                                                   

 

这里关于通用栈帧构造不在赘述,详情看CSAPP中3.7.1章节。

栈帧运行过程

 以简单的main函数调用add函数为例进行讲解。

运行环境:Linux Ubuntu 1404LTS i386 +gcc

#include<stdio.h>
int add(int a,int b)
{
    return a+b;
}
int main()
{
int res=add(1,2);
return 0;
}

gcc对其进行编译

gcc -S hello.c hello.s

使用vscode打开hello.s汇编文件。去掉注释信息。

add:
    pushl    %ebp         //将ebp压入栈【假设ebp=addr2】,保存main函数的栈帧(栈底)
    movl    %esp, %ebp    //将当前栈顶值赋值给%ebp,此时%ebp是新函数(add)的栈底
    movl    12(%ebp), %eax //将12+M(%ebp)内存处的值移动到%eax寄存器,该内存处的值其实是在main函数的栈帧上
    movl    8(%ebp), %edx  //与上类似
    addl    %edx, %eax    //将两寄存器的值相加保存在%eax寄存器上(存储返回值)
    popl    %ebp          //栈弹出,将此时addr2值存入寄存器%ebp,此时栈底指针指向了addr2即为原main函数的栈底
    ret                   //把main函数的返回地址值赋值给rip

main:
    pushl    %ebp        //将main函数上一个函数的ebp压入栈
    movl    %esp, %ebp   //将上一个函数的esp栈顶指针存入%ebp寄存器,此时%esp,%ebp指向同一位置,但%ebp此时的含义是新栈帧(main函数的栈底)
    subl    $24, %esp    //为main函数开辟栈空间24byte
    movl    $2, 4(%esp)  //立即数2存入M(%esp)+4的内存   cdecl方式(实参从右往左先入栈)
    movl    $1, (%esp)   //立即数1存入M(%esp)的内存
    call    add          //调用call时,执行两个动作:将main函数的返回地址压入栈;将add函数的入口地址EntryPoint赋值给rip,随即执行add函数
    movl    %eax, -4(%ebp)  //将%eax寄存器的值移动到内存M(%ebp)
    movl    $0, %eax       //eax赋值为0
    leave
    ret

图解如下所示:

posted @ 2022-06-25 19:22 liweiyin 阅读(434) 评论(0) 推荐(0) 编辑
摘要: 打开终端替换APT源 所使用Linux信息: ubuntu14.04 i386 首先打开终端: open terminal in ubuntu: Ctrl+Alt+T 替换APT源时需要修改/etc/apt/sources.list文件,其需要使用root用户进行操作。 li@li-virtual- 阅读全文
posted @ 2022-06-18 18:28 liweiyin 阅读(161) 评论(0) 推荐(0) 编辑
摘要: 本文依据已故的印度程序员Harsha Suryanarayana图文讲解整理而来。 B站视频链接:https://www.bilibili.com/video/BV1bo4y1Z7xf 指针的基本介绍 关键词组:内存图示(体系架构)、数据所在内存区域(文本区、常量区、堆区、栈区),不同数据类型的内存 阅读全文
posted @ 2022-06-05 12:12 liweiyin 阅读(443) 评论(0) 推荐(0) 编辑
摘要: 在MFC中常常要处理控件与控件实际关联变量(数据成员变量)之间的数据关系,MFC为我们提供了一种方式,通过将控件与其实际成员变量进行关联,通过DDX实现两者之间的数据交换。 不采用关联变量来对控件进行设值的时候,使用如下方式来设置: 1使用GetDlgItem(IDC_EDIT1)->GetWind 阅读全文
posted @ 2021-11-14 14:44 liweiyin 阅读(1178) 评论(0) 推荐(0) 编辑
摘要: 按钮控件 按钮控件包括命令按钮(Button)、单选按钮(Radio Button)和复选框(Check Box)等。 按钮控件会向父窗口发送通知消息,最常用的通知消息莫过于BN_CLICKED和BN_DOUBLECLICKED了。用户在按钮上单击鼠标时会向父窗口发送BN_CLICKED消息,双击鼠 阅读全文
posted @ 2021-11-13 16:44 liweiyin 阅读(1529) 评论(0) 推荐(0) 编辑
摘要: CWnd类介绍 CWnd类提供 Microsoft 基础类库中所有窗口类的基本功能。 当我们建立一个CMyWindowDlg 对话框时,该对话框与CWnd类之间的继承关系如下: CMyWindowDlg <- CDialogEx <- CDialog <- CWnd(通用窗口类,所有的窗口,包括控件 阅读全文
posted @ 2021-11-02 15:40 liweiyin 阅读(2281) 评论(0) 推荐(0) 编辑
摘要: MFC程序各个文件 在上一篇《从Win32过渡到MFC工程》中,我们将所有的逻辑写在了一个cpp文件中,现在我们对文件进行整理。 FirstMFC的功能:弹出一个窗口,显示窗口对应的图标、窗口名、窗口可以相应各种事件。 FirstMFC头文件与CPP文件分开 FirstMFC.h进行声明 class 阅读全文
posted @ 2021-10-20 16:02 liweiyin 阅读(324) 评论(0) 推荐(0) 编辑
摘要: 命名管道定义 一个命名管道是一个命名的,单向或双面管道的管道服务器和一个或多个管道客户端之间的通信。命名管道的所有实例共享相同的管道名称,但每个实例都有自己的缓冲区和句柄,并为客户端/服务器通信提供单独的管道。实例的使用使多个管道客户端能够同时使用同一个命名管道。 这里要理解实例的概念:当我用Cre 阅读全文
posted @ 2021-10-19 13:47 liweiyin 阅读(3132) 评论(0) 推荐(0) 编辑
摘要: MFC简介 MFC(Microsoft Foundation Classes),是微软公司提供的一个类库(class libraries),以C++类的形式封装了Windows的API,并且包含一个应用程序框架,以减少应用程序开发人员的工作量。其中包含的类包含大量Windows句柄封装类和很多Win 阅读全文
posted @ 2021-10-15 09:16 liweiyin 阅读(342) 评论(0) 推荐(0) 编辑
摘要: 模态对话框与非模态对话定义 一般来说,Windows应用程序中,对话框分为模态对话框和非模态对话框两种。二者的区别在于当对话框打开时,是否允许用户进行其他对象的操作。 模态对话框窗口打开之后,必须操作完该窗口,才能去操作自己程序的其他窗口(大多数对话框); 非模态对话框窗口打开之后,允许还没有操作完 阅读全文
posted @ 2021-10-10 22:16 liweiyin 阅读(642) 评论(0) 推荐(0) 编辑
点击右上角即可分享
微信分享提示