栈的初始化及栈帧概念解析

1、栈:FILO先进后出的数据结构
栈底是第一个进栈的数据的位置(压箱  底) 
栈顶是最后一个进栈的数据位置

2、根据SP指针指向的位置,栈可分为  满栈和空栈 
满栈:当sp指针总是指向最后压入堆栈  的数据(ARM采用满栈)
 栈的初始化及栈帧概念解析_第1张图片
空栈:当堆栈指针SP总是指向下一个将  要放入数据的空位置。
 栈的初始化及栈帧概念解析_第2张图片

3、根据SP指针移动的方向,可分为升  栈和降栈 
升栈:随数据的入栈,SP由低地址-->  高地址 
栈的初始化及栈帧概念解析_第3张图片
降栈:随数据的入栈,SP由高地址-->  低地址(ARM采用降栈)
栈的初始化及栈帧概念解析_第4张图片 

4、栈帧:存储在用户栈上的(当然内核栈同样适用)每一次函数调用涉及的相关信息的记录单元 ;  栈帧(stack frame)就是一个函数所 使用的那部分栈,所有函数的栈帧串起 来就组成了一个完整的栈。
栈帧的两个 边界分别有FP(R11)和SP(R13)L来 限定。 
                栈帧
栈的初始化及栈帧概念解析_第5张图片
栈的作用:
1)保存局部变量
分析代码:
[html]  view plain  copy
  1. #include <stdio.h>  
  2. int main()  
  3. {      
  4.     int a;  
  5.     a++;  
  6.     return a;  
  7. }span>  

