转载自地址:http://blog.csdn.net/zsy2020314/article/details/9429707

 

 

 

今天突然想分析一下函数在相互调用过程中栈帧的变化,还是想尽量以比较清晰的思路把这一过程描述出来,关于c函数调用原理的理解是很重要的。

1.关于栈

首先必须明确一点也是非常重要的一点,栈是向下生长的,所谓向下生长是指从内存高地址->低地址的路径延伸,那么就很明显了,栈有栈底和栈顶,那么栈顶的地址要比栈底低。对x86体系的CPU而言,其中

---> 寄存器ebp(base pointer )可称为“帧指针”或“基址指针”,其实语意是相同的。

---> 寄存器esp(stack pointer)可称为“ 栈指针”。

要知道的是:

---> ebp 在未受改变之前始终指向栈帧的开始,也就是栈底,所以ebp的用途是在堆栈中寻址用的。

---> esp是会随着数据的入栈和出栈移动的,也就是说,esp始终指向栈顶。

见下图,假设函数A调用函数B,我们称A函数为"调用者",B函数为“被调用者”则函数调用过程可以这么描述:

(1)先将调用者(A)的堆栈的基址(ebp)入栈,以保存之前任务的信息。

(2)然后将调用者(A)的栈顶指针(esp)的值赋给ebp,作为新的基址(即被调用者B的栈底)。

(3)然后在这个基址(被调用者B的栈底)上开辟(一般用sub指令)相应的空间用作被调用者B的栈空间。

(4)函数B返回后,从当前栈帧的ebp即恢复为调用者A的栈顶(esp),使栈顶恢复函数B被调用前的位置;然后调用者A再从恢复后的栈顶可弹出之前的ebp值(可以这么做是因为这个值在函数调用前一步被压入堆栈)。这样,ebp和esp就都恢复了调用函数B前的位置,也就是栈恢复函数B调用前的状态。

这个过程在AT&T汇编中通过两条指令完成,即:

leave

ret

这两条指令更直白点就相当于:

mov   %ebp , %esp

pop    %ebp

2.举个简单的实例,从汇编的视角看函数调用

2.1建立一个简单的程序,程序文件名为  main.c

开发测试环境:

Ubuntu 12.04

gcc版本:4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)  (是Ubuntu自带的)

  1. <span style="font-size:18px;">/*main.c代码:*/
  2. void swap(int *a,int *b)
  3. {
  4. int c;
  5. c = *a;
  6. *a = *b;
  7. *b = c;
  8. }
  9. int main(void)
  10. {
  11. int a ;
  12. int b ;
  13. int ret;
  14. a =16;
  15. b = 64;
  16. ret = 0;
  17. swap(&a,&b);
  18. ret = a - b;
  19. return ret;
  20. }</span>

2.2编译

#gcc    -g   -o   main   main.c

#objdump   -d  main   >   main.dump

#gcc   -Wall   -S  -o   main.s   main.c

这样大家可以看main.s也可以看main.dump,这里我们选择使用main.dump。

截取关键的部分,即_start,   swap  ,  main,为什么会有_start呢,因为ELF格式的入口其实是_start而不是main()。下面的图展示了main()函数调用swap()前后的栈空间的结构。右边的数字代表相对帧指针的偏移字节数。后面我们使用GDB调试就会发现栈的变化跟下图是一致的。

(!!!请注意,由于栈对齐的缘故,编译器分配栈空间时可能会有没用到的内存地址,而这些没使用到的内存地址就没在下图表示出来,所以下图只能当作示意图来了解函数栈帧结构!!具体的栈内存内容以下文的GDB调试的信息为准!!!)

下面是main.dump中_start的代码注释,比较重要的是对esp的栈对齐操作,esp是16字节对齐的,注意左边行号的右边的0x8048300一类的数字是指令地址。

下面是main.dump中swap()函数和main()函数的汇编代码,代码旁有详细的注释。

下面我们使用GDB调试main.c的代码,使用刚才编译好的main镜像。

# gdb    start      (启动gdb)

# (gdb) file     main      (加载镜像文件)

# (gdb) break  main     (把main()设置为断点,注意gdb并没有把断点设置在main的第一条指令,而是设置在了调整栈指针为局部变量保留空间之后)

# (gdb) run                     (运行程序)

# (gdb) stepi                  (单步执行,不熟悉gdb的童鞋要注意了,stepi命令执行之后显示出来的源代码行或者指令地址,都是即将执行的指令,而不是刚刚执行完的指令!)

