use core::fmt; use std::{char, env, fs}; fn main() { let mut args = env::args().skip(1); let path = args.next().unwrap(); let bytes = fs::read(path).unwrap(); let elf = Elf::parse(&bytes).unwrap(); } #[derive(Clone)] struct InputBytes<'a> { bytes: &'a [u8], } impl<'a> InputBytes<'a> { fn new(bytes: &'a [u8]) -> Self { Self { bytes } } fn new_offset(bytes: &'a [u8], offset: usize) -> Self { Self::new(bytes).advanced(offset) } fn prefix_len(&self, n: usize) -> usize { self.bytes.len().min(n) } fn advanced(&self, n: usize) -> Self { Self { bytes: &self.bytes[self.prefix_len(n)..], } } fn advance(&mut self, n: usize) -> &mut Self { self.bytes = &self.bytes[self.prefix_len(n)..]; self } fn peek(&self, n: usize) -> Result<&'a [u8], ParseError> { self.bytes.get(..n).ok_or(ParseError::UnexpectedEof) } fn pop(&mut self, n: usize) -> Result<&'a [u8], ParseError> { let res = self.peek(n); if res.is_ok() { self.advance(n); } res } fn pop_arr(&mut self) -> Result<[u8; N], ParseError> { self.pop(N).map(|bs| <[u8; N]>::try_from(bs).unwrap()) } fn pop_u8(&mut self) -> Result { self.pop_arr::<1>().map(|[x]| x) } fn pop_u16(&mut self, endianness: Endianness) -> Result { let bytes = self.pop_arr::<2>()?; Ok(endianness.read_u16(bytes)) } fn pop_u32(&mut self, endianness: Endianness) -> Result { let bytes = self.pop_arr::<4>()?; Ok(endianness.read_u32(bytes)) } fn pop_u64(&mut self, endianness: Endianness) -> Result { let bytes = self.pop_arr::<8>()?; Ok(endianness.read_u64(bytes)) } fn pop_word(&mut self, endianness: Endianness, width: Width) -> Result { match width { Width::Bits32 => self.pop_u32(endianness).map(u64::from), Width::Bits64 => self.pop_u64(endianness), } } fn pop_word_usize(&mut self, endianness: Endianness, width: Width) -> Result { self.pop_word(endianness, width) .and_then(|x| usize::try_from(x).map_err(|_| ParseError::OffsetOutOfBounds)) } fn hexdump(&self, n: usize) { print!("{: <8}", ""); for i in 0..16 { print!(" {:x}", i); } println!(); for (i, chunk) in self.bytes[.. n.min(self.bytes.len())].chunks(16).enumerate() { print!("{:08x}", i * 16); for b in chunk { print!(" {:02x}", b); } print!("{: <1$}", "", 4 + 3 * (16 - chunk.len())); for b in chunk { let c = match char::from(*b) { c if c.is_ascii_graphic() => c, _ => '.', }; print!("{}", c); } println!(); } } } struct Elf<'a> { elf_bytes: &'a [u8], bl: ByteLayout, header_version: u8, abi: u8, ty: ElfType, instruction_set: Option, elf_version: u32, } impl<'a> Elf<'a> { fn parse(bytes: &'a [u8]) -> Result { let mut hdr = InputBytes::new(bytes); hdr.hexdump(64); let magic = hdr.pop(4)?; if magic != &[0x7f, b'E', b'L', b'F'] { return Err(ElfError::BadMagic); } let width = match hdr.pop_u8()? { 1 => Width::Bits32, 2 => Width::Bits64, _ => return Err(ElfError::UnsupportedValue), }; let endianness = match hdr.pop_u8()? { 1 => Endianness::Little, 2 => Endianness::Big, _ => return Err(ElfError::UnsupportedValue), }; let bl = ByteLayout { endianness, width, }; let header_version = hdr.pop_u8()?; let abi = hdr.pop_u8()?; hdr.pop(8)?; let ty = match hdr.pop_u16(endianness)? { 1 => ElfType::Relocatable, 2 => ElfType::Executable, 3 => ElfType::Shared, 4 => ElfType::Core, _ => return Err(ElfError::UnsupportedValue), }; let instruction_set = match hdr.pop_u16(endianness)? { 0 => None, 0x02 => Some(InstructionSet::Sparc), 0x03 => Some(InstructionSet::I386), 0x08 => Some(InstructionSet::Mips), 0x14 => Some(InstructionSet::PowerPc), 0x28 => Some(InstructionSet::Arm), 0x2a => Some(InstructionSet::SuperH), 0x32 => Some(InstructionSet::Ia64), 0x3e => Some(InstructionSet::Amd64), 0xb7 => Some(InstructionSet::Aarch64), 0xf3 => Some(InstructionSet::RiscV), _ => return Err(ElfError::UnsupportedValue), }; let elf_version = hdr.pop_u32(endianness)?; let entry_offset = hdr.pop_word(endianness, width)?; let pht_offset = hdr.pop_word_usize(endianness, width)?; let sht_offset = hdr.pop_word_usize(endianness, width)?; let flags = hdr.pop_u32(endianness)?; let header_size = hdr.pop_u16(endianness)?; let pht_entry_size = hdr.pop_u16(endianness)?; let pht_len = hdr.pop_u16(endianness)?; let sht_entry_size = hdr.pop_u16(endianness)?; let sht_len = hdr.pop_u16(endianness)?; let shstr_idx = hdr.pop_u16(endianness)?; dbg!(entry_offset); dbg!(pht_offset); dbg!(sht_offset); dbg!(flags); dbg!(header_size); dbg!(pht_entry_size); dbg!(pht_len); dbg!(sht_entry_size); dbg!(sht_len); dbg!(shstr_idx); // TODO let shstr = SectionHeader::parse(bl, InputBytes::new_offset(bytes, sht_offset + usize::from(sht_entry_size * shstr_idx)))?; let str_table = InputBytes::new_offset(bytes, shstr.offset); str_table.hexdump(shstr.size); Ok(Self { elf_bytes: bytes, bl, header_version, abi, ty, instruction_set, elf_version, }) } } struct SectionHeader { name: u32, // FIXME: parse type ty: u32, flags: u64, addr: u64, offset: usize, size: usize, link: u32, info: u32, align: u64, ent_size: usize, } impl SectionHeader { fn parse(bl: ByteLayout, mut bytes: InputBytes) -> Result { let name = bytes.pop_u32(bl.endianness)?; let ty = bytes.pop_u32(bl.endianness)?; let flags = bytes.pop_word(bl.endianness, bl.width)?; let addr = bytes.pop_word(bl.endianness, bl.width)?; let offset = bytes.pop_word_usize(bl.endianness, bl.width)?; let size = bytes.pop_word_usize(bl.endianness, bl.width)?; let link = bytes.pop_u32(bl.endianness)?; let info = bytes.pop_u32(bl.endianness)?; let align = bytes.pop_word(bl.endianness, bl.width)?; let ent_size = bytes.pop_word_usize(bl.endianness, bl.width)?; Ok(Self { name, ty, flags, addr, offset, size, link, info, align, ent_size, }) } } #[derive(Clone, Copy, PartialEq, Eq, Debug)] struct ByteLayout { endianness: Endianness, width: Width, } #[derive(Clone, Copy, PartialEq, Eq, Debug)] enum Width { Bits32, Bits64, } #[derive(Clone, Copy, PartialEq, Eq, Debug)] enum Endianness { Little, Big, } impl Endianness { fn read_u16(self, bytes: [u8; 2]) -> u16 { match self { Self::Little => u16::from_le_bytes(bytes), Self::Big => u16::from_be_bytes(bytes), } } fn read_u32(self, bytes: [u8; 4]) -> u32 { match self { Self::Little => u32::from_le_bytes(bytes), Self::Big => u32::from_be_bytes(bytes), } } fn read_u64(self, bytes: [u8; 8]) -> u64 { match self { Self::Little => u64::from_le_bytes(bytes), Self::Big => u64::from_be_bytes(bytes), } } } #[derive(Clone, Copy, PartialEq, Eq, Debug)] enum ElfType { Relocatable, Executable, Shared, Core, } #[derive(Clone, Copy, PartialEq, Eq, Debug)] enum InstructionSet { Sparc, I386, Mips, PowerPc, Arm, SuperH, Ia64, Amd64, Aarch64, RiscV, } #[derive(Debug)] enum ElfError { BadMagic, UnexpectedEof, OffsetOutOfBounds, UnsupportedValue, } impl From for ElfError { fn from(value: ParseError) -> Self { match value { ParseError::UnexpectedEof => Self::UnexpectedEof, ParseError::OffsetOutOfBounds => Self::OffsetOutOfBounds, } } } #[derive(Debug)] enum ParseError { UnexpectedEof, OffsetOutOfBounds, }