Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1509696
  • 博文数量: 228
  • 博客积分: 1698
  • 博客等级: 上尉
  • 技术积分: 3241
  • 用 户 组: 普通用户
  • 注册时间: 2008-12-24 21:49
个人简介

Linux

文章分类

全部博文(228)

文章存档

2017年(1)

2016年(43)

2015年(102)

2014年(44)

2013年(5)

2012年(30)

2011年(3)

分类: 网络与安全

2014-05-21 18:14:01

栈从高地址向低地址延伸,函数的每次调用,都有独立的栈帧,栈帧中维持着所需要的各种信息。
寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部(低地址)。
下图为栈的位置示意图:

入栈操作:push eax;  等价于 esp=esp-4, eax->[esp]; 如下图:

出栈操作:pop eax;  等价于 [esp]->eax,esp=esp+4; 如下图:

我们来看下面这个C程序在执行过程中,栈的变化情况:
void func(int m, int n)

{

    int a, b;

    a = m;

    b = n;

}

int main(void)
{

    ...

    func(m, n);

    L:  下一条语句

    ...
}
在main调用func函数前,栈的情况,也就是说main的栈帧:

从低地址esp到高地址ebp的区域,就是当前main函数的栈帧。当main中调用func时,写成汇编大致是:

    push m

    push n; 两个参数压入栈

    call func; 调用func,将返回地址填入栈,并跳转到func



当跳转到了func,来看看func的汇编大致的样子:
__func:

        push ebp; 新函数要有自己栈帧,必须保存main栈帧底部;栈顶不用保存,因为main栈帧顶部是func栈帧底部(两栈相邻)

        mov ebp, esp; 上一栈帧的顶部,就是这个栈帧的底部

目前的栈的使用情况如下:

新栈帧开始:

      sub esp, 8;  // int a, b 声明了两个int,所以esp减小8个字节来为a,b分配空间

      mov dword ptr [esp+4], [ebp+12];  // a=m

      mov dword ptr [esp], [ebp+8];         // b=n         

  

    ret 8;  返回,8是参数占用字节数,当返回后,esp-8,释放参数m,n的空间

     由此可见,通过ebp,能够很容易定位到上面的参数。当从func函数返回时,首先esp移动到栈帧底部(即释放局部变量),
然后把上一个函数的栈帧底部指针弹出到ebp,再弹出返回地址到cs:ip上,esp继续移动划过参数。这样,ebp,esp就回到了调用函数前的状态,即现在恢复了原来的 main的栈帧。

阅读(7896) | 评论(0) | 转发(5) |
给主人留下些什么吧!~~