Chinaunix首页 | 论坛 | 博客
  • 博客访问: 311330
  • 博文数量: 22
  • 博客积分: 186
  • 博客等级: 入伍新兵
  • 技术积分: 1091
  • 用 户 组: 普通用户
  • 注册时间: 2012-01-12 19:54
文章存档

2022年(1)

2020年(1)

2018年(1)

2013年(10)

2012年(9)

分类: LINUX

2012-08-27 18:31:08


    谈x86的栈帧之前,补充一下堆和栈的认识。
1.  堆和栈的关系
    我们平时说的堆栈其实是指栈,而实际上堆和栈是两种不同的内存分配。简单罗列一下各方面的异同点。
    1).堆需要用户在程序中显式申请,栈不用,由系统自动完成。申请/释放堆内存的API,在C中是malloc/free,在C++中是new/delete。申请与释放一定要配对使用,否则会造成内存泄漏(memory leak),久而久之系统就无内存可用了,出现OOM(Out Of Memory)错误。一般在return/exit或break/continue等语句时容易忘记释放内存,所以检查内存泄漏的代码时要关注这些语句,看它们前面是否有必要的释放语句free/delete。
    2).堆的空间比较大,栈比较小。所以申请大的内存一般在堆中申请;栈上不要有较大的内存使用,比如大的静态数组;而且除非算法必要,否则一般不要使用较深的迭代函数调用,那样栈消耗内存会随着迭代次数的增加飞涨。
    3).关于生命周期。栈较短,随着函数退出或返回,本函数的栈就完成了使用;堆就要看什么时候释放,生命周期就什么时候结束。
    说了这么多,我们发现解析Coredump还是跟栈的关系相对紧密,跟堆的关系是有一种产生Coredump的原因是访问堆内存出错。

2.  x86的栈帧
    继续谈栈帧布局,这次说说x86的栈帧布局和操作方法,见栈帧布局图:
    
    上图描述的是一般x86的stack frame栈帧布局方式,当前帧为当前函数(被调用者)的stack frame,调用者的帧为调用函数(调用者)的stack frame。栈底在高地址,栈向下增长。图中,ebp(base pointer)就是栈基址,它指向当前函数的栈帧起始地址;esp(stack pointer)则是当前函数的栈指针,它指向栈顶的位置。压栈的顺序依次为栈基址ebp、其它寄存器、本地变量和临时变量。注意所传递的参数和返回地址lr一般放在调用者的栈帧中,当然按照实际的压栈过程,也有人认为previous ebp也在调用者栈帧内,这个暂时影响不大。
    相比于MIPS,x86比较可爱的地方是刚刚提到的两点,就是可以用栈基址和栈指针明确标示栈帧的位置,栈指针esp一直移动,同时压栈的顺序有一定的规律,一个栈空间内的地址前面,必然有一个代码地址(lr)明确标示着调用函数位置内的某个地址

3.  x86的栈操作
    x86有一对表示栈底和栈顶的寄存器,寄存器ebp是栈基址指针,指栈帧的底部(高地址),寄存器esp是栈指针,指栈帧的顶部(地址地)。
    函数的返回结果是通过寄存器eax传递的,因此在函数退出前会将计算结果拷贝到eax中,然后再出栈返回调用者。
    再来关注几个常用的函数内外跳转指令:
    call: 调用一个函数。以寄存器和偏移量来调用函数和c++里的虚函数调用很类似
    ret:  从一个函数返回。_stdcall调用规范要求如果有返回值,就要将返回值从堆栈的栈顶弹出
    leave:是move ESP EBP/pop EBP的简写,用来退出函数,同时释放了为局部变量分配的空间
    jmp:  无条件跳转
    je:   如果相等则跳转
    jne:  如果不等则跳转
    顺便提一下数据的传递操作mov和movl,mov是将右操作数复制到左操作数,而movl传递方向相反,是将左操作数复制到右操作数。装入有效地址指令(即用来得到局部变量和函数参数的指针)lea和leal也一样。
    最后,x86的压栈出栈指令众人皆知,分别是push和pop,或者其各种变体,比如puchad/popad是对所有通用寄存器的栈操作。 

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

r_luo2013-05-06 23:28:14

很抱歉,这么迟答复。
调用子程序时CPU自动将返回地址ra压入栈中,然后进入子程序,执行到最后,将返回值赋给eax寄存器,再将ebp出栈,然后调用ret指令返回到父函数的ra地址处(即将保存在栈中的ra出栈),继续执行。你的问题我认为是通过栈中的ra来实现。
示例:
main函数调用get_bp函数的部分:
main:
...
0x080483fe <main+60>: call 0x8048384 <get_bp>
0x08048403 <main+65>: mov (%eax), %edx            //0x08048403就是ra!
...

get_bp函数的返回部分:
get_bp:
...
0x0804

vampirezh2013-04-19 23:41:38

高手您对您提供的信息确定吗   我是从百度文库里 搜到这篇之一的文章  想看 X86的就用搜索搜关键字 收到您的博客里来了 但是   您对于X86构架栈操作说的还差一些关键点 就是   当被调用者返回结果以后 调用者如何接续 您是说是通过eax 但在这之前怎么跳回到原调用者跳出的位置的配合返回值继续下文指令呢。这里面又有什么新的寄存器参与吗