b26dc6de01
Probably incorrect PC was set.
136 lines
4.9 KiB
Rust
136 lines
4.9 KiB
Rust
//! 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,
|
|
}
|
|
}
|
|
}
|