//! Basic ELF parsing functionality using the `elf` crate. //! //! This may be extended in the future to support loading programs, however we //! currently use this for getting the sizes of sections in our kernel ELF at //! runtime. //! //! This is used for implementing stacktraces in std::unwind. //! //! # TODO //! //! * Add support for loading binary programs (this should probably be written //! in a different module) use alloc::format; use elf::{ ElfBytes, ParseError, endian::LittleEndian, parse::{ParseAt, ParsingTable}, section::{SectionHeader, SectionHeaderTable}, string_table::StringTable, symbol::SymbolTable, }; use limine::request::KernelFileRequest; use crate::prelude::*; #[cfg(target_arch = "x86_64")] /// The length of the ELF header in bytes. pub const ELF_HEADER_LEN: usize = 64; /// Information about our own ELF file to make ELF parsing easier, such as the /// length of the file and a pointer to the contents. #[used] pub static KERNEL_FILE_REQUEST: KernelFileRequest = KernelFileRequest::new(); /// A list of errors that may occur when parsing ELF files. #[derive(Debug)] pub enum ElfError { /// Returned if a section did not exist in [ElfReader::get_section_size]. SectionNotExists, /// Parse errors returned by the `elf` crate. OtherParseError(elf::ParseError), } impl From for ElfError { fn from(err: elf::ParseError) -> Self { Self::OtherParseError(err) } } pub struct ElfReader { /// Structure returned by the `elf` crate having parsed the ELF header. elf: ElfBytes<'static, LittleEndian>, } impl ElfReader { /// Parses the ELF file for the kernel, this uses data from Limine's Kernel /// File Request to get a slice over the whole executable file. /// /// # Safety /// /// Assumes a properly formed ELF file, and that Limine returns a correct /// pointer to the start of the file as well as a valid length in bytes. /// /// Both of these should be satisfied, but this function is marked unsafe /// just in case, because we are derefererencing arbitrary pointers. pub unsafe fn new() -> Result { let response = KERNEL_FILE_REQUEST .get_response() .expect("Didn't get the kernel file from Limine. That's odd."); // We fetch these from Limine and use them to parse our own ELF file. let file = response.file(); let file_start_ptr = file.addr(); let file_size = file.size() as usize; // Safety: This slice should contain the whole bytes of the ELF file. let elf_hdr_slice = unsafe { core::slice::from_raw_parts(file_start_ptr, file_size) }; let elf: ElfBytes<'static, LittleEndian> = elf::ElfBytes::minimal_parse(elf_hdr_slice)?; Ok(Self { elf }) } #[warn(clippy::unwrap_used)] pub fn get_symbol_table( &self, ) -> Result< Option<(SymbolTable<'static, LittleEndian>, StringTable<'static>)>, ElfError, > { // TODO: Remove .unwrap(). Ok(self.elf.symbol_table().unwrap()) } /// Gets the section size of `section_name` in bytes. pub fn get_section_size( &self, section_name: &'static str, ) -> Result { Ok(self.get_section_header(section_name)?.sh_size) } /// Gets the start address of the section `section_name` in memory. pub fn get_section_addr( &self, section_name: &'static str, ) -> Result<*const u8, ElfError> { Ok(self.get_section_header(section_name)?.sh_addr as *const u8) } /// Gets the section header of `section_name`. pub fn get_section_header( &self, section_name: &'static str, ) -> Result { let section_hdr = self .elf .section_header_by_name(section_name) .map_err(|_e| ElfError::SectionNotExists)?; section_hdr.ok_or(ElfError::SectionNotExists) } }