1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
//! Provides the default entry points and lang items for panics and unwinding.
//!
//! These lang items are required by the Rust compiler.
//! They should never be directly invoked by developers, only by the compiler.
//!
#![no_std]
#![feature(alloc_error_handler)]
#![allow(internal_features)]
#![feature(lang_items)]
#![feature(panic_info_message)]
extern crate alloc;
use core::panic::PanicInfo;
use log::error;
#[cfg(target_arch = "x86_64")]
use early_printer::println;
#[cfg(target_arch = "aarch64")]
use log::info as println;
/// The singular entry point for a language-level panic.
///
/// The Rust compiler will rename this to "rust_begin_unwind".
#[cfg(not(test))]
#[panic_handler] // same as: #[lang = "panic_impl"]
fn panic_entry_point(info: &PanicInfo) -> ! {
// Since a panic could occur before the memory subsystem is initialized,
// we must check before using alloc types or other functions that depend on the memory system (the heap).
// We can check that by seeing if the kernel mmi has been initialized.
let kernel_mmi_ref = memory::get_kernel_mmi_ref();
let res = if kernel_mmi_ref.is_some() {
// proceed with calling the panic_wrapper, but don't shutdown with try_exit() if errors occur here
#[cfg(not(loadable))] {
panic_wrapper::panic_wrapper(info)
}
#[cfg(loadable)] {
// An internal function for calling the panic_wrapper, but returning errors along the way.
// We must make sure to not hold any locks when invoking the panic_wrapper function.
fn invoke_panic_wrapper(info: &PanicInfo) -> Result<(), &'static str> {
type PanicWrapperFunc = fn(&PanicInfo) -> Result<(), &'static str>;
const PANIC_WRAPPER_SYMBOL: &'static str = "panic_wrapper::panic_wrapper::";
let section = {
mod_mgmt::get_initial_kernel_namespace()
.and_then(|namespace| namespace.get_symbol_starting_with(PANIC_WRAPPER_SYMBOL).upgrade())
.ok_or("Couldn't get single symbol matching \"panic_wrapper::panic_wrapper::\"")?
};
let func: &PanicWrapperFunc = unsafe { section.as_func() }?;
func(info)
}
// call the above internal function
invoke_panic_wrapper(info)
}
}
else {
Err("memory subsystem not yet initialized, cannot call panic_wrapper because it requires alloc types")
};
if let Err(_e) = res {
error!("Halting due to early panic: {}", info);
// basic early panic printing with no dependencies
println!("\nHalting due to early panic: {}", info);
}
// If we failed to handle the panic, there's not really much we can do about it,
// other than just let the thread spin endlessly (which doesn't hurt correctness but is inefficient).
// But in general, this task should be killed by the panic_wrapper, so it shouldn't reach this point.
// Only panics early on in the initialization process will get here, meaning that the OS will basically stop.
loop { core::hint::spin_loop() }
}
/// Typically this would be an entry point in the unwinding procedure, in which a stack frame is unwound.
/// However, in Theseus we use our own unwinding flow which is simpler.
///
/// This function will always be renamed to "rust_eh_personality" no matter what function name we give it here.
#[cfg(not(test))]
#[lang = "eh_personality"]
#[no_mangle]
#[doc(hidden)]
extern "C" fn rust_eh_personality() -> ! {
panic!("BUG: Theseus does not use rust_eh_personality. Why has it been invoked?")
}
/// This function is automatically jumped to after each unwinding cleanup routine finishes executing,
/// so it's basically the return address of every cleanup routine.
///
/// Just like the panic_entry_point() above, this is effectively just an entry point
/// that invokes the real `unwind_resume()` function in the `unwind` crate,
/// but does so dynamically in loadable mode.
#[no_mangle]
#[cfg(target_arch = "x86_64")]
extern "C" fn _Unwind_Resume(arg: usize) -> ! {
#[cfg(not(loadable))] {
unwind::unwind_resume(arg)
}
#[cfg(loadable)] {
// An internal function for calling the real unwind_resume function, but returning errors along the way.
// We must make sure to not hold any locks when invoking the function.
fn invoke_unwind_resume(arg: usize) -> Result<(), &'static str> {
type UnwindResumeFunc = fn(usize) -> !;
const UNWIND_RESUME_SYMBOL: &'static str = "unwind::unwind_resume::";
let section = {
mod_mgmt::get_initial_kernel_namespace()
.and_then(|namespace| namespace.get_symbol_starting_with(UNWIND_RESUME_SYMBOL).upgrade())
.ok_or("Couldn't get single symbol matching \"unwind::unwind_resume::\"")?
};
let func: &UnwindResumeFunc = unsafe { section.as_func() }?;
func(arg)
}
match invoke_unwind_resume(arg) {
Ok(()) => error!("BUG: _Unwind_Resume: unexpectedly returned Ok(()) from unwind::unwind_resume()"),
Err(e) => error!("_Unwind_Resume: failed to dynamically invoke unwind::unwind_resume! Error: {}", e),
}
loop { core::hint::spin_loop() }
}
}
#[no_mangle]
#[cfg(target_arch = "aarch64")]
extern "C" fn _Unwind_Resume(_arg: usize) -> ! {
todo!("_Unwind_Resume is not yet implemented on aarch64")
}
/// This is the callback entry point that gets invoked when the heap allocator runs out of memory.
#[alloc_error_handler]
#[cfg(not(test))]
fn oom(_layout: core::alloc::Layout) -> ! {
error!("\n(oom) Out of Heap Memory! requested allocation: {:?}", _layout);
panic!("\n(oom) Out of Heap Memory! requested allocation: {:?}", _layout);
}