Clean up dead code
This commit is contained in:
@@ -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
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
});
|
||||
@@ -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(§ion)
|
||||
.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 = §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()
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
Reference in New Issue
Block a user