嵌入式系统中栈的变化

描述

  函数调用是通过栈来实现的,而且知道在栈中存放着该函数的局部变量。但是,对于栈的实现细节可能不一定清楚。本文将介绍一下在Linux平台下函数栈是如何实现的。

栈帧的结构

函数在调用的时候都是在栈空间上开辟一段空间以供函数使用,栈是由高地址向地地址的方向生长的,而且栈有其栈顶和栈底,入栈出栈的地方就叫做栈顶。

在x86系统的CPU中,rsp是栈指针寄存器,这个寄存器中存储着栈顶的地址。rbp中存储着栈底的地址。函数栈空间主要是由这两个寄存器来确定的。

当程序运行时,栈指针rsp可以移动,栈指针和帧指针rbp一次只能存储一个地址,所以,任何时候,这一对指针指向的是同一个函数的栈帧结构。

而帧指针rbp是不移动的,访问栈中的元素可以用-4(%rbp)或者8(%rbp)访问%rbp指针下面或者上面的元素。

函数

测试代码如下:

 

#include 

int sum (int a,int b)
{
 int c = a + b;
 return c;
}

int main()
{
 int x = 5,y = 10,z = 0;
 z = sum(x,y);
 printf("%drn",z);
 return 0;
}
0000000000000000 :
   0: 55                    push   %rbp 
   1: 48 89 e5              mov    %rsp,%rbp
   4: 89 7d ec              mov    %edi,-0x14(%rbp) # 参数传递
   7: 89 75 e8              mov    %esi,-0x18(%rbp) # 参数传递
   a: 8b 55 ec              mov    -0x14(%rbp),%edx
   d: 8b 45 e8              mov    -0x18(%rbp),%eax
  10: 01 d0                 add    %edx,%eax 
  12: 89 45 fc              mov    %eax,-0x4(%rbp) # 局部变量
  15: 8b 45 fc              mov    -0x4(%rbp),%eax # 存储结果
  18: 5d                    pop    %rbp
  19: c3                    retq   

000000000000001a :
  1a: 55                    push   %rbp # 保存%rbp。rbp,栈底的地址
  1b: 48 89 e5              mov    %rsp,%rbp # 设置新的栈指针。rsp 栈指针,指向栈顶的地址
  1e: 48 83 ec 10           sub    $0x10,%rsp # 分配 16字节栈空间。%rsp = %rsp-16
  22: c7 45 f4 05 00 00 00  movl   $0x5,-0xc(%rbp) # 赋值
  29: c7 45 f8 0a 00 00 00  movl   $0xa,-0x8(%rbp) # 赋值
  30: c7 45 fc 00 00 00 00  movl   $0x0,-0x4(%rbp) # 赋值
  37: 8b 55 f8              mov    -0x8(%rbp),%edx  
  3a: 8b 45 f4              mov    -0xc(%rbp),%eax 
  3d: 89 d6                 mov    %edx,%esi # 参数传递 ,从右向左
  3f: 89 c7                 mov    %eax,%edi # 参数传递
  41: e8 00 00 00 00        callq  46  # 调用sum
  46: 89 45 fc              mov    %eax,-0x4(%rbp) 
  49: 8b 45 fc              mov    -0x4(%rbp),%eax # 存储计算结果
  4c: 89 c6                 mov    %eax,%esi
  4e: 48 8d 3d 00 00 00 00  lea    0x0(%rip),%rdi        # 55 
  55: b8 00 00 00 00        mov    $0x0,%eax
  5a: e8 00 00 00 00        callq  5f 
  5f: b8 00 00 00 00        mov    $0x0,%eax 
  64: c9                    leaveq 
  65: c3                    retq   
+0x45>+0x3b>+0x2c>

 

在函数被调用之前,调用者会为调用函数做准备。首先,函数栈上开辟了16字节的空间,存储定义的3个int型变量,建立了main函数的栈。

函数

CALL指令内部其实还暗含了一个将返回地址(即CALL指令下一条指令的地址)压栈的动作(由硬件完成)。

具体来说,call指令执行时,先把下一条指令的地址入栈,再跳转到对应函数执行的起始处。

  审核编辑:汤梓红

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分