bootloader的第二部分代码主要是采用C语言来实现的,利用C语言实现串口、网卡等功能,并成功启动操作系统。
一、栈的初始化
1、概念解析
栈:先进后出性质的数据组织方式
栈底:第一个进栈的数据所处位置
栈顶:最后一个进栈的数据所处位置
满栈:当堆栈指针SP总是指向最后压入堆栈的数据
空栈:当堆栈指针SP指向下一个将要放入数据的空位置
升栈:随着数据的入栈,SP指针从 低地址–》高低址
降栈:随着数据的入栈,SP指针从 高地址–》低低址
栈帧:就是一个函数所使用的那部分栈(R11寄存器,也就是栈帧寄存器FP)
所有函数的栈帧串起来组成就是一个完整的栈,在堆栈指针SP(下边界)与栈帧指针FP(上边界)之间就是栈区。
ARM采用 满降栈!!
2、手把手带你分析
栈的作用:
2、1 保存局部变量保存
//stack1.c
#include <stdio.h>
int main()
{
int a;
a++;
return a;
}
//在sheel里面运行实例程序stack1.c
arm-linux-gcc -g stack1.c -o stack1
arm-linux-objdump -D -S stack1 >dump1 //反汇编
vim dump1 //进入文件后,在:中搜索关键字,例如: :/main
进入dump后可以结合汇编代码,可知a是存储在栈帧指针fp和堆栈指针sp之间的。如图:
2、2 传递参数
当函数的参数大于 4个的时候,将使用栈来传递参数。
//stack2.c
#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;
}
//在sheel里面运行实例程序stack2.c
arm-linux-gcc -g stack2.c -o stack2
arm-linux-objdump -D -S stack2 >dump2 //反汇编
vim dump2
3.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);
c=a+b;
}
int main()
{
func1(1,2);
return 0;
}
//在sheel里面运行实例程序stack3.c
arm-linux-gcc -g stack3.c -o stack3
arm-linux-objdump -D -S stack3 >dump3 //反汇编
vim dump3
3、初始化堆栈
不管是2440还是6410还是210,将sp指针指向64M内存(保证够用)的地方,在使用的过程中,sp向下移(因为ARM中采用的降栈)。
2440:0x30000000+64M = 0x34000000
6410:0x50000000+64M
210 :0x20000000+64M
//2440的堆栈初始化
init_stack:
ldr sp, =0x34000000
mov pc ,lr
二、bss段初始化
未初始化的全局变量
分析验证代码如下:
//bss.c
#include <stdio.h>
int year;
int main()
{
year = 2017;
return year;
}
//在sheel里面输入
arm-linux-gcc -g bss.c -o bss
arm-linux-readelf -a bss >dump
vi dump //然后:/year
由下图结果可知,未初始化的全局变量处于bss段之间:
如果存在bss段的内容没有赋值而直接采用,则里面存储的值可能是乱码,为了确保软件质量,所以一般要有清零操作。
clean_bss:
ldr r0, =bss_start //bss_start和bss_end是在链接器脚本gboot.lds中定义的
ldr r1, =bss_end
cmp r0, r1
moveq pc, lr //若r0和r1相等,则直接返回,否则展开清零工作
clean_loop:
mov r2, #0
str r2, [r0], #4
cmp r0, r1
bne clean_loop
mov pc, lr
三、跳转到C大门
采用绝对方式跳转。
定义文件main.c
int gboot_main()
{
return 0;
}
在start.s中加上:
ldr pc, =goot_main
修改Makefile:
因为之前是在汇编中点亮led的(起一个验证代码是否正确的作用),现在为了验证程序能不能正确跳转到main.c中,可以将点亮led的代码移植到main.c中:
请注意:(volatile unsigned long*)的书写方式。
//main.c
#define GPBCON (volatile unsigned long*)0x56000010
#define GPBDAT (volatile unsigned long*)0x56000014
int gboot_main()
{
*(GPBCON) = 0x400;
*(GPBDAT) = 0x0;
return 0;
}
之前汇编中的代码如下:
bl light_led
...
light_led:
#define GPBCON 0x56000010
#define GPBDAT 0x56000014
light_led:
ldr r0, =GPBCON
mov r1, #0x400
str r1, [r0]
ldr r0, =GPBDAT
mov r1, #0x0
str r1, [r0]
mov pc, lr
将上面的bl light_led备注掉,即@bl light_led。注意汇编中的注释符号是“”@“”。