c函数调用过程原理及函数栈帧分析的更多相关文章

  1. C函数调用过程原理及函数栈帧分析(转)

    在x86的计算机系统中,内存空间中的栈主要用于保存函数的参数,返回值,返回地址,本地变量等.一切的函数调用都要将不同的数据.地址压入或者弹出栈.因此,为了更好地理解函数的调用,我们需要先来看看栈是怎么 ...

  2. Cortex-M3双堆栈MSP和PSP+函数栈帧

    为了防止几百年以后找不到该文章,特此转载 ------------------------------------------------开始转载--------------------------- ...

  3. 计算机是如何计算的、运行时栈帧分析(神奇i++续)

    关于i++的疑问 通过JVM javap -c 查看字节码执行步骤了解了i++之后,衍生了一个问题: int num1=50; num1++*2执行的是imul(将栈顶两int类型数相乘,结果入栈), ...

  4. 函数调用过程中,函数参数的入栈顺序,why?

    C语言函数参数入栈顺序为从右至左.具体原因为:C方式参数入栈顺序(从右至左)的好处就是可以动态变化参数个数.通过栈堆分析可知,自左向右的入栈方式,最前面的参数被压在栈底.除非知道参数个数,否则是无法通 ...

  5. MIPS架构上函数调用过程的堆栈和栈帧

    转载于CSDN:http://blog.csdn.net/do2jiang/article/details/5404566 在计算机科学中,Call stack是指存放某个程序的正在运行的函数的信息的 ...

  6. 图文并茂-超详解 CS:APP: Lab3-Attack(附带栈帧分析)

    CS:APP:Lab3-ATTACK 0. 环境要求 关于环境已经在lab1里配置过了.lab1的连接如下 实验的下载地址如下 说明文档如下 http://csapp.cs.cmu.edu/3e/at ...

  7. 自己动手实现arm函数栈帧回溯【转】

    转自:http://blog.csdn.net/dragon101788/article/details/18668505 内核版本:2.6.14 glibc版本:2.3.6 CPU平台:arm gl ...

  8. C语言的函数调用过程(栈帧的创建与销毁)

    从汇编的角度解析函数调用过程 看看下面这个简单函数的调用过程: int Add(int x,int y) { ; sum = x + y; return sum; } int main () { ; ...

  9. [Android Pro] 深入理解函数的调用过程——栈帧

    cp :http://blog.csdn.net/x_perseverance/article/details/78897637 每一个函数被调用时,都会为函数开辟一块空间,这块空间就称为栈帧. 首先 ...

随机推荐

  1. C++ 打印输出指针

    大家想必在很多场景下需要打印输出指针地址.看一下下面的输出:       CObject* pObject = new CObject;     std::cout << pObject  ...

  2. redhat6和ubuntu13.10在WMware player 下与Windows共享文件

    Redhat下: 点击VMware的 setting -> vmware tools install mount /dev/cdrom /mnt/cdromcd /mnt/cdrom里面有一个v ...

  3. 自定义View(1)简单流程及示例模板

    1,继承View , ViewGroup,或TextView等等 2,绘制相关的api, canvas 画布, paint 画笔 2,重写重要的函数(注意这个顺序) onMeasure 属于View的 ...

  4. C# App.config 详解

      读语句: String str = ConfigurationManager.AppSettings["DemoKey"]; 写语句: Configuration cfa = ...

  5. Redis安装教程

    1. Linux下Redis安装教程 (1)安装 #tar xf redis-2.6.14.tar.gz #cd redis-2.6.14 #make #make install (2)配置 修改re ...

  6. bzoj2436

    不难发现两边的活动是交替进行的,我们可以dp 先对时间离散化,设f[i,j]到时间i一个会场选j个活动,另一个会场最多有多少活动,那么f[i,j]=max(f[k,j]+s[k,i],f[k,j-s[ ...

  7. iOS开发:本地数据存储-NSUserDefaults

    Getting Default Values arrayForKey(_:) boolForKey(_:) dataForKey(_:) dictionaryForKey(_:) floatForKe ...

  8. Web网站的性能测试工具

    随着Web 2.0技术的迅速发展,许多公司都开发了一些基于Web的网站服务,通常在设计开发Web应用系统的时候很难模拟出大量用户同时访问系统的实际情况,因此,当Web网站遇到访问高峰时,容易发生服务器 ...

  9. web请求报出 “超过了最大请求长度” 【注意:重启IIS】

    摘自:http://www.cnblogs.com/loalongblogs/archive/2012/10/16/2726372.html web请求报出 “超过了最大请求长度”   错误原因:as ...

  10. LeetCode Binary Tree Level Order Traversal II (二叉树颠倒层序)

    题意:从左到右统计将同一层的值放在同一个容器vector中,要求上下颠倒,左右不颠倒. 思路:广搜逐层添加进来,最后再反转. /** * Definition for a binary tree no ...