專題10-C語言環境初始化

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  //若r0r1相等,則直接返回,否則展開清零工作

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章