裸机程序开始为什么要有一段汇编
前言
在看正点原子 i.MX6ULL 的裸机代码时,看到每个工程里面都有一个汇编代码文件 start.S,那么裸机程序为什么一定要以汇编代码作为开始呢?直接使用 C 语言不行吗?
汇编都干了啥
start.S
.global _start
_start:
ldr sp, =0x80200000 /* 设置 SP 指针 */
b main /* 跳转到 C 语言 main 函数 */
就干了两件事
- 设置堆栈指针
- 跳转到 C 语言 main() 函数
所以汇编起到的作用就是设置栈指针。
因为 C 语言执行的都是函数,只要执行函数就需要使用栈空间。如果没有设置栈指针,程序可能会因为栈溢出或其他未定义行为而崩溃或者产生错误。
测试一下
编个纯 C 测下
main.c
#include "main.h"
/* 使能外设时钟 */
void clk_enable(void)
{
CCM_CCGR1 = 0xFFFFFFFF;
CCM_CCGR2 = 0xFFFFFFFF;
CCM_CCGR3 = 0xFFFFFFFF;
CCM_CCGR4 = 0xFFFFFFFF;
CCM_CCGR5 = 0xFFFFFFFF;
CCM_CCGR6 = 0xFFFFFFFF;
}
/* 初始化 LED */
void led_init(void)
{
SW_MUX_GPIO1_IO03 = 0x5; /* 复用为 GPIO1-IO03 */
SW_PAD_GPIO1_IO03 = 0x10B0; /* 设置 GPIO1-IO03 电气属性 */
/* GPIO 初始化 */
GPIO1_GDIR = 0x08; /* 设置为输出 */
GPIO1_DR = 0x0; /* 打开 LED 灯 */
}
/* 短延时 */
void delay_short(volatile unsigned int n)
{
while (n--) {
}
}
/* 延时,大概 ms 级别,主频 396MHz */
void delay(volatile unsigned int n)
{
while (n--) {
delay_short(0x7ff);
}
}
void led_on(void)
{
GPIO1_DR &= ~(1 << 3); /* bit3 清零 */
}
void led_off(void)
{
GPIO1_DR |= (1 << 3); /* bit3 置位 */
}
int main(void)
{
clk_enable(); /* 使能外设时钟 */
led_init(); /* 初始化 LED */
while (1) {
led_on();
delay(2000);
led_off();
delay(2000);
}
return 0;
}
Makefile
objs = main.o
ledc.bin : $(objs)
arm-linux-gnueabihf-ld -Ttext 0x87800000 $^ -o ledc.elf -e main
arm-linux-gnueabihf-objcopy -O binary -S ledc.elf $@
arm-linux-gnueabihf-objdump -D -m arm ledc.elf > ledc.dis
%.o : %.c
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o $@ $<
clean:
rm -rf *.o ledc.bin ledc.elf ledc.dis
install:
cp ledc.bin ~/tftp
编译
$ make clean && make && make install
rm -rf *.o ledc.bin ledc.elf ledc.dis
arm-linux-gnueabihf-gcc -Wall -nostdlib -c -o main.o main.c
arm-linux-gnueabihf-ld -Ttext 0x87800000 main.o -o ledc.elf -e main
arm-linux-gnueabihf-objcopy -O binary -S ledc.elf ledc.bin
arm-linux-gnueabihf-objdump -D -m arm ledc.elf > ledc.dis
cp ledc.bin ~/tftp
i.MX6ULL 开发板进入 uboot 模式,tftp 下载 ledc.bin 到 0x87800000,并跳转到该地址运行,发现板子卡住了(LED 没有闪灯),应该是栈溢出,修改了不该修改的内存,导致程序崩溃了。
恢复带汇编 start.S 的版本。程序运行成功(LED 闪烁)。
延伸思考1
C 语言不能设置堆栈指针吗?
C 语言可以设置堆栈指针,但是运行 C 语言前就要设置好,不然无法运行 C 程序。
延伸思考2
bootloader 开始代码 arch/arm/lib/vectors.S
kernel 开始代码 arch/arm/kernel/head.S
都是从汇编开始
延伸思考3
C 语言比 Java 语言更底层,因为 C 语言可以直接操作内存地址。
汇编语言比 C 语言更底层,因为汇编语言可以直接操作寄存器。