国嵌上学期笔记-自己实现bootloader-第四节
摘要:本节把代码复制到内存。初始化C语言的环境,并调到main函数开始执行。
1、从NAND启动,则NAND前4KB的代码会被复制到SRAM,从SRAM开始执行,这4KB的代码把NAND剩下的代码复制到内存中,调到内存相应位置执行。
本节首先将SRAM的代码复制到内存运行。因为NAND还没有初始化。
2、链接地址:
当main函数执行时,调用了一个函数,PC指针就会被赋值为这个函数的链接地址。
ldr伪指令直接修改PC指针为链接地址,跳转到相应地址执行。
上电之后,还不在内存,PC为0,b指令和bl指令是相对跳转(bl会把返回地址放到 lr 寄存器),相对跳转不是把链接地址直接给PC,而是计算一下跳转位置和当前位置的差,给PC指针加上一个差值。
所以要把代码拷贝到链接起始地址处。
3、反汇编:arm-linux-objdump -D -S gboot.elf > dump
-D反汇编所有段,-S尽可能反汇编出源代码,尤其当编译的时候指定了-g这种调试参数时。
4、栈初始化:
(1)满栈:SP指针总是指向最后压入栈的数据。
空栈:SP指针总是指向下一个将要放入数据的空位置。
ARM采用满栈。
升栈:SP指针从低地址到高地址。
降栈:SP指针从高地址到低地址。
ARM采用降栈。
栈帧:每个函数使用的一部分栈成为栈帧。栈帧的上边界FP(R11),下边界SP(R13)。main函数调用fun,则fun栈帧里面保存着main函数的FP和SP。
(2)栈的作用:
保存局部变量:汇编第一句,保存FP,!表示SP-4之后再赋值给SP。
传递参数:参数个数大于4的时候,用栈传递,参数小于4用寄存传递。(不一定是4,x86_64是6个参数)
保存寄存器的值:改变寄存器之前,将寄存器的值保存到栈里面。
(3)SP指针:0x3400 0000 内存地址
5、BSS段的初始化:将BSS段清0。
(1)BSS段的作用:初始化的全局变量存放在数据段。
局部变量在栈中。
malloc来自于堆。
未初始化全局变量和静态变量存放在BSS段。
(2)初始化原因:如果不初始化,写程序的时候又忘记初始化全局变量,直接用,会是不确定的值,为了避免错误,将BSS段 的内存全部初始化为0。
6、跳转到main函数,进入C语言:
相对跳转B和BL只能在一个代码段里面跳转,main函数在sram和内存中都有,必须用绝对跳转。
7、C与汇编混合编程。
(1)汇编:效率高,直接控制处理器。
C语言:可读性强,移植性好。
(2)种类:
C调用汇编:例如,汇编中的标号是led,直接在C语言中 led() 就可以(led必须声名为全局的.global led)。
汇编调用C:把函数名赋值给PC指针。
C内嵌汇编:_asm_或者asm都可以。有4个部分:第一部分必须有,其余三部分可以省略,但是冒号都不能省略。
_asm_(
汇编语句
:输出部分-------------->在汇编中可能修改C语言的变量,这些变量在输出部分
:输入部分-------------->汇编需要拿到C语言的一些值作为参数
:破坏描述部分--------->如果修改某些寄存器的值,这些寄存器放在这里
);