ENTRY(Reset_Handler) 与 startup_gcc.S 的关系
这条链路是:链接脚本声明 ELF 入口符号 → 链接器把该符号地址写入 ELF → BootLoader/CPU 从该地址开始执行 → 对应 startup_gcc.S 里的 Reset_Handler 实现。
1. ENTRY(Reset_Handler) 做什么
在 gcc_aic.ld.S / gcc_aic.ld 里:
214:214:D:\code\rt-thread\luban-lite-260519\luban-lite-master\bsp\artinchip\sys\d13x\link_script\gcc_aic.ld.S
ENTRY(Reset_Handler)
对 GNU ld 的含义:
- 把
Reset_Handler的链接地址 记为 ELF 的 入口点 (e_entry) - BootLoader 加载固件后,通常 跳转到
e_entry,而不是随便从.text开头跑 - 用
readelf -h xxx.elf可见Entry point address等于Reset_Handler的 VMA
工程里用了 -nostartfiles (rtconfig.py),不会链接 glibc 的 crt0。因此 第一个由你控制的 C/汇编入口就是 Reset_Handler ,必须由链接脚本 ENTRY 指定。
2. 符号在哪里定义
120:133:D:\code\rt-thread\luban-lite-260519\luban-lite-master\bsp\artinchip\sys\d13x\startup_gcc.S
.text
.align 2
_start:
.text
.align 2
.globl Reset_Handler
...
Reset_Handler:
.option push
.option norelax
j save_boot_params
save_boot_params_ret:
要点:
| 符号 | 作用 |
|---|---|
Reset_Handler |
全局符号,真正的复位入口 ;与 ENTRY(Reset_Handler) 同名绑定 |
_start |
仅段内标签,不是 ELF 入口;常见于习惯命名,本工程入口仍是 Reset_Handler |
save_boot_params / save_boot_params_ret |
在 boot_param_gcc.S 中保存 Boot 传入参数,再跳回 save_boot_params_ret 继续初始化 |
链接脚本保证启动代码不被 GC 掉,并尽量靠前:
237:241:D:\code\rt-thread\luban-lite-260519\luban-lite-master\bsp\artinchip\sys\d13x\link_script\gcc_aic.ld.S
.text : AT(ADDR(.text)){
...
KEEP(*startup_gcc.o(*.text*))
*(.text)
startup_gcc.o 里含 Reset_Handler 的 .text 会 优先 放进 .text,与 Boot 跳转到固定入口的用法一致。
3. 从复位到 RT-Thread 的执行链
PC = e_entry
BootLoader / 复位
Reset_Handler
save_boot_params
save_boot_params_ret
缓存/TCM/PSRAM/BSS 等
entry
rtthread_startup
Reset_Handler 里主要步骤(按顺序):
j save_boot_params:保存 a0--a7、s0--s11、sp、ra;非 BootLoader 时还可把 SPL 的boot_arg拷到boot_argsave_boot_params_ret:设gp、配置mtvec/mtvt、设栈、开 I/D Cache- 可选:TCM、SRAM_S1、PSRAM 属性等
- QEMU :拷贝
.data;实机当前配置 :LMA=VMA,一般 不 在启动里拷.data - 清零 BSS (
__bss_start__~__bss_end__) reloc_private_params、SystemInit(若未禁用)jalr entry(定义了KERNEL_RTTHREAD时)
236:238:D:\code\rt-thread\luban-lite-260519\luban-lite-master\bsp\artinchip\sys\d13x\startup_gcc.S
#if (defined(KERNEL_RTTHREAD) || defined(KERNEL_FREERTOS))
la a5, entry
jalr a5
RT-Thread 的 entry() 在 components.c:
170:174:D:\code\rt-thread\luban-lite-260519\luban-lite-master\kernel\rt-thread\src\components.c
int entry(void)
{
rtthread_startup();
return 0;
}
4. Reset_Handler 与 entry 的区别
Reset_Handler |
entry |
|
|---|---|---|
| 语言 | 汇编 | C |
| 谁调用 | BootLoader / 硬件复位后 PC 指向它 | Reset_Handler 末尾 jalr |
| 链接器角色 | ENTRY() → ELF 入口 |
普通全局函数 |
| 职责 | 硬件级初始化:向量、栈、Cache、BSS、板级 SystemInit |
进入内核:rtthread_startup() |
| 能否省略 | 不能(无 crt0) | 不能(否则进不了 RT-Thread) |
注释里写的 "Add -eentry" 是 ARM 工程 的另一种写法;本 D13x RISC-V 工程用的是 ENTRY(Reset_Handler) + 启动末尾再调 entry(),两层入口分工不同。
5. 和向量表的关系
- 执行入口 :
Reset_Handler(在.text/ PSRAM) - 中断向量表 :
__Vectors在.vectors,经KEEP(*startup_gcc.o(*.vectors*))放进.data,启动里csrw mtvt, __Vectors
11:15:D:\code\rt-thread\luban-lite-260519\luban-lite-master\bsp\artinchip\sys\d13x\startup_gcc.S
.section .vectors, "aw", @progbits
...
__Vectors:
复位后 先跑 Reset_Handler 做环境初始化 ,之后中断才按 __Vectors 分发;二者不是同一个符号。
6. 小结
ENTRY(Reset_Handler) 告诉链接器:ELF 的 e_entry = startup_gcc.S 里 Reset_Handler 的地址 。Boot 把 PC 设到该地址后,经 save_boot_params → 硬件/内存初始化 → entry() → rtthread_startup() 进入 RT-Thread。
若要核对本机固件,可在构建产物目录执行:
bash
readelf -h d13x.elf | grep Entry
nm d13x.elf | grep -E 'Reset_Handler| entry'
两者地址应一致为 Reset_Handler 的 VMA(在你当前配置下通常在 PSRAM 的 .text 段)。