34.栈的初始化

34.栈的初始化

前面知道,uboot的第一阶段是用汇编代码来对硬件等进行初始化的。第二阶段是用C语言的。但是此时没有C语言的运行环境。而C语言的运行需要堆栈等资源。所以这一节是第二阶段的C语言环境运行的栈的初始化。

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

2.满栈和空栈

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

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

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

注意:ARM采用满栈的方式

满栈

空栈

3.升栈和降栈

升栈:随着数据的入栈,sp指针从低地址à高地址移动

降栈:随着数据的入栈,sp指针从高地址à低地址移动

注意:ARM采用降栈。

升栈

降栈

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

C代码分析:

局部变量保存在栈里:

Stack1.c:

#include <stdio.h>

int main()

{

int a;

a++;

return a;

}

编译和反汇编:

反汇编文件:

int main()

{

834c:    e52db004     push    {fp}        ; (str fp, [sp, #-4]!)

8350:    e28db000     add    fp, sp, #0    ; 0x0

8354:    e24dd00c     sub    sp, sp, #12    ; 0xc //准备工作,保存现场

int a;

a++;

8358:    e51b3008     ldr    r3, [fp, #-8]

835c:    e2833001     add    r3, r3, #1    ; 0x1 //a++,局部变量

8360:    e50b3008     str    r3, [fp, #-8]

return a;

8364:    e51b3008     ldr    r3, [fp, #-8]

}

8368:    e1a00003     mov    r0, r3

836c:    e28bd000     add    sp, fp, #0    ; 0x0

8370:    e8bd0800     pop    {fp}

8374:    e12fff1e     bx    lr

Stack2.c:

函数参数多于四个的那些参数保存在栈里,4个之内的保存在寄存器r0-r3里。

#include <stdio.h>

void func1(int a,int b,int c,int d,int e,int f)

{

    int k;

    k=e+f;

}

int main()

{

func1(1,2,3,4,5,6);

return 0;

}

编译和反汇编:

0000834c <func1>:

#include <stdio.h>

void func1(int a,int b,int c,int d,int e,int f)

{

834c:    e52db004     push    {fp}        ; (str fp, [sp, #-4]!) //fp入栈

8350:    e28db000     add    fp, sp, #0    ; 0x0 //fp和sp相同的位置

8354:    e24dd01c     sub    sp, sp, #28    ; 0x1c

8358:    e50b0010     str    r0, [fp, #-16]

835c:    e50b1014     str    r1, [fp, #-20]

8360:    e50b2018     str    r2, [fp, #-24]

8364:    e50b301c     str    r3, [fp, #-28] //参数在四个内

    int k;

    k=e+f;

8368:    e59b3004     ldr    r3, [fp, #4]//多于四个的保存在栈里,参数5,6

836c:    e59b2008     ldr    r2, [fp, #8]

8370:    e0833002     add    r3, r3, r2

8374:    e50b3008     str    r3, [fp, #-8]

}

8378:    e28bd000     add    sp, fp, #0    ; 0x0

837c:    e8bd0800     pop    {fp}

8380:    e12fff1e     bx    lr

00008384 <main>:

int main()

{

8384:    e92d4800     push    {fp, lr}

8388:    e28db004     add    fp, sp, #4    ; 0x4

838c:    e24dd008     sub    sp, sp, #8    ; 0x8

func1(1,2,3,4,5,6);

8390:    e3a03005     mov    r3, #5    ; 0x5

8394:    e58d3000     str    r3, [sp]

8398:    e3a03006     mov    r3, #6    ; 0x6

839c:    e58d3004     str    r3, [sp, #4]

83a0:    e3a00001     mov    r0, #1    ; 0x1 //函数的参数在四个之内的采用寄存器r0-r3来传递,大于四个的那些参数,就要使用栈来传递的。

83a4:    e3a01002     mov    r1, #2    ; 0x2

83a8:    e3a02003     mov    r2, #3    ; 0x3

83ac:    e3a03004     mov    r3, #4    ; 0x4

83b0:    ebffffe5     bl    834c <func1>

return 0;

83b4:    e3a03000     mov    r3, #0    ; 0x0

}

83b8:    e1a00003     mov    r0, r3

83bc:    e24bd004     sub    sp, fp, #4    ; 0x4

83c0:    e8bd4800     pop    {fp, lr}

83c4:    e12fff1e     bx    lr

保存寄存器的值:

Statk3.c:

#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);

    c=a+b;

}

int main()

{

func1(1,2);

return 0;

}

编译和反汇编:

0000834c <func2>:

#include <stdio.h>

void func2(int a,int b)

{

834c:    e52db004     push    {fp}        ; (str fp, [sp, #-4]!)

8350:    e28db000     add    fp, sp, #0    ; 0x0

8354:    e24dd014     sub    sp, sp, #20    ; 0x14

8358:    e50b0010     str    r0, [fp, #-16]

835c:    e50b1014     str    r1, [fp, #-20]

int k;

k=a+b;

8360:    e51b3010     ldr    r3, [fp, #-16]

8364:    e51b2014     ldr    r2, [fp, #-20]

8368:    e0833002     add    r3, r3, r2

836c:    e50b3008     str    r3, [fp, #-8]

}

8370:    e28bd000     add    sp, fp, #0    ; 0x0

8374:    e8bd0800     pop    {fp}

8378:    e12fff1e     bx    lr

0000837c <func1>:

void func1(int a,int b)

{

837c:    e92d4800     push    {fp, lr}

8380:    e28db004     add    fp, sp, #4    ; 0x4

8384:    e24dd010     sub    sp, sp, #16    ; 0x10

8388:    e50b0010     str    r0, [fp, #-16]

838c:    e50b1014     str    r1, [fp, #-20] //将寄存器里的值存入栈

    int c;

    func2(3,4); //之所以要把寄存器里的值保存入栈,是因为这里的func2(3,4)函数也有两个参数,不保存的话会被覆盖掉。后面再进行相加就会出错。因为系统还是会用寄存器r0和r1来存func2函数的参数。

8390:    e3a00003     mov    r0, #3    ; 0x3

8394:    e3a01004     mov    r1, #4    ; 0x4

8398:    ebffffeb     bl    834c <func2>

    c=a+b;

839c:    e51b3010     ldr    r3, [fp, #-16]

83a0:    e51b2014     ldr    r2, [fp, #-20]//从栈里面取出值放入寄存器

83a4:    e0833002     add    r3, r3, r2

83a8:    e50b3008     str    r3, [fp, #-8]

}

83ac:    e24bd004     sub    sp, fp, #4    ; 0x4

83b0:    e8bd4800     pop    {fp, lr}

83b4:    e12fff1e     bx    lr

000083b8 <main>:

int main()

{

83b8:    e92d4800     push    {fp, lr}

83bc:    e28db004     add    fp, sp, #4    ; 0x4

func1(1,2);

83c0:    e3a00001     mov    r0, #1    ; 0x1

83c4:    e3a01002     mov    r1, #2    ; 0x2 //用寄存器来保存参数

83c8:    ebffffeb     bl    837c <func1>

return 0;

83cc:    e3a03000     mov    r3, #0    ; 0x0

}

83d0:    e1a00003     mov    r0, r3

83d4:    e24bd004     sub    sp, fp, #4    ; 0x4

83d8:    e8bd4800     pop    {fp, lr}

83dc:    e12fff1e     bx    lr

接下来就是完成堆栈的初始化:

知道6410的内存是256MB,210是512MB,2440是64MB。这里我选的是6410的。Sp指针指向64MB的位置。

2440内存开始的地方:30000000

6410内存开始的地方:50000000

210内存开始的地方:20000000

所以要sp指针指向64MB的地址就是500000000(6410)+64MB即可:

64MB=0x4000000,所以sp指向的地方是0x54000000即可:

原文地址:https://www.cnblogs.com/FORFISH/p/5188758.html