专题10-C语言环境初始化

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lvjianxin6015/article/details/55282763

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 传递参数

扫描二维码关注公众号,回复: 3775511 查看本文章

当函数的参数大于 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。注意汇编中的注释符号是“”@“”。

猜你喜欢

转载自blog.csdn.net/lvjianxin6015/article/details/55282763