diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 496e819..7c2efcc 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -149,10 +149,5 @@ pub fn boot() -> Result<(), Box> { x86_64::instructions::interrupts::enable(); debugln!("[Success]"); - debug!(" Initializing Stack Unwinder... "); - // Force evaluate the constructor. - // let _unwinder = &*UNWINDER; - debugln!("[Success]"); - Ok(()) } diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 45c695c..a22c9f2 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -3,18 +3,11 @@ extern crate alloc; -use core::arch::asm; - use foundry_os::{ - // arch::x86_64::processing::async_io::task::{Executor, Task}, + arch::x86_64::processing::async_io::task::{Executor, Task}, prelude::*, - // std::unwind::UNWINDER, - // util::shell::shell, + util::shell::shell, }; -// use framehop::{ -// x86_64::{CacheX86_64, UnwindRegsX86_64}, -// *, -// }; #[unsafe(no_mangle)] extern "C" fn kmain() -> ! { @@ -22,80 +15,9 @@ extern "C" fn kmain() -> ! { if let Err(err) = foundry_os::boot() { panic!("{}", err); } - println_log!(" [ Kernel Initialised Successfully ] "); - // println!("TESTING :: Allocation"); - // let somevec = vec![0; 1_000_000]; - // println!("{:?}", somevec); - // println!("{}", somevec.len()); - // println!("PASSED!"); - - test1(); - - // let mut executor = Executor::new(); - // executor.spawn(Task::new(shell())); - // executor.run() - - loop {} -} - -#[inline(never)] -pub fn test1() { - test2(); -} - -#[inline(never)] -pub fn test2() { - test3(); -} - -const fn read_stack(addr: u64) -> Result { - Ok(unsafe { *(addr as *const u64) }) -} - -#[inline(never)] -pub fn test3() { - // let mut cache = CacheX86_64::new(); - // let unwinder = &*UNWINDER; - - // let mut rip: u64; - // let mut rsp: u64; - // let mut rbp: u64; - // let mut read_stack_fn = read_stack; - - // unsafe { - // asm!("lea {0}, [rip]", - // "mov {1}, rsp", - // "mov {2}, rbp", - // out(reg) rip, - // out(reg) rsp, - // out(reg) rbp); - // } - - // let regs = UnwindRegsX86_64::new(rip, rsp, rbp); - // let mut frame_iter = - // unwinder.iter_frames(rip, regs, &mut cache, &mut read_stack_fn); - - // // This is just me testing the unwinding. It seems to not return any - // stack // frames for some odd reason. - // loop { - // match frame_iter.next() { - // Ok(Some(frame)) => { - // debugln!("Got function with {:x?}", - // frame.address_for_lookup()); // match frame { - // // FrameAddress::InstructionPointer(rip) => (), - // // FrameAddress::ReturnAddress(ra) => {} - // // } - // } - // Ok(None) => { - // debugln!("Hit end of stack."); - // break; - // } - // Err(why) => { - // debugln!("{}", why); - // break; - // } - // } - // } + let mut executor = Executor::new(); + executor.spawn(Task::new(shell())); + executor.run() } diff --git a/kernel/src/std/mod.rs b/kernel/src/std/mod.rs index 5305aee..918d009 100644 --- a/kernel/src/std/mod.rs +++ b/kernel/src/std/mod.rs @@ -1,7 +1,5 @@ pub mod application; pub mod ascii; pub mod debug; -// pub mod elf; pub mod io; pub mod maths; -// pub mod unwind; diff --git a/kernel/src/std/unwind/eh_info.rs b/kernel/src/std/unwind/eh_info.rs deleted file mode 100644 index 247fd55..0000000 --- a/kernel/src/std/unwind/eh_info.rs +++ /dev/null @@ -1,135 +0,0 @@ -//! Contains a [EhInfo] struct that contains the parsed DWARF exception header -//! data from the ELF .eh_frame and .eh_frame_hdr sections. - -use alloc::{boxed::Box, slice}; -use gimli::{ - BaseAddresses, EhFrame, EhFrameHdr, EhHdrTable, EndianSlice, LittleEndian, - ParsedEhFrameHdr, -}; -use spin::Lazy; - -use crate::{println_log, std::elf::ElfReader}; - -/// Contains useful data parsed from the ELF file in question. In the kernel -/// this will be our own process. -/// -/// We use this to implement stack traces and potential unwinding. -/// -/// # Sources -/// -/// This code is reproduced from [lesenchal.fr](https://lesenechal.fr/en/linux/unwinding-the-stack-the-hard-way#h5.1-parsing-eh_frame-and-eh_frame_hdr-with-gimli) -/// and will later be extended as required. -pub struct EhInfo { - /// A set of base addresses used for relative addressing. - pub base_addrs: BaseAddresses, - /// The parsed `.eh_frame_hdr` section. - pub hdr: &'static ParsedEhFrameHdr>, - /// The lookup table in the parsed `.eh_frame_hdr` section. - /// This is a binary search table, it is optional but should be present as - /// we are linking with LLD(?) \[needs citation]. - pub hdr_table: EhHdrTable<'static, EndianSlice<'static, LittleEndian>>, - /// The parsed `.eh_frame` containing the CFIs (call frame information). - pub eh_frame: EhFrame>, -} - -/// Stores the [ElfReader] struct for this ELF file. -pub static ELF: Lazy = - Lazy::new(|| unsafe { ElfReader::new().unwrap() }); - -impl EhInfo { - /// Gets the `.eh_frame_hdr` size in bytes. - /// - /// # Panics - /// - /// If we can't get the size of `.eh_frame_hdr`. - pub fn eh_frame_hdr_size() -> usize { - ELF.get_section_size(".eh_frame_hdr") - .expect("Cannot get size of `.eh_frame_hdr`.") as usize - } - - /// Gets the `.eh_frame` size in bytes. - /// - /// # Panics - /// - /// If we can't get the size of `.eh_frame`. - pub fn eh_frame_size() -> usize { - ELF.get_section_size(".eh_frame") - .expect("Cannot get size of `.eh_frame`.") as usize - } - - /// Constructs a [EhInfo] from the base address. This is defined for a - /// symbol in the linker script so we can initialise stack traces and -- in - /// the future -- unwinding. - /// - /// # Safety - /// - /// Assumes the `.eh_frame_hdr` pointer to be valid, as well as the sizes of - /// the containing sections. These sizes are computed using [ElfReader]. - /// - /// # Panics - /// - /// This function panics if Gimli throws parsing errors -- for example due - /// to a malformed or corrupted binary, or because this is called in - /// release-mode, or on a stripped binary. - /// - /// # TODOs - /// - /// * Support external System.map files which list symbols and contain - /// debugging information. - pub unsafe fn from_hdr_ptr(eh_frame_hdr: *const u8) -> Self { - let mut base_addrs = BaseAddresses::default(); - // We add the `.eh_frame_hdr` pointer to the set of base addresses which - // are used by Gimli for later parsing. This may be used to compute a - // pointer to `.eh_frame`. - base_addrs = base_addrs.set_eh_frame_hdr(eh_frame_hdr as u64); - - // Leaking the Box gives us a reference with `'static` lifetime to use - // in Self. - let hdr = Box::leak(Box::new( - // We need to construct a slice as input for `EhFrameHdr::new`. - // This is sound if data pointer and length are known to be - // correct. - EhFrameHdr::new( - unsafe { - core::slice::from_raw_parts( - eh_frame_hdr, - Self::eh_frame_hdr_size(), - ) - }, - LittleEndian, - ) // Parse the header using the base address we provided (virtual - // memory). Address size is how many bytes make an - // address (64 bits). - .parse(&base_addrs, 8) - .expect( - "Could not parse `.eh_frame_hdr`. The ELF must be malformed.", - ), - )); - - // Create a pointer to the `.eh_frame` ready to parse it. - let eh_frame = match hdr.eh_frame_ptr() { - gimli::Pointer::Direct(addr) => addr as *mut u8, - _ => unimplemented!(), - }; - - // Add the `.eh_frame` address for addresses relative to this section. - base_addrs = base_addrs.set_eh_frame(eh_frame as u64); - - // Finally parse the `.eh_frame` section of our ELF. - let eh_frame = EhFrame::new( - unsafe { - core::slice::from_raw_parts(eh_frame, Self::eh_frame_size()) - }, - LittleEndian, - ); - - Self { - base_addrs, - hdr, - hdr_table: hdr.table().expect( - "The CFI binary search table was not present in this binary, oh dear.", - ), - eh_frame, - } - } -} diff --git a/kernel/src/std/unwind/mod.rs b/kernel/src/std/unwind/mod.rs deleted file mode 100644 index d28ec2d..0000000 --- a/kernel/src/std/unwind/mod.rs +++ /dev/null @@ -1,31 +0,0 @@ -use core::arch::asm; - -use alloc::vec::Vec; -use framehop::{ - x86_64::{CacheX86_64, UnwinderX86_64}, - *, -}; -use spin::{Lazy, Mutex}; - -use crate::arch::x86_64::memory::mapping::KERNEL_ADDRESS_REQUEST; - -pub mod eh_info; -pub mod panic; -pub mod unwinder; - -/// We should initialise on program start. -pub static UNWINDER: Lazy>> = - Lazy::new(|| { - let mut unwinder: UnwinderX86_64<_, MayAllocateDuringUnwind> = - UnwinderX86_64::new(); - - panic::add_object( - &mut unwinder, - KERNEL_ADDRESS_REQUEST - .get_response() - .unwrap() - .virtual_base(), - ); - - unwinder - }); diff --git a/kernel/src/std/unwind/panic.rs b/kernel/src/std/unwind/panic.rs deleted file mode 100644 index df39bfa..0000000 --- a/kernel/src/std/unwind/panic.rs +++ /dev/null @@ -1,171 +0,0 @@ -//! Defines a simple panic handler which handles stack traces as required. - -use core::{arch::asm, ops::Range, panic::PanicInfo}; - -use alloc::{ - borrow::{Cow, ToOwned}, - slice, - string::{String, ToString}, - vec::Vec, -}; -use elf::segment::ProgramHeader; -use framehop::x86_64::{CacheX86_64, UnwinderX86_64}; -use framehop::{Unwinder, x86_64::UnwindRegsX86_64}; - -use crate::{ - arch::x86_64::memory::mapping::KERNEL_ADDRESS_REQUEST, - prelude::*, - std::{ - self, - elf::ElfReader, - unwind::{UNWINDER, eh_info::ELF}, - }, -}; - -#[panic_handler] -/// A basic panic handler which can produce a helpful stack trace. -pub fn panic_handler(info: &PanicInfo<'_>) -> ! { - debugln!("Kernel panic: {}", info); - - crate::hcf() -} - -use object::{Object, ObjectSection, ObjectSegment}; - -use framehop::*; - -pub fn add_object(unwinder: &mut U, base_avma: u64) -where - U: Unwinder>>, -{ - let mut buf = ELF.bytes; - - let file = object::File::parse(buf).expect("Could not parse object file"); - - struct Module<'a>(object::File<'a, &'a [u8]>); - - impl ModuleSectionInfo> for Module<'_> { - fn base_svma(&self) -> u64 { - relative_address_base(&self.0) - } - - fn section_svma_range(&mut self, name: &[u8]) -> Option> { - let section = self.0.section_by_name_bytes(name)?; - Some(section.address()..section.address() + section.size()) - } - - fn section_data(&mut self, name: &[u8]) -> Option> { - match self.0.section_by_name_bytes(name) { - Some(section) => { - section.data().ok().map(|data| data.to_owned()) - } - None if name == b".debug_frame" => { - let section = - self.0.section_by_name_bytes(b"__zdebug_frame")?; - get_uncompressed_section_data(§ion) - .map(|d| d.into_owned()) - } - None => None, - } - } - - fn segment_svma_range(&mut self, name: &[u8]) -> Option> { - let segment = self - .0 - .segments() - .find(|s| s.name_bytes() == Ok(Some(name)))?; - Some(segment.address()..segment.address() + segment.size()) - } - - fn segment_data(&mut self, name: &[u8]) -> Option> { - let segment = self - .0 - .segments() - .find(|s| s.name_bytes() == Ok(Some(name)))?; - segment.data().ok().map(|data| data.to_owned()) - } - } - - let module = framehop::Module::new( - "Kernel".to_string(), - base_avma..(base_avma + buf.len() as u64), - base_avma, - Module(file), - ); - unwinder.add_module(module); -} - -fn get_uncompressed_section_data<'a>( - section: &impl object::ObjectSection<'a>, -) -> Option> { - // let section_data = section.uncompressed_data().ok()?; - - // // Make sure the data is actually decompressed. - // if section.name_bytes().ok()?.starts_with(b"__zdebug_") - // && section_data.starts_with(b"ZLIB\0\0\0\0") - // { - // // Object's built-in compressed section handling didn't detect this - // as a // compressed section. This happens on Go binaries which use - // compressed // sections like __zdebug_ranges, which is generally - // uncommon on macOS, // so object's mach-O parser doesn't handle - // them. // But we want to handle them. - // // Go stopped using zdebug sections for ELF files in https://github.com/golang/go/issues/50796 - // // but still uses them for mach-O builds. - // let b = section_data.get(8..12)?; - // let uncompressed_size = u32::from_be_bytes([b[0], b[1], b[2], b[3]]); - // let compressed_bytes = §ion_data[12..]; - - // let mut decompressed = Vec::with_capacity(uncompressed_size as - // usize); let mut decompress = flate2::Decompress::new(true); - // decompress - // .decompress_vec( - // compressed_bytes, - // &mut decompressed, - // flate2::FlushDecompress::Finish, - // ) - // .ok()?; - // Some(Cow::Owned(decompressed)) - // } else { - // Some(section_data) - // } - - todo!() -} - -/// Relative addresses are u32 offsets which are relative to some "base -/// address". -/// -/// This function computes that base address. It is defined as follows: -/// -/// - For Windows binaries, the base address is the "image base address". -/// - For mach-O binaries, the base address is the vmaddr of the __TEXT -/// segment. -/// - For ELF binaries, the base address is zero. -/// -/// Stand-alone mach-O dylibs usually have a base address of zero because their -/// __TEXT segment is at address zero. -/// -/// In the following cases, the base address is usually non-zero: -/// -/// - The "image base address" of Windows binaries is usually non-zero. -/// - mach-O executable files (not dylibs) usually have their __TEXT segment at -/// address 0x100000000. -/// - mach-O libraries in the dyld shared cache have a __TEXT segment at some -/// non-zero address in the cache. -pub fn relative_address_base<'data>( - object_file: &impl object::Object<'data>, -) -> u64 { - if let Some(text_segment) = object_file - .segments() - .find(|s| s.name() == Ok(Some("__TEXT"))) - { - // This is a mach-O image. "Relative addresses" are relative to the - // vmaddr of the __TEXT segment. - return text_segment.address(); - } - - // For PE binaries, relative_address_base() returns the image base address. - // Otherwise it returns zero. This gives regular ELF images a base address - // of zero, which is what we want. - object_file.relative_address_base() -} diff --git a/kernel/src/std/unwind/unwinder.rs b/kernel/src/std/unwind/unwinder.rs deleted file mode 100644 index 7a4929a..0000000 --- a/kernel/src/std/unwind/unwinder.rs +++ /dev/null @@ -1,229 +0,0 @@ -//! Implements the core stack unwinding logic. -//! -//! # TODOs -//! -//! * Support evaluation of DWARF expressions, this might not be required -//! however, because Rust doesn't tend to use this DWARF feature. - -use fallible_iterator::FallibleIterator; -use gimli::{ - CfaRule, EndianSlice, LittleEndian, Register, RegisterRule, StoreOnHeap, - UnwindContext, UnwindSection, X86_64, -}; - -use super::eh_info::EhInfo; - -/// Implements the core stack unwinding logic by parsing the call frame -/// information. This also stores current (DWARF) register values. -/// -/// # Sources -/// -/// Taken from [lesenechal.fr](https://lesenechal.fr/en/linux/unwinding-the-stack-the-hard-way) -/// with some additional features to be added soon. -pub struct Unwinder { - /// The call frame information. - eh_info: EhInfo, - /// An [UnwindContext] used by Gimli for optimisations. - unwind_ctx: UnwindContext, - /// The current values of ABI/architecture independent registers. There are - /// used by DWARF. - pub regs: RegisterSet, - /// The current CFA address. - cfa: u64, - /// Is this the first iteration? - is_first: bool, -} - -// TODO: Use map_err et al. -impl FallibleIterator for Unwinder { - type Item = CallFrame; - type Error = UnwinderError; - - /// Returns call frames of calling functions. This may be called to produce - /// a stack trace or otherwise support physical unwinding. - fn next(&mut self) -> Result, Self::Error> { - // Gets the current program counter from the DWARF register set. - let Some(pc) = self.regs.get_pc() else { - return Err(UnwinderError::NoPcRegister); - }; - // 0xffffffff8000014b b - 7 = 11 - 7 = 4 - - if self.is_first { - self.is_first = false; - - return Ok(Some(CallFrame { pc, symbol: 0 })); - } - - // This is a row in the virtual unwind table AKA the CFI which will help - // us find the CFA (canonical frame address) for a given program - // counter. - let Ok(row) = self.eh_info.hdr_table.unwind_info_for_address( - &self.eh_info.eh_frame, - &self.eh_info.base_addrs, - &mut self.unwind_ctx, - pc, - |section, bases, offset| - // Finds a DWARF CIE using an offset as given. - section.cie_from_offset(bases, offset), - ) else { - return Err(UnwinderError::NoUnwindInfo); - }; - - // We compute the CFA (canonical frame address from its rule). - // TODO: Support other rules such as DWARF expressions. - match row.cfa() { - CfaRule::RegisterAndOffset { register, offset } => { - let Some(reg_val) = self.regs.get(*register) else { - return Err(UnwinderError::CfaRuleUnknownRegister( - *register, - )); - }; - - self.cfa = (reg_val as i64 + offset) as u64; - } - - // TODO: Support other rules for computing the CFA. - _ => return Err(UnwinderError::UnsupportedCfaRule), - } - - for reg in RegisterSet::iter() { - match row.register(reg) { - RegisterRule::Undefined => self.regs.undef(reg), - RegisterRule::SameValue => (), - RegisterRule::Offset(offset) => { - // Adds the given offset to the register contents and - // retrieve the value from the stack at address CFA + - // offset. - let ptr = (self.cfa as i64 + offset) as u64 as *const usize; - - self.regs.set(reg, unsafe { ptr.read() } as u64)? - } - - _ => { - return Err(UnwinderError::UnimplementedRegisterRule); - } - } - } - - // Get the new value for %rip from the function return value and - // subtract one from it, because the address actually points the the - // next instruction after the call. - let Some(pc) = self.regs.get_ret() else { - return Err(UnwinderError::NoReturnAddr); - }; - - // REVIEWME: Must be a nicer way of doing this. - let Some(pc) = pc.checked_sub(1) else { - // REVIEWME: This should handle underflow now. - return Ok(None); - }; - - self.regs.set_pc(pc); - - // Set %rsp to the CFA. This simulates returning from the function, - // destroying the call frame, so we were able to virtually unwind (to - // the caller function). - self.regs.set_stack_ptr(self.cfa); - - Ok(Some(CallFrame { pc, symbol: 0 })) - } -} - -impl Unwinder { - pub fn new(eh_info: EhInfo, regset: RegisterSet) -> Self { - Self { - eh_info, - regs: regset, - unwind_ctx: UnwindContext::new(), - cfa: 0, - is_first: true, - } - } -} - -/// The set of registers used by DWARF. This struct allows future portability if -/// we want to target other architectures than x86_64. -#[derive(Debug, Default)] -pub struct RegisterSet { - rip: Option, - rsp: Option, - rbp: Option, - /// The return address register. - ret: Option, -} - -impl RegisterSet { - pub const fn get(&self, reg: Register) -> Option { - match reg { - X86_64::RSP => self.rsp, - X86_64::RBP => self.rbp, - X86_64::RA => self.ret, - _ => None, - } - } - - pub const fn set( - &mut self, - reg: Register, - val: u64, - ) -> Result<(), UnwinderError> { - *match reg { - X86_64::RSP => &mut self.rsp, - X86_64::RBP => &mut self.rbp, - X86_64::RA => &mut self.ret, - _ => return Err(UnwinderError::UnexpectedRegister(reg)), - } = Some(val); - - Ok(()) - } - - const fn undef(&mut self, reg: Register) { - *match reg { - X86_64::RSP => &mut self.rsp, - X86_64::RBP => &mut self.rbp, - X86_64::RA => &mut self.ret, - _ => return, - } = None; - } - - const fn get_pc(&self) -> Option { - self.rip - } - - pub const fn set_pc(&mut self, val: u64) { - self.rip = Some(val); - } - - const fn get_ret(&self) -> Option { - self.ret - } - - pub const fn set_stack_ptr(&mut self, val: u64) { - self.rsp = Some(val); - } - - fn iter() -> impl Iterator { - [X86_64::RSP, X86_64::RBP, X86_64::RA].into_iter() - } -} - -/// The current instruction pointer. -#[derive(Debug)] -pub struct CallFrame { - /// The current instruction pointer. - pub pc: u64, - /// The symbol of the function. - pub symbol: usize, -} - -#[derive(Debug)] -/// A list of errors that could occur whilst unwinding the stack. -pub enum UnwinderError { - UnexpectedRegister(Register), - UnsupportedCfaRule, - UnimplementedRegisterRule, - CfaRuleUnknownRegister(Register), - NoUnwindInfo, - NoPcRegister, - NoReturnAddr, -}