Comprehensive Rust 课程实战:AArch64 裸机异常向量表与 Rust 异常处理实现

发布时间:2026/9/10 17:38:53

Comprehensive Rust 课程实战:AArch64 裸机异常向量表与 Rust 异常处理实现 Comprehensive Rust 课程实战AArch64 裸机异常向量表与 Rust 异常处理实现【免费下载链接】comprehensive-rustThis is the Rust course used by the Android team at Google. It provides you the material to quickly teach Rust.项目地址: https://gitcode.com/GitHub_Trending/co/comprehensive-rust导读在 Comprehensive Rust 课程Google Android 团队使用的 Rust 教学项目的 Application Processors应用处理器专题中exceptions.md讲解了一个核心裸机知识点如何在 AArch64 架构上为 EL1 异常级别搭建 16 条目的异常向量表并用汇编保存易失寄存器后进入 Rust 代码执行处理逻辑。本文以该文档为骨架结合仓库中src/bare-metal/aps/examples/的完整源码exceptions.rs、exceptions.S、entry.S等系统讲解 AArch64 异常模型、汇编宏的寄存器保存/恢复布局、向量表内存对齐规则、启动时代码设置VBAR_EL1的过程以及aarch64-rtcrate 提供的 trait 抽象方案帮助读者完整掌握在无操作系统环境下用 Rust 汇编处理异常的实战方法。一、背景为什么 AArch64 裸机程序需要自建异常向量表在微控制器如 Arm Cortex-M 系列中异常处理通常由厂商提供的启动文件或中断向量表机制直接承接。而应用处理器Application Processor场景下——课程使用 QEMU 的 aarch64virt开发板采用-machine virt -cpu max参数模拟——我们像编写操作系统一样直接面对硬件异常向量表必须由我们自己放在正确的地址并把处理逻辑接进 Rust。AArch64 架构定义了一张包含16 个条目的异常向量表结构为4 种异常类型 × 4 种来源状态来源状态 \ 异常类型Synchronous同步异常IRQFIQSError当前 EL使用 SP0✓✓✓✓当前 EL使用 SPxSP_ELx✓✓✓✓低 ELAArch64 状态✓✓✓✓低 ELAArch32 状态✓✓✓✓其中Synchronous同步异常包括未定义指令、系统调用SVC/HVC/SMC、数据中止、指令中止、栈对齐错误等IRQ / FIQ中断请求与快速中断请求属于异步异常SError系统错误属于异步异常。向量表条目之间以0x80128 字节为步长对齐这正是exceptions.S中连续出现的.balign 0x80指令的作用。整个向量表本身还必须 2KB0x800对齐对应汇编中的.balign 0x800。课程文档明确指出本次下午练习的所有示例都运行在EL1Exception Level 1即内核/OS 所在特权级。详见 src/bare-metal/aps/examples/src/exceptions.S。二、Rust 侧8 个extern C处理函数异常向量表用汇编编写但真正的处理逻辑放在 Rust 中。仓库中的 src/bare-metal/aps/examples/src/exceptions.rs 定义了与 16 个向量表条目一一对应的 8 个处理函数每条汇编入口跳转到同一个 Rust 函数故 16 条目只需 8 个函数use log::error; use smccc::Hvc; use smccc::psci::system_off; // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] extern C fn sync_current(_elr: u64, _spsr: u64) { error!(sync_current); system_off::Hvc().unwrap(); } #[unsafe(no_mangle)] extern C fn irq_current(_elr: u64, _spsr: u64) { error!(irq_current); system_off::Hvc().unwrap(); } // ...fiq_current、serror_current、sync_lower、irq_lower、fiq_lower、serror_lower 同构关键设计要点#[unsafe(no_mangle)]extern C汇编通过bl带链接分支直接调用这些函数因此必须使用 C ABI 并保留符号原名保证汇编侧符号解析成功。源码注释中每条都标注了// SAFETY: There is no other global function of this name.即安全前提是仓库内不存在同名的其他全局函数no_mangle不会导致符号冲突。两个形参_elr: u64与_spsr: u64汇编侧在调用前会把elr_el1异常返回地址寄存器和spsr_el1保存的程序状态寄存器读入x0、x1恰好作为 C ABI 下函数的第一、第二个参数。这样 Rust 处理函数可以直接拿到异常发生时在哪条指令、处于什么 PSTATE为后续恢复/诊断提供依据。处理策略记录日志并关机每个处理函数都调用log::error!输出异常类型名随后调用system_off::Hvc().unwrap()通过 PSCI 固件接口以 HVC 调用方式关闭系统。课程文档说明这样做的原因示例中不预期任何异常真正发生一旦发生只记录并停机便于在 QEMU 串口上看到是哪一类异常被触发。三、汇编侧保存易失寄存器并转入 Rust汇编实现位于 src/bare-metal/aps/examples/src/exceptions.S核心思路是用两条汇编宏分别完成保存易失寄存器到栈与从栈恢复中间插入一次对 Rust 处理函数的调用。3.1save_volatile_to_stack14 条指令的现场保存.macro save_volatile_to_stack /* Reserve stack space and save registers x0-x18, x29 x30. */ stp x0, x1, [sp, #-(8 * 24)]! stp x2, x3, [sp, #8 * 2] stp x4, x5, [sp, #8 * 4] stp x6, x7, [sp, #8 * 6] stp x8, x9, [sp, #8 * 8] stp x10, x11, [sp, #8 * 10] stp x12, x13, [sp, #8 * 12] stp x14, x15, [sp, #8 * 14] stp x16, x17, [sp, #8 * 16] str x18, [sp, #8 * 18] stp x29, x30, [sp, #8 * 20] /* Save elr_el1 spsr_el1... */ mrs x0, elr_el1 mrs x1, spsr_el1 stp x0, x1, [sp, #8 * 22] .endm要点一次性压栈 24 个 8 字节槽位共 192 字节覆盖x0–x18、x29帧指针、x30链接寄存器以及elr_el1、spsr_el1同时保存elr_el1/spsr_el1的目的见源码注释是支持嵌套异常发生时仍能正确回溯unwind宏末尾把elr_el1、spsr_el1分别装入x0、x1正好满足 Rust 处理函数(elr, spsr)的参数约定。3.2restore_volatile_from_stack对称恢复.macro restore_volatile_from_stack ldp x2, x3, [sp, #8 * 2] /* ... 依次恢复 x2-x18, x29, x30 ... */ ldp x0, x1, [sp, #8 * 22] msr elr_el1, x0 msr spsr_el1, x1 ldp x0, x1, [sp], #8 * 24 .endm恢复时先还原通用寄存器再把栈上保存的elr_el1、spsr_el1写回系统寄存器用x0/x1作临时寄存器最后恢复x0/x1并释放 192 字节栈空间。3.3current_exception组装完整处理流程.macro current_exception handler:req save_volatile_to_stack bl \handler restore_volatile_from_stack eret .endm保存现场 → 调用 Rust 处理函数 → 恢复现场 →eret返回被中断的执行流。值得注意的工程细节源码注释有明确计算保存现场 跳转共 15 条指令恢复 返回共 15 条指令整个 handler 恰好 30 条指令能塞进 128 字节32 条指令的向量表条目配额内——这正是把保存/恢复提取为宏而不是逐条展开的根本原因。3.4 16 条目向量表.section .text.vector_table_el1, ax .global vector_table_el1 .balign 0x800 vector_table_el1: sync_cur_sp0: current_exception sync_current .balign 0x80 irq_cur_sp0: current_exception irq_current /* ... 依序排列到 serr_lower_32 ... */16 个条目的排布严格遵循 AArch64 规范顺序当前 ELSP0四类 → 当前 ELSPx四类 → 低 EL AArch64 四类 → 低 EL AArch32 四类。课程文档同时说明了两处刻意简化不区分 SP0 与 SPx当前 EL 的两组异常共用一个处理函数不区分 AArch32 与 AArch64 低 EL 异常。四、启动流程把向量表地址写进VBAR_EL1有了向量表还不够——CPU 必须知道它在哪里。这一职责由启动汇编 src/bare-metal/aps/examples/src/entry.S 承担在跳转 Rustmain之前完成/* Prepare the stack. */ adr_l x30, boot_stack_end mov sp, x30 /* Set up exception vector. */ adr x30, vector_table_el1 msr vbar_el1, x30 /* Call into Rust code. */ bl mainVBAR_EL1Vector Base Address Register持有异常向量表基地址entry.S注释总结了整个入口的职责链配置 MMU 恒等映射并开启缓存 → 开启浮点 → 清零 bss → 准备栈 → 设置异常向量 → 跳转 Rustmain。同时entry.S明确提到入口会保留x0–x3给 Rust 入口点因为它们可能携带引导参数这正是main_rt.rs中fn main(x0: u64, x1: u64, x2: u64, x3: u64)四个参数的来源。五、把异常处理看作线程Send/Sync的考量课程文档给出一条非常实用的心智模型可以把异常处理函数与主执行上下文近似地视为不同的线程。因此想在两者之间共享的数据其类型必须满足相应的Send/Sync约束与线程间共享数据完全同理例如某值只Send而不Sync可移动所有权但不可多线程同时引用那么在异常处理函数与程序其他部分之间共享它时就需要用Mutex之类的互斥原语包裹后放入static静态变量中。这一节对应课程 Send 和 Sync 专题的内容在裸机环境下同样是编写安全并发/中断代码的基本功。特别要注意Send/Sync是unsafe trait除非数据确实满足共享条件否则不能随意实现。六、进阶抽象用aarch64-rtcrate 免除手写汇编手写 16 条目向量表属于从零做起的教学路线。课程还提供了另一条生产级路线aarch64-rtcrate 的 trait 抽象对应文档 src/bare-metal/aps/aarch64-rt/exceptions.md 与示例 src/bare-metal/aps/examples/src/exceptions_rt.rsuse aarch64_rt::{ExceptionHandlers, RegisterStateRef, exception_handlers}; struct Handlers; impl ExceptionHandlers for Handlers { extern C fn sync_current(_state: RegisterStateRef) { error!(sync_current); system_off::Hvc().unwrap(); } extern C fn irq_current(_state: RegisterStateRef) { error!(irq_current); system_off::Hvc().unwrap(); } // ... } exception_handlers!(Handlers);核心机制结合 src/bare-metal/aps/aarch64-rt/exceptions.md 的说明ExceptionHandlerstrait每个方法都有默认实现默认直接 panic因此不期望发生的异常可以干脆不重写对应方法代码更精简exception_handlers!宏编译期生成global_asm!块内含与手写版exceptions.S同构的异常向量表自动把汇编侧的栈帧接到 Rust 方法上RegisterStateRef包装异常发生时汇编保存寄存器栈帧的引用可用于从低 EL 的 SMC/HVC 调用中提取参数或在处理函数返回时更新将被恢复的寄存器值——为编写虚拟化/固件场景如 SMC/HVC 转发提供了便捷入口。对应的可运行程序是 src/bare-metal/aps/examples/src/main_rt.rs#![no_main] #![no_std]通过initial_pagetable!建立恒等页表0x900_0000处 PL011 UART 设备内存、普通内存与 256 GiB 处的设备内存entry!(main)注入启动入口main中通过Uart打印x0–x3引导参数后system_off。该二进制对应Cargo.toml中的rtbin 目标。七、构建与运行QEMU 上验证异常处理示例工程的构建与运行命令集中在 src/bare-metal/aps/examples/Makefilert.bin: build cargo objcopy --bin rt -- -O binary $ qemu_rt: rt.bin qemu-system-aarch64 -machine virt -cpu max -serial mon:stdio -display none -kernel $ -s流程为cargo build编译工程为 workspace依赖见 src/bare-metal/aps/examples/Cargo.toml含aarch64-paging、aarch64-rt、smccc、log等→cargo objcopy --bin rt -- -O binary生成裸二进制 → QEMU 以virt机器、maxCPU、串口直连终端的方式加载-kernel。运行后可在串口观察到main(0x0, 0x0, ...)之类的输出若程序运行中触发了异常则会看到对应处理函数打出的sync_current/irq_current等日志并触发关机——这就是验证向量表工作与否的直接手段。八、总结从课程文档 src/bare-metal/aps/exceptions.md 出发本文完整还原了 AArch64 EL1 裸机异常处理的四条主线异常模型16 条目向量表 4 种异常类型 × 4 种来源状态条目 128 字节对齐、整表 2KB 对齐汇编现场保护用save_volatile_to_stack/restore_volatile_from_stack两条宏在 30 条指令内完成保存、调用 Rust、恢复、eret的完整闭环并传递elr_el1、spsr_el1给 Rust 侧启动接线entry.S在进入 Rust 前把向量表地址写入VBAR_EL1两种落地方式手写汇编 extern C函数或aarch64-rt的 trait 宏抽象无论哪种都要遵循Send/Sync视角看待异常处理函数即隐式线程的共享数据纪律。这套实现既是 Comprehensive Rust 课程应用处理器专题的教学核心也是一份可以直接参考的裸机中断/异常处理工程范式。【免费下载链接】comprehensive-rustThis is the Rust course used by the Android team at Google. It provides you the material to quickly teach Rust.项目地址: https://gitcode.com/GitHub_Trending/co/comprehensive-rust创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/10 17:38:53

流程定时启动全解析:触发原理、配置要点与运维实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/10 17:38:53

STM32三大隐性陷阱:时钟树、外设状态、开发环境脆弱性

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/10 17:33:53

CANN/GE模型查询信息创建接口

aclmdlBundleCreateQueryInfo 产品支持情况 产品 是否支持 Atlas A3 训练系列产品/Atlas A3 推理系列产品√ Atlas A2 训练系列产品/Atlas A2 推理系列产品√ 功能说明 创建aclmdlBundleQueryInfo类型的数据,表示模型描述信息。 如需销毁aclmdlBundleQueryInfo…

2026/9/10 18:24:04

麻雀搜索算法优化RBF神经网络的多变量时间序列预测实践

做多变量时间序列预测时,我一开始用的是普通的径向基函数神经网络(RBF神经网络)。数据归一化、滑动窗口、训练集测试集划分都按常规流程走完,模型收敛速度倒是挺快,但预测结果就是不怎么稳定,有时候换一组随…

2026/9/10 18:24:04

内容IP策划方法论:从叙事框架到生态构建

1. 从文字编辑到内容IP策划的思维跃迁十年前我刚入行做编辑时,每天的工作就是机械地校对错别字、调整段落格式。直到参与某知识付费专栏的孵化项目,才真正意识到:在内容爆炸的时代,单纯的内容加工者正在被算法和AI取代&#xff0c…

2026/9/10 18:24:04

基于CNN的游戏大局观教练:教孩子思考而非代打

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/10 18:24:04

MCP与TypeScript SDK实战:协议边界、选型与排错指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/10 18:19:00

丝杆升降机晃动与弯曲问题的诊断与解决方案

1. 丝杆升降机晃动与弯曲问题概述丝杆升降机作为工业领域常见的线性传动设备,其稳定性直接影响生产安全和效率。在实际使用中,晃动和弯曲是最常见的两类故障现象。根据我十多年的设备维护经验,这两种问题往往不是独立存在的——晃动会加速结构…

2026/9/10 16:39:38

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/10 11:16:38

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/9 16:31:09

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/10 0:00:55

目录对比去重实战:用哈希算法精准清理重复文件

我电脑里现在还有一块换了三次机的“数据墓地”硬盘,里面存着2016年以前所有旧笔记本的完整备份。平时不觉得有什么,直到前阵子想把它整理归档,发现同一个安装包、同一批照片、同一份论文草稿,在几个不同的备份目录里反复出现。更…

2026/9/10 0:00:55

Leaflet离线地图完整Demo合集:内网部署与坐标纠偏实战

简介:这是一份面向Web GIS开发者的LeafLet离线地图示例合集,帮助开发者快速掌握离线地图从搭建到交互的完整流程。压缩包共723个文件,大小14.06MB,以319个js脚本、175个html页面和29个css样式文件为主体,配合png/svg图…

2026/9/10 0:00:55

MATLAB读取Rinex 3.02观测文件:多系统GNSS数据解析实战

简介:基于MATLAB开发的Rinex3.02版观测文件(o文件)读取代码包,面向卫星定位导航方向的学习者与研究人员,用于解决新版观测文件的数据解析、历元提取与时间转换问题。压缩包共4个文件,包含两个m脚本、一个19…

2026/9/10 12:32:02

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/10 15:19:50

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/10 15:49:53

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码