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。注意彙編中的註釋符號是“”@“”。