反汇编之后的代码;
[html]  view plain  copy
  1. stack: file format elf32-littlearm  
  2. Disassembly of section .text:  
  3.   
  4. 00000000 <main>:  
  5. #include <stdio.h>  
  6.   
  7. int main()  
  8. {  
  9.    0:   e52db004 push   {fp}     ; (str fp, [sp, #-4]!) @将栈帧底部指针FP压入栈中;创建属于main函数的栈帧。  
  10.    4:   e28db000 add    fp, sp, #0  ; 0x0 @fp指针为函数栈帧的底部,  
  11.    8:   e24dd00c sub    sp, sp, #12 ; 0xc   @sp指针为栈帧的顶部,同时为栈的栈顶。  
  12.     int a;  
  13.   
  14.     a++;  
  15.    c:   e51b3008 ldr    r3, [fp, #-8]   @由此三句可知变量a在栈帧中执行了加法操作,及栈帧具有保存局部变量的作用  
  16.   10:   e2833001 add    r3, r3, #1  ; 0x1  
  17.   14:   e50b3008 str    r3, [fp, #-8]  
  18.   
  19.     return a;  
  20.   18:   e51b3008 ldr    r3, [fp, #-8]  
  21. }  
  22. span>  



2)保存函数的参数 
分析代码:
[html]  view plain  copy
  1. <span style="font-size:18px;">#include <stdio.h>  
  2. void func1(int a,int b,int c,int d,int e,int f)  
  3. {  
  4.  int k;  
  5.  k=e+f;  
  6. }  
  7.   
  8. int main()  
  9. {  
  10.     func1(1,2,3,4,5,6);  
  11.     return 0;  
  12. }  
  13. 反汇编之后的代码;  
  14. void func1(int a,int b,int c,int d,int e,int f) @多于4个参数  
  15. {  
  16.    0:   e52db004 push   {fp}     ; (str fp, [sp, #-4]!)@保存main函数的栈帧底部指针FP  
  17.    4:   e28db000 add    fp, sp, #0  ; 0x0  
  18.    8:   e24dd01c sub    sp, sp, #28 ; 0x1c @由栈帧顶部指针SP创建一片栈帧保存子函数的前四个参数  
  19.    c:   e50b0010 str    r0, [fp, #-16]  @ a  
  20.   10:   e50b1014 str    r1, [fp, #-20]  @ b  
  21.   14:   e50b2018 str    r2, [fp, #-24]  @ c  
  22.   18:   e50b301c str    r3, [fp, #-28]  @ d  
  23.  int k;  
  24.  k=e+f;  
  25.   1c:   e59b3004 ldr    r3, [fp, #4]    @在子函数的栈帧中实现第五个参数与第六个参数的运算  
  26.   20:   e59b2008 ldr    r2, [fp, #8] @由ldr  r2, [fp, #8]知参数保存在main函数的栈帧中,并运算  
  27.   24:   e0833002 add    r3, r3, r2   @以子函数的栈帧底部指针(fp)做参考坐标实现对参数的查找  
  28.   28:   e50b3008 str    r3, [fp, #-8]  
  29. }  
  30.   2c:   e28bd000 add    sp, fp, #0  ; 0x0  
  31.   30:   e8bd0800 pop    {fp}  
  32.   34:   e12fff1e bx lr  
  33.   
  34. 00000038 <main>:  
  35.   
  36. int main()  
  37. {  
  38.   38:   e92d4800 push   {fp, lr}    @由于调用子函数,先保存main函数的栈帧底部指针FP和返回地址LR(当前PC指针的下一地址)  
  39.   3c:   e28db004 add    fp, sp, #4  ; 0x4 @可知先压入FP,后压入lr.把此时子函数(被调用者)的栈帧底部指针FP指向保存在子函数栈帧的main函数(调用者)的栈帧底部指针FP  
  40.   40:   e24dd008 sub    sp, sp, #8  ; 0x8   @创建栈  
  41.     func1(1,2,3,4,5,6);  
  42.   44:   e3a03005 mov    r3, #5  ; 0x5  
  43.   48:   e58d3000 str    r3, [sp]  
  44.   4c:   e3a03006 mov    r3, #6  ; 0x6  
  45.   50:   e58d3004 str    r3, [sp, #4]  
  46.   54:   e3a00001 mov    r0, #1  ; 0x1 @用通用寄存器保存前四个参数的值  
  47.   58:   e3a01002 mov    r1, #2  ; 0x2  
  48.   5c:   e3a02003 mov    r2, #3  ; 0x3  
  49.   60:   e3a03004 mov    r3, #4  ; 0x4  
  50.   64:   ebfffffe bl 0 <func1>  
  51.     return 0;  
  52.   68:   e3a03000 mov    r3, #0  ; 0x0  
  53. }  
  54.   6c:   e1a00003 mov    r0, r3  
  55.   70:   e24bd004 sub    sp, fp, #4  ; 0x4  
  56.   74:   e8bd4800 pop    {fp, lr}  
  57.   78:   e12fff1e bx lrspan>  


注:C中,若函数的参数小于等于4个, 则用通用寄存器保存其参数值,多于4 个的参数保存在栈中

3)保存寄存器的值
分析代码:
[html]  view plain  copy
  1. <span style="font-size:18px;">include <stdio.h>  
  2.   
  3. void func2(int a,int b)  
  4. {  
  5.     int k;  
  6.     k=a+b;  
  7. }  
  8.   
  9. void func1(int a,int b)  
  10. {  
  11.  int c;  
  12.  func2(3,4);  
  13.  c=a+b;  
  14. }  
  15.   
  16. int main()  
  17. {  
  18.     func1(1,2);  
  19.     return 0;  
  20. }span>  

反汇编之后的代码;
[html]  view plain  copy
  1. <span style="font-size:18px;">void func2(int a,int b)  
  2. {  
  3.    0:   e52db004 push   {fp}     ; (str fp, [sp, #-4]!)  
  4.    4:   e28db000 add    fp, sp, #0  ; 0x0  
  5.    8:   e24dd014 sub    sp, sp, #20 ; 0x14  
  6.    c:   e50b0010 str    r0, [fp, #-16] @保存寄存器的值  
  7.   10:   e50b1014 str    r1, [fp, #-20]  
  8.     int k;  
  9.     k=a+b;  
  10.   14:   e51b3010 ldr    r3, [fp, #-16]  
  11.   18:   e51b2014 ldr    r2, [fp, #-20]  
  12.   1c:   e0833002 add    r3, r3, r2  
  13.   20:   e50b3008 str    r3, [fp, #-8]  
  14. }  
  15.   24:   e28bd000 add    sp, fp, #0  ; 0x0  
  16.   28:   e8bd0800 pop    {fp}  
  17.   2c:   e12fff1e bx lr  
  18.   
  19. 00000030 <func1>:  
  20.   
  21. void func1(int a,int b)  
  22. {  
  23.   30:   e92d4800 push   {fp, lr}  
  24.   34:   e28db004 add    fp, sp, #4  ; 0x4  
  25.   38:   e24dd010 sub    sp, sp, #16 ; 0x10  
  26.   3c:   e50b0010 str    r0, [fp, #-16] @代码44行调用func2函数后,又使用r0\r1保存参数,所以此时将r0\r1寄存器的  
  27.   40:   e50b1014 str    r1, [fp, #-20]  @值放入栈中  
  28.  int c;  
  29.  func2(3,4);  
  30.   44:   e3a00003 mov    r0, #3  ; 0x3  
  31.   48:   e3a01004 mov    r1, #4  ; 0x4  
  32.   4c:   ebfffffe bl 0 <func2>  
  33.  c=a+b;  
  34.   50:   e51b3010 ldr    r3, [fp, #-16]  
  35.   54:   e51b2014 ldr    r2, [fp, #-20]  
  36.   58:   e0833002 add    r3, r3, r2  
  37.   5c:   e50b3008 str    r3, [fp, #-8]  
  38. }  
  39.   60:   e24bd004 sub    sp, fp, #4  ; 0x4  
  40.   64:   e8bd4800 pop    {fp, lr}  
  41.   68:   e12fff1e bx lr  
  42.   
  43. 0000006c <main>:  
  44.   
  45. int main()  
  46. {  
  47.   6c:   e92d4800 push   {fp, lr}  
  48.   70:   e28db004 add    fp, sp, #4  ; 0x4  
  49.     func1(1,2);  
  50.   74:   e3a00001 mov    r0, #1  ; 0x1  
  51.   78:   e3a01002 mov    r1, #2  ; 0x2  
  52.   7c:   ebfffffe bl 30 <func1>  
  53.     return 0;  
  54.   80:   e3a03000 mov    r3, #0  ; 0x0  
  55. }  
  56.   84:   e1a00003 mov    r0, r3  
  57.   88:   e24bd004 sub    sp, fp, #4  ; 0x4  
  58.   8c:   e8bd4800 pop    {fp, lr}  
  59.   90:   e12fff1e bx lrspan>  




初始化栈 :即对SP指针赋予一个内存地址(统一标准:2440、6410、210)
在内存的64MB位置即ldr sp, =0x34000000(2440)
ldr sp, =0x54000000(6410)
ldr sp, =0x24000000(210)
由上可知ARM采用满栈(指向刚入栈的数据)、降栈(由高地址向低地址入栈)

问题:因为ARM不同工作模式有不同的栈,定义栈的技巧是什么,避免定义相同的地址使用不同栈?


转自:http://blog.csdn.net/u011467781/article/details/39559737


你可能感兴趣的:(Bare,code)