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();
|
x86_64::instructions::interrupts::enable();
|
||||||
debugln!("[Success]");
|
debugln!("[Success]");
|
||||||
|
|
||||||
debug!(" Initializing Stack Unwinder... ");
|
|
||||||
// Force evaluate the constructor.
|
|
||||||
// let _unwinder = &*UNWINDER;
|
|
||||||
debugln!("[Success]");
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-83
@@ -3,18 +3,11 @@
|
|||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
use core::arch::asm;
|
|
||||||
|
|
||||||
use foundry_os::{
|
use foundry_os::{
|
||||||
// arch::x86_64::processing::async_io::task::{Executor, Task},
|
arch::x86_64::processing::async_io::task::{Executor, Task},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
// std::unwind::UNWINDER,
|
util::shell::shell,
|
||||||
// util::shell::shell,
|
|
||||||
};
|
};
|
||||||
// use framehop::{
|
|
||||||
// x86_64::{CacheX86_64, UnwindRegsX86_64},
|
|
||||||
// *,
|
|
||||||
// };
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
extern "C" fn kmain() -> ! {
|
extern "C" fn kmain() -> ! {
|
||||||
@@ -22,80 +15,9 @@ extern "C" fn kmain() -> ! {
|
|||||||
if let Err(err) = foundry_os::boot() {
|
if let Err(err) = foundry_os::boot() {
|
||||||
panic!("{}", err);
|
panic!("{}", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
println_log!(" [ Kernel Initialised Successfully ] ");
|
println_log!(" [ Kernel Initialised Successfully ] ");
|
||||||
|
|
||||||
// println!("TESTING :: Allocation");
|
let mut executor = Executor::new();
|
||||||
// let somevec = vec![0; 1_000_000];
|
executor.spawn(Task::new(shell()));
|
||||||
// println!("{:?}", somevec);
|
executor.run()
|
||||||
// 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;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
pub mod application;
|
pub mod application;
|
||||||
pub mod ascii;
|
pub mod ascii;
|
||||||
pub mod debug;
|
pub mod debug;
|
||||||
// pub mod elf;
|
|
||||||
pub mod io;
|
pub mod io;
|
||||||
pub mod maths;
|
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