zoukankan      html  css  js  c++  java
  • 栈操作与栈帧 (转)

    结构化程序的一个最基本的单元就是“函数”或者叫“过程”。在汇编这一层自然也相应的有支持这些概念的指令操作,如栈操作和栈帧的概念。

    首先这里要为“打开汇编之门”那篇blog补充一点的是:汇编语言是与机器相关,这里的一切都是基于IA-32机器平台的。

    1、寻址方式
    我们已经知道在操作数表示中有一种是用来指示内存地址的内容的,在GNU Assembly中指示内存地址有多种方式,这些方式被统称“寻址方式”。通用的寻址格式为:“Imm(Eb, Ei, s)”[1]。解释一下:该表达式的计算方式为Imm + R[Eb] + R[Ei] * s,这一串的结果是什么呢?是一个存储器的地址,操作指令通过该操作数表达式计算出来的内存地址来访问内存。

    由通用形式演化几种常见特殊形式如下:
    1) Imm - 注意与$Imm区别,后者为立即数,而前者是以立即数形式承载的一个内存地址,这种方式叫绝对寻址;
    2) (Ex) - 注意与Ex区别,后者为寄存器内容,而前者是以寄存器内容形式承载的一个内存地址,这种方式叫间接寻址;
    3) Imm(Eb) - 其表示结果是内存地址为Imm + R[Eb];
    4) (Eb, Ei) - 其表示结果是内存地址为R[Eb] + R[Ei];
    5) Imm((Eb, Ei) - 其表示结果是内存地址为Imm + R[Eb] + R[Ei]。

    2、寄存器使用
    在“打开汇编之门”中曾经提过虽然寄存器的专用性已经降低,但是某些寄存器还是有其专用场合的。GNU为我们制定了一个寄存器使用规则,规则规定:“%eax、%ecx和%edx是由调用者负责存储的,而%ebx、%ebi和%esi则由被调用者保护,而%esp和%ebp都是栈操作专用的”。

    3、栈操作
    栈,实际上是一块儿专用的内存区域,每个进程地址空间都有其专有的栈区。地球人都知道关于栈有两种操作:Push和Pop。相应的GNU Assembly分别定义了“pushl S”和“popl D”分别来完成压栈和出栈操作。每个操作都包含两个步骤:移动栈顶指针和数据传送。
    pushl S <=> R[%esp] <-- R[%esp] - 4 ;M[R[%esp]]<-- S
    popl D <=>  D <-- M[R[%esp]];R[%esp] <-- R[%esp] + 4

    4、栈帧的形成
    提到函数或者过程调用就不能离开栈操作。而每个函数或者过程调用也都离不开一个叫“栈帧”的概念。栈是用来传递参数、保存返回结果等作用的,而栈帧则是1对1映射到某个过程调用的。栈帧由%ebp来标识。我们来看看一个例子,通过该例子看看栈帧里到底有些什么东西?
    void callee(int x, int y) {
     x = 1;
     y = 2;
    }

    void caller(int m, int n) {
     callee(m, n);
    }

    翻译为汇编代码为:
    _callee:
     pushl %ebp   //保存调用者的栈帧地址
     movl %esp, %ebp  //初始化callee栈帧地址
     movl $1, 8(%ebp)  //获取参数x信息
     movl $2, 12(%ebp)  //获取参数y信息
     popl %ebp
     ret
    ... ...
    ... ...
    _caller:
     pushl %ebp   //保存调用者的栈帧地址
     movl %esp, %ebp  //初始化caller栈帧地址
     subl $8, %esp  
     movl 12(%ebp), %eax  
     movl %eax, 4(%esp)
     movl 8(%ebp), %eax
     movl %eax, (%esp)
     call _callee
     leave
     ret
    看看callee的汇编码:进入callee后首先保存其调用者caller的栈帧地址,然后读取其调用者caller栈帧中的参数信息进行计算。可以看出一个过程的栈帧中起码包括其上一个栈帧的起始地址,然后是一些参数信息,按照CS.APP说法,栈帧在存储参数信息之前还有可能保存一些本地变量或临时变量等。在每个过程的栈帧的结尾处都记录着过程返回地址,这个返回地址是由call执行时自动加入的。callee都是通过%ebp +/- 偏移量来获取参数信息的。用下面的图可以小结一下栈帧的模样(起始:%ebp所指的字节--> 终止:返回地址所在字节):

    +              +
     |               |
    +----------+
    | old %ebp | <--- %ebp
    +----------+
    | 本地变量 |
    +----------+
    |   参数n  |
    +----------+
    |   参数...|
    +----------+
    |   参数1  |
    +----------+
    | 返回地址 | 
    +----------+
    |     ...        |
    |                |<-- %esp


    [注1]
    这里采用了CS.APP中的表示方法,Eb表示基址寄存器,Ei表示变址寄存器,s为伸缩因子。我们使用R来表示引用某个寄存器的值,使用M来表示引用某内存地址。

    http://englishman2008.blog.163.com/blog/static/28012907200810910551135/

  • 相关阅读:
    Tornado session 插件 pycket 定制时间和时间续租
    为Tornado框架加上基于Redis或Memcached的session 【第三方】
    正则表达式大全 --【Python举例】
    Django 最好的缓存memcached的使用 小记
    Django 1.9 admin 使用suit 小记
    IntelliJ IDEA 注册码
    Python 爬虫抓取代理IP,并检测联通性
    MySQL自定义函数
    css补充知识
    sqlalchemy 知识补充
  • 原文地址:https://www.cnblogs.com/softidea/p/5289656.html
Copyright © 2011-2022 走看看