ARM——栈

1 栈:栈是一种具有后进先出数据组织方式,也就是说后存放的先取出,先存放的后取出。栈底是第一个进栈的数据所处位置,栈顶是最后一个数据进栈所处的位置。

  数据组织:有链表、图、树等等(就数据结构那些东东)

2 满/空栈

  根据SP指针指向的位置,栈可以分为满栈空栈

  满栈:当堆栈指针总是指向最后压入堆栈的数据

  空栈:当堆栈指针总是指向下一个将要放入数据的空位置

  ARM采用满栈

3 升/降栈

  根据SP指针移动的方向,栈可以分为升栈和降栈

  升栈:随着数据的入栈,SP指针从低地址->高地址移动

  降栈:随着数据的入栈,SP指针从高地址->低地址移动

    ARM采用降栈

注:ARM是满降栈

4 栈帧

  上图描述的是ARM的栈帧布局方式,main stack frame为调用函数的栈帧,func1 stack frame为当前函数(被调用者)的栈帧,栈底在高地址,栈向下增长。图中FP就是栈基址,它指向函数的栈帧起始地址;SP则是函数的栈指针,它指向栈顶的位置。ARM压栈的顺序依次为当前函数指针PC、返回指针LR、栈指针SP、栈基址FP、传入参数个数及指针、本地变量和临时变量。如果函数准备调用另一个函数,跳转之前临时变量区先要保存另一个函数的参数。
  ARM也可以用栈基址和栈指针明确标示栈帧的位置,栈指针SP一直移动。

  栈帧(stack frame):就是一个函数所使用的那部分栈,所有函数的栈帧串起来就组成了一个完整的栈。栈帧的两个边界分别由fp(r11)和sp(r13)来限定。

#include <stdio.h>

int main()
{
    ...
    func1();
    ...
}

int func1()
{
    ...
}

  例子中有两个函数,程序运行起来会有一个栈。

  fp(r11)栈帧指针,栈帧上边界由fp指针界定,下边界有sp指针界定。从main函数进入到func1函数,main函数的上边界和下边界保存在被它调用的栈帧里面。

5 栈的作用

5.1 保存局部变量

#include <stdio.h>

int main()
{
    int a;
    a++;
    return a;
}
/******************************************************
反汇编找到main函数
dongry@d-linux:~/test/hardwork/stack$ arm-linux-gcc -g stack1.c -o stack1
dongry@d-linux:~/test/hardwork/stack$ arm-linux-objdump -D -S stack1 >dump
dongry@d-linux:~/test/hardwork/stack$ vim dump

/main
*******************************************************/
/*反汇编代码*/
 000083a0 <main>:
 #include <stdio.h>
 
 int main()
 {
     83a0:       e1a0c00d        mov     ip, sp                    
     83a4:       e92dd800        stmdb   sp!, {fp, ip, lr, pc}
     83a8:       e24cb004        sub     fp, ip, #4      ; 0x4
     83ac:       e24dd004        sub     sp, sp, #4      ; 0x4
         int a;
         a++;
     83b0:       e51b3010        ldr     r3, [fp, #-16]
     83b4:       e2833001        add     r3, r3, #1      ; 0x1
     83b8:       e50b3010        str     r3, [fp, #-16]
         return  a;
     83bc:       e51b3010        ldr     r3, [fp, #-16]
 }
/*分析*/
    mov ip,sp        //保存sp到ip
    stmdb sp!,{fp,ip,lr,pc}   /*先对sp-4,再对fp,ip,lr,pc压栈*/
                             //sp=sp-4;push {pc};sp=pc;  /*先压pc*/
                             //sp=sp-4;push {lr};sp=lr; /*压lr*/
                             //sp=sp-4;push {ip};sp=ip;  /*压ip*/
                             //sp=sp-4;push {fp};sp=fp; /*压fp*/
    sub fp,ip,#4        //fp指向ip-4
    sub sp,sp,#4       //开辟一块空间
  
    ldr r3,[fp,#-16]   //临时存放在[fp-16]
    add r3,r3,#1
    str r3,[fp,#-16]

5.2 参数传递

#include <stdio.h>

void func(int a,int b,int c,int d,int e,int f)
{
    int k;
    int l;
    k=e+f;
    l=a+b;
}

int main()
{
    func(1,2,3,4,5,6);
    return 0;
}

  参数大于4个的时候,多出来的参数用栈传递;

5.3 保存寄存器的值

#include <stdio.h>

void func2(int a,int b)
{
    int k;
    k=a+b;
}

void func1(int a,int b)
{
    int c;
    func2(3,4);
}

int main()
{
    func1(1,2);
    return 0;
}

  如果不用栈,会将原来r0、r1寄存器中的值覆盖掉

 

posted @ 2019-03-07 17:27  dongry  阅读(4248)  评论(0编辑  收藏  举报