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即可: