//! 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, } } }