Clean up dead code

This commit is contained in:
2025-03-08 18:43:43 +00:00
parent da6690fd8b
commit a83108a08e
7 changed files with 5 additions and 656 deletions
-5
View File
@@ -149,10 +149,5 @@ pub fn boot() -> Result<(), Box<dyn core::error::Error>> {
x86_64::instructions::interrupts::enable();
debugln!("[Success]");
debug!(" Initializing Stack Unwinder... ");
// Force evaluate the constructor.
// let _unwinder = &*UNWINDER;
debugln!("[Success]");
Ok(())
}
+5 -83
View File
@@ -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<u64, ()> {
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()
}
-2
View File
@@ -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;
-135
View File
@@ -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<EndianSlice<'static, LittleEndian>>,
/// 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<EndianSlice<'static, LittleEndian>>,
}
/// Stores the [ElfReader] struct for this ELF file.
pub static ELF: Lazy<ElfReader> =
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,
}
}
}
-31
View File
@@ -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<framehop::x86_64::UnwinderX86_64<Vec<u8>>> =
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
});
-171
View File
@@ -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<U>(unwinder: &mut U, base_avma: u64)
where
U: Unwinder<Module = Module<Vec<u8>>>,
{
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<Vec<u8>> for Module<'_> {
fn base_svma(&self) -> u64 {
relative_address_base(&self.0)
}
fn section_svma_range(&mut self, name: &[u8]) -> Option<Range<u64>> {
let section = self.0.section_by_name_bytes(name)?;
Some(section.address()..section.address() + section.size())
}
fn section_data(&mut self, name: &[u8]) -> Option<Vec<u8>> {
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(&section)
.map(|d| d.into_owned())
}
None => None,
}
}
fn segment_svma_range(&mut self, name: &[u8]) -> Option<Range<u64>> {
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<Vec<u8>> {
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<Cow<'a, [u8]>> {
// 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 = &section_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()
}
-229
View File
@@ -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<usize, StoreOnHeap>,
/// 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<Option<Self::Item>, 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<u64>,
rsp: Option<u64>,
rbp: Option<u64>,
/// The return address register.
ret: Option<u64>,
}
impl RegisterSet {
pub const fn get(&self, reg: Register) -> Option<u64> {
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<u64> {
self.rip
}
pub const fn set_pc(&mut self, val: u64) {
self.rip = Some(val);
}
const fn get_ret(&self) -> Option<u64> {
self.ret
}
pub const fn set_stack_ptr(&mut self, val: u64) {
self.rsp = Some(val);
}
fn iter() -> impl Iterator<Item = Register> {
[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,
}