D13x:Reset_Handler到entry解析

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

工程里用了 -nostartfilesrtconfig.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 里主要步骤(按顺序):

  1. j save_boot_params :保存 a0--a7、s0--s11、sp、ra;非 BootLoader 时还可把 SPL 的 boot_arg 拷到 boot_arg
  2. save_boot_params_ret :设 gp、配置 mtvec/mtvt、设栈、开 I/D Cache
  3. 可选:TCM、SRAM_S1、PSRAM 属性等
  4. QEMU :拷贝 .data实机当前配置 :LMA=VMA,一般 在启动里拷 .data
  5. 清零 BSS__bss_start__ ~ __bss_end__
  6. reloc_private_paramsSystemInit(若未禁用)
  7. 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_Handlerentry 的区别

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.SReset_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 段)。

相关推荐
xzl042 天前
PBP 运行时:pbp_cfg.bin 解析全流程
artinchip
xzl044 天前
D13x开发实战:从QEMU到真机全流程
artinchip