#[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct ElfIdent { /// Magic number - 0x7F, then 'ELF' in ASCII pub magic: [u8; 4], /// 1 = 32 bit, 2 = 64 bit pub class: Class, // 1 = little endian, 2 = big endian pub data: Endian, /// ELF header version pub version: u8, /// OS ABI - usually 0 for System V pub OSABI: OSABI, // This field is used to distinguish among incompatible versions of an ABI. pub ABIVersion: u8, /// Unused/padding pub _unused: [u8; 7], } impl std::fmt::Display for ElfIdent { fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("Magic:\t"); for b in unsafe {*(std::ptr::addr_of!(self) as *const [u8; 16])} { formatter.pad(&format!("{:02x?} ", b)); } formatter.write_fmt(format_args!("\nClass:\t{:?}", self.class)); formatter.write_fmt(format_args!("\nData:\t{}", self.data)); formatter.write_fmt(format_args!("\nVersion:\t{} ({})", self.version, match self.version { 0 => "invalid", 1 => "current", 2_u8..=u8::MAX => todo!(), // need to cover all cases })); formatter.write_fmt(format_args!("\nOS/ABI:\t{}", self.OSABI)); formatter.write_fmt(format_args!("\nABI Verscion:\t{}", self.ABIVersion)) } } // https://man7.org/linux/man-pages/man5/elf.5.html // https://wiki.osdev.org/ELF // https://en.wikipedia.org/wiki/Executable_and_Linkable_Format #[repr(C)] #[derive(Debug)] pub struct ElfHeader { pub ident: ElfIdent, /// 1 = relocatable, 2 = executable, 3 = shared, 4 = core pub r#type: Type, /// Instruction set - see table below pub machine: Machine, /// ELF Version pub version: u32, /// Program entry position (virtual) pub entry: u64, /// Program header table position (bytes into file) pub phoff: u64, /// Section header table position (bytes into file) pub shoff: u64, /// This member holds processor-specific flags associated with the file /// Currently, no flags have been defined. pub flags: u32, /// Header size pub ehsize: u16, /// Size of an entry in the program header table pub phentsize: u16, /// Number of entries in the program header table pub phnum: u16, /// Size of an entry in the section header table pub shentsize: u16, /// Number of entries in the section header table pub shnum: u16, /// Index in section header table with the section names pub shstrndx: u16, } impl std::fmt::Display for ElfHeader { fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_fmt(format_args!("{}", self.ident)); formatter.write_fmt(format_args!("\nType:\t{}", self.r#type)); formatter.write_fmt(format_args!("\nMachine:\t{}", self.machine)); formatter.write_fmt(format_args!("\nVersion:\t{:#02x?}", self.version)); formatter.write_fmt(format_args!("\nEntry point address:\t{:#x?}", self.entry)); formatter.write_fmt(format_args!("\nStart of program headers:\t{} (bytes into file)", self.phoff)); formatter.write_fmt(format_args!("\nStart of section headers:\t{} (bytes into file)", self.shoff)); formatter.write_fmt(format_args!("\nFlags:\t{:#x?}", self.flags)); formatter.write_fmt(format_args!("\nSize of this header:\t{} (bytes)", self.ehsize)); formatter.write_fmt(format_args!("\nSize of program headers:\t{} (bytes)", self.phentsize)); formatter.write_fmt(format_args!("\nNumber of program headers:\t{}", self.phnum)); formatter.write_fmt(format_args!("\nSize of section headers:\t{} (bytes)", self.shentsize)); formatter.write_fmt(format_args!("\nNumber of section headers:\t{}", self.shnum)); formatter.write_fmt(format_args!("\nSection header string table index:\t{}", self.shstrndx)) } } #[derive(Debug, Copy, Clone)] #[repr(u16)] pub enum Type { None = 0, Rel = 1, Exec = 2, Dyn = 3, Core = 4, } impl std::fmt::Display for Type { fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { match self { Type::None => formatter.write_str("NONE (None)"), Type::Rel => formatter.write_str("REL (Relocatable file)"), Type::Exec => formatter.write_str("EXEC (Executable file)"), Type::Dyn => formatter.write_str("DYN (Shared object file)"), Type::Core => formatter.write_str("CORE (Core file)"), } } } #[derive(Debug, Copy, Clone)] #[repr(u16)] pub enum Machine { // there are many many many more, im just gonna do x86 // https://github.com/bminor/binutils-gdb/blob/master/binutils/readelf.c#L2746 None = 1, X86_64 = 62, } impl std::fmt::Display for Machine { fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { match self { Machine::None => formatter.write_str("None"), Machine::X86_64 => formatter.write_str("Advanced Micro Devices X86-64"), } } } #[derive(Debug, Copy, Clone)] #[repr(u8)] pub enum Class { ELF32 = 1, ELF64 = 2, } #[derive(Debug, Copy, Clone)] #[repr(u8)] pub enum OSABI { None = 0x00, SystemV = 0x01, // HPUX = 0x02, // Solaris = 0x03, // IRIX = 0x04, // FreeBSD = 0x05, // TRU64 = 0x06, // ARM = 0x07, Standalone = 0x08, } impl std::fmt::Display for OSABI { fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { match self { OSABI::None => formatter.write_str("UNIX - System V"), OSABI::SystemV => formatter.write_str("UNIX - System V"), OSABI::Standalone => formatter.write_str("Standalone"), } } } #[derive(Debug, Copy, Clone)] #[repr(u8)] pub enum Endian { Little = 1, Big = 2, } impl std::fmt::Display for Endian { fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { match self { Endian::Little => formatter.write_str("2's complement, little endian"), Endian::Big => formatter.write_str("1's complement, big endian"), } } }