在非#![no_std]环境下覆盖Rust标准库默认Panic处理程序
我正在尝试用Rust编写内核,发现通过以下配置可以从源码编译并使用标准库:
[unstable] build-std = ["core", "std", "panic_abort"]
显然初始状态下很多功能无法正常工作。目前我试图让panic!()正常运行,但从标准库实现代码来看,panic会因尝试打印栈跟踪而触发异常。
对应标准库std::panicking::rust_panic_with_hook代码片段:
if must_abort || panics > 2 { if panics > 2 { // Don't try to print the message in this case // - perhaps that is causing the recursive panics. rtprintpanic!("thread panicked while processing panic. aborting.\n"); } else { // Unfortunately, this does not print a backtrace, because creating // a `Backtrace` will allocate, which we must to avoid here. let panicinfo = PanicInfo::internal_constructor(message, location, can_unwind); rtprintpanic!("{panicinfo}\npanicked after panic::always_abort(), aborting.\n"); } crate::sys::abort_internal(); }
在完成堆分配设置后,我尝试通过以下代码添加无限循环作为panic hook:
std::panic::set_hook(Box::new(|_| loop {}))
但打印操作发生在hook执行之前,这部分逻辑是标准库硬编码的。
请问是否存在无需使用#![no_std]即可覆盖标准库默认panic处理程序的方案?
编辑补充:
我使用的是v1.70.0-nightly版本,并启用了#![feature(restricted_std)]特性。
附上所有配置文件:
x86_64-my_os.json
{ "llvm-target": "x86_64-unknown-none", "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", "arch": "x86_64", "target-endian": "little", "target-pointer-width": "64", "target-c-int-width": "32", "os": "none", "executables": true, "linker-flavor": "ld.lld", "linker": "rust-lld", "panic-strategy": "abort", "disable-redzone": true, "features": "-mmx,-sse,+soft-float" }
.cargo/config.toml
[unstable] build-std = ["core", "std", "panic_abort", "compiler_builtins", "alloc"] build-std-features = ["compiler-builtins-mem"] [build] target = "x86_64-my_os.json" [target.'cfg(target_os = "none")'] runner = "bootimage runner"
Cargo.toml
[package] name = "my_os" version = "0.1.0" edition = "2021" [dependencies] bootloader = "0.9.8" linked_list_allocator = "0.10"
由于你使用的是panic_abort panic策略,且目标为无OS环境,标准库的panic处理逻辑确实会在调用自定义hook前执行打印操作,这是硬编码在rust_panic_with_hook中的行为,无法通过set_hook跳过。但可以通过以下方式解决:
自定义
panic_handler完全替代默认逻辑
利用#[panic_handler]属性,你可以直接覆盖标准库的panic处理流程——即使使用std(restricted_std特性允许在无OS环境中使用受限标准库的同时,保留自定义panic handler的能力)。示例代码:#![feature(panic_handler)] #[panic_handler] fn panic(info: &core::panic::PanicInfo) -> ! { // 这里实现自定义panic逻辑,比如输出信息到串口、进入无限循环等 loop {} }这个handler会完全跳过标准库中触发异常的打印步骤,直接执行你定义的逻辑。
实现
rtprintpanic的底层支持
标准库的rtprintpanic!宏依赖目标平台的底层打印实现,在无OS环境中需要你手动提供对应的底层函数支持。实现std::sys模块下的打印相关接口后,rtprintpanic就能正常工作,不会触发异常。切换
panic_unwind策略(不推荐)
若切换到panic_unwind,panic处理流程会变化,但这需要你实现unwind的底层支持,对于内核开发来说复杂度较高,不是首选方案。
注意:即使使用std,无OS环境下多数标准库功能都需要你手动实现底层syscall支持,restricted_std仅允许链接标准库,不提供这些底层实现。
内容的提问来源于stack exchange,提问作者Gian Laager

