过程调用与栈帧

晨曦之光 发布于 2012/03/09 14:15
阅读 227
收藏 0

本博客(http://blog.csdn.net/livelylittlefish)贴出作者(三二一@小鱼)相关研究、学习内容所做的笔记,欢迎广大朋友指正!

     

过程调用和栈帧

 

1. 过程调用

一个过程调用包括将数据和控制从代码的一部分传递到另一部分。

过程调用的任务:为过程的局部变量分配空间,并在退出时释放这些空间,俗称保存现场/恢复现场。

 

2. 栈帧

栈的作用:参数传递、局部变量分配、保存调用的返回地址、保存寄存器以供恢复

 

IA32程序用程序栈来支持过程调用。

栈帧:为单个过程分配的那部分栈称为栈帧(stack frame

下图表述了一个通用的栈帧: 

 

 high address |=================|<----+

              |                 |     |

              |                 |     |

              |                 |     |

              |                 |  previous stack frame

              |                 |     |

              |                 |     |

              |                 |     |

              |=================|<----+

              |    ...          |     |

              |                 |     |

              |-----------------|     |

        4+4n  |    param n      |     |

              |-----------------|     |

              |    ...          |  caller stack frame

              |                 |     |

              |-----------------|     |

          +8  |    param 1      |     |

              |-----------------|     |

          +4  |  return addr    |     |

              |=================|<----+

      %ebp->  |     %ebp'       |     |  //this saved %ebp' for previous stack frame

              |-----------------|     |

              |                 |     |

              | saved register  |     |

              | local variable  |     |

              | temp variable   |     |

              |-----------------|     |

              |                 |  callee stack frame

              | param section   |     |

              |                 |     |

              |-----------------|     |

              |                 |     |

              |-----------------|     |

      %esp->  |                 |     |

  low address |-----------------|<----+

 

 

帧指针:%ebp,指向栈底,在本过程中,%ebp值保持不变

栈指针:%esp,指向栈顶,程序执行时,栈指针可以移动,来分配或释放空间

  • %esp减小——分配空间
  • %esp增大——释放空间

 

%ebp不变,而%esp可变,故程序执行中,大多数信息的访问都是相对于帧指针的,例如如下指令:

lea 0xffffffe8(%ebp), %eax ;%ebp-24地址中的数据(实际上是地址)存入%eax寄存器

(回忆一下《汇编语言》,lea是什么?。。。。leaLoad Effective Address

(为什么是%ebp-24?。。。。因为计算机中数据是用反码表示的,反码0xffffffe8表示的值即为-24,即-0x18

 

考虑这样一个调用,调用者caller,被调用者callee

caller调用callee时,caller的返回地址被压入栈中,形成caller栈帧的末尾。

返回地址:就是当程序从callee返回时应该继续执行的地方(指令的地址),当被调用过程返回时,程序会从此处继续执行。

callee的栈帧从保存的帧指针的值(%ebp)开始,其后即为其保存的寄存器的值,和callee调用其他过程的参数。

 

思考:为什么(非寄存器)局部变量存放在栈中而非寄存器中?

  • 寄存器不够存放所有的局部变量
  • 有些局部变量是数组或结构,必须通过数组或结构引用来访问
  • 若要对一个局部变量使用地址操作符“&”,必须能为它产生一个地址

 

一般地,C/C++程序的调用方式为__cdecl,即参数从右向左的顺序入栈;如此,则%ebp+0x8为第一个参数,%ebp+0xc为第二个参数,以此类推。(默认地,编译器会考虑内存按双字对齐)

 

3. 各类型的数据在栈中的排列方式

栈的增长方式:由高地址向低地址方向增长

 

数据存放方式:

例如内存中存放的long型数据为0x12345678

 

a. 高端法(Gig Endian)

 

高字节(高位) => 低地址

低字节(低位) => 高地址

 

       |----------|

0x2003 |    78    | high address

       |----------|

0x2002 |    56    |

       |----------|

0x2001 |    34    |

       |----------|

0x2000 |    12    | low address

       |----------|

 

 

数组存放方式(大端法):

例如int a[8];

 

             |----------|

a[7], 0x2017 |    80    | high address

             |----------|

a[6], 0x2016 |    70    |

             |----------|

a[5], 0x2015 |    60    |

             |----------|

a[4], 0x2014 |    50    |

             |----------|

a[3], 0x2013 |    40    |

             |----------|

a[2], 0x2012 |    30    |

             |----------|

a[1], 0x2011 |    20    |

             |----------|

a[0], 0x2010 |    10    | low address

             |----------|

 

b. 小端法(Little Endian)

 

高字节(高位) => 高地址

低字节(低位) => 低地址

 

       |----------|

0x2003 |    12    | high address

       |----------|

0x2002 |    34    |

       |----------|

0x2001 |    56    |

       |----------|

0x2000 |    78    | low address

       |----------|

 

 

4. 过程调用和返回指令

  • call 过程调用,有一个目标地址(被调用过程起始点指令地址)

任务:将返回地址入栈,并跳转到被调用过程的起始处

  • leave:为返回准备栈

任务:

movl %ebp, %esp ;set stack pointer to the beginning of frame

popl %ebp ; restore saved %ebp and set stack ptr to the end of caller's frame

  • ret 从过程调用中返回

任务:从栈中弹出返回地址,并跳转到那个位置

 

%eax通常用来返回值,如果函数要返回整数或指针。 

 

Reference

关于程序调用方式:

http://yangwei.blogbus.com/logs/1550582.html

http://blog.csdn.net/cradmin/archive/2008/10/22/3124159.aspx

 

栈帧结构:

《深入理解计算机系统》

http://bigwhite.blogbus.com/logs/2005/11/1592114.html

http://bbs.tech.ccidnet.com/read.php?tid=645517


原文链接:http://blog.csdn.net/livelylittlefish/article/details/5051545
加载中
返回顶部
顶部