构建过程
大量的构建过程都在各个库中的 build.rs 中实现,主要是通过不同的构建目录将预定点的链接文件复制到输出目录中,并通过 cargo:rustc-link-search= 指令将对应的目录加入到链接脚本的查询目录中,最后在构建是 通过 link-arg= 加上指定的入口链接脚本 构建完成的目标文件.
链接脚本的主要作用就是指定程序在内存中的布局
rust 代码入口在 esp-riscv-rt 库中的 lib.rs 中,通过一段内嵌宏实现的入口点,在引导程序bootloader执行完成后会跳转到 _start 入口处。在进入main方法前的代码需要结合 链接脚本中的符号定义进行分析。
跳转过程:
_start -》 _abs_start -》_start_rust
_start_rust 方法对应start_rust 方法,
start_rust方法中顺序调用 __post_init(); _setup_interrupts();main(a0, a1, a2);
fn main 对应到我们定义的main方法
__post_init 对应 default_post_init 方法,这个方法是一个外部方法,置未找到实现位置,
_setup_interrupts = default_setup_interrupts
default_setup_interrupts 中会调用 mtvec::write 设置中断入口为_start_trap
_start_trap = default_start_trap
default_start_trap 定义在汇编部分
代码执行流程大概如下:
通过_start 进入程序后跳转到 _start_rust 汇编处 ,start_rust 中会调用 _setup_interrupts() 设置中断入口,
然后通过main(a0, a1, a2);进入被#[entry]标记的方法中。当有中断触发时会进入default_start_trap
汇编处再跳到 start_trap_rust 中,通过mcause 寄存器判断出是异常还是中断,中断与异常会进入不同的处理方法,
异常会进入ExceptionHandler , 中断会判断中断类型进入不同的handler中,这些handler 都是与相应的中断名称相关联的,
在链接脚本当中会定义所有中断默认为 DefaultInterruptHandler ,这个默认处理中是一空loop,所以我们需要定义不同的
中断处理程序,比如GPIO 中断就定义一个GPIO方法并用 #[interrupt]标记, 中断标记与入口标记类似会导出相应的符号
由对应的程序调用。