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.carm-linux-gcc -g stack1.c -o stack1arm-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.carm-linux-gcc -g stack2.c -o stack2arm-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.carm-linux-gcc -g stack3.c -o stack3arm-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 bssarm-linux-readelf -a bss >dumpvi 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*)0x56000014int 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。注意汇编中的注释符号是“”@“”。
新闻热点
疑难解答