#[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: "); for b in unsafe {*(std::ptr::addr_of!(self.magic) as *const [u8; 16])} { formatter.pad(&format!("{:02x?} ", b)); } formatter.write_str("\n"); formatter.write_fmt(format_args!(" Class: {:?}\n", self.class)); formatter.write_fmt(format_args!(" Data: {}\n", self.data)); formatter.write_fmt(format_args!(" Version: {} ({})\n", self.version, match self.version { 0 => "invalid", 1 => "current", 2_u8..=u8::MAX => todo!(), // need to cover all cases })); formatter.write_fmt(format_args!(" OS/ABI: {}\n", self.OSABI)); formatter.write_fmt(format_args!(" ABI Version: {}\n", 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 { // yes, this is really what they do in readelf.c formatter.write_fmt(format_args!("{}", self.ident)); formatter.write_fmt(format_args!(" Type: {}\n", self.r#type)); formatter.write_fmt(format_args!(" Machine: {}\n", self.machine)); formatter.write_fmt(format_args!(" Version: {:#02x?}\n", self.version)); formatter.write_fmt(format_args!(" Entry point address: {:#x?}\n", self.entry)); formatter.write_fmt(format_args!(" Start of program headers: {} (bytes into file)\n", self.phoff)); formatter.write_fmt(format_args!(" Start of section headers: {} (bytes into file)\n", self.shoff)); formatter.write_fmt(format_args!(" Flags: {:#x?}\n", self.flags)); formatter.write_fmt(format_args!(" Size of this header: {} (bytes)\n", self.ehsize)); formatter.write_fmt(format_args!(" Size of program headers: {} (bytes)\n", self.phentsize)); formatter.write_fmt(format_args!(" Number of program headers: {}\n", self.phnum)); formatter.write_fmt(format_args!(" Size of section headers: {} (bytes)\n", self.shentsize)); formatter.write_fmt(format_args!(" Number of section headers: {}\n", self.shnum)); formatter.write_fmt(format_args!(" Section header string table index: {}\n", 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"), } } }