diff --git a/elf/src/main.rs b/elf/src/main.rs index df27015..c901f59 100644 --- a/elf/src/main.rs +++ b/elf/src/main.rs @@ -13,59 +13,79 @@ fn main() { #[derive(Clone)] struct InputBytes<'a> { bytes: &'a [u8], - offset: usize, } impl<'a> InputBytes<'a> { fn new(bytes: &'a [u8]) -> Self { - Self { bytes, offset: 0 } + Self { bytes } + } + + fn new_offset(bytes: &'a [u8], offset: usize) -> Self { + Self::new(bytes).advanced(offset) } - fn offset(&self) -> usize { - self.offset + fn prefix_len(&self, n: usize) -> usize { + self.bytes.len().min(n) } - fn advance_offset(&self, n: usize) -> usize { - self.bytes.len().min(self.offset + n) + fn advanced(&self, n: usize) -> Self { + Self { + bytes: &self.bytes[self.prefix_len(n)..], + } } fn advance(&mut self, n: usize) -> &mut Self { - self.offset = self.advance_offset(n); + self.bytes = &self.bytes[self.prefix_len(n)..]; self } - fn peek(&self, n: usize) -> Option<&'a [u8]> { - self.bytes.get(self.offset .. (self.offset + n)) + fn peek(&self, n: usize) -> Result<&'a [u8], ParseError> { + self.bytes.get(..n).ok_or(ParseError::UnexpectedEof) } - fn pop(&mut self, n: usize) -> Option<&'a [u8]> { + fn pop(&mut self, n: usize) -> Result<&'a [u8], ParseError> { let res = self.peek(n); - self.advance(n); + if res.is_ok() { + self.advance(n); + } res } - fn pop_u16(&mut self, endianness: Endianness) -> Option { - let bytes = self.pop(2).and_then(|bytes| <[u8; 2]>::try_from(bytes).ok())?; - Some(endianness.read_u16(bytes)) + 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_u32(&mut self, endianness: Endianness) -> Option { - let bytes = self.pop(4).and_then(|bytes| <[u8; 4]>::try_from(bytes).ok())?; - Some(endianness.read_u32(bytes)) + fn pop_u16(&mut self, endianness: Endianness) -> Result { + let bytes = self.pop_arr::<2>()?; + Ok(endianness.read_u16(bytes)) } - fn pop_u64(&mut self, endianness: Endianness) -> Option { - let bytes = self.pop(8).and_then(|bytes| <[u8; 8]>::try_from(bytes).ok())?; - Some(endianness.read_u64(bytes)) + fn pop_u32(&mut self, endianness: Endianness) -> Result { + let bytes = self.pop_arr::<4>()?; + Ok(endianness.read_u32(bytes)) } - fn pop_word(&mut self, endianness: Endianness, width: Width) -> Option { + 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 { @@ -91,9 +111,8 @@ impl<'a> InputBytes<'a> { } struct Elf<'a> { - bytes: &'a [u8], - width: Width, - endianness: Endianness, + elf_bytes: &'a [u8], + bl: ByteLayout, header_version: u8, abi: u8, ty: ElfType, @@ -107,67 +126,68 @@ impl<'a> Elf<'a> { hdr.hexdump(64); - let magic = hdr.pop(4).ok_or(ElfError)?; + let magic = hdr.pop(4)?; if magic != &[0x7f, b'E', b'L', b'F'] { - return Err(ElfError); + return Err(ElfError::BadMagic); } - let width = match hdr.pop(1) { - Some(&[1]) => Width::Bits32, - Some(&[2]) => Width::Bits64, - _ => return Err(ElfError), - }; - - let endianness = match hdr.pop(1) { - Some(&[1]) => Endianness::Little, - Some(&[2]) => Endianness::Big, - _ => return Err(ElfError), + let width = match hdr.pop_u8()? { + 1 => Width::Bits32, + 2 => Width::Bits64, + _ => return Err(ElfError::UnsupportedValue), }; - let Some(&[header_version]) = hdr.pop(1) else { - return Err(ElfError) + let endianness = match hdr.pop_u8()? { + 1 => Endianness::Little, + 2 => Endianness::Big, + _ => return Err(ElfError::UnsupportedValue), }; - let Some(&[abi]) = hdr.pop(1) else { - return Err(ElfError) + let bl = ByteLayout { + endianness, + width, }; + + let header_version = hdr.pop_u8()?; - hdr.pop(8); + let abi = hdr.pop_u8()?; - let ty = match hdr.pop_u16(endianness) { - Some(1) => ElfType::Relocatable, - Some(2) => ElfType::Executable, - Some(3) => ElfType::Shared, - Some(4) => ElfType::Core, - _ => return Err(ElfError), + 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) { - Some(0) => None, - Some(0x02) => Some(InstructionSet::Sparc), - Some(0x03) => Some(InstructionSet::I386), - Some(0x08) => Some(InstructionSet::Mips), - Some(0x14) => Some(InstructionSet::PowerPc), - Some(0x28) => Some(InstructionSet::Arm), - Some(0x2a) => Some(InstructionSet::SuperH), - Some(0x32) => Some(InstructionSet::Ia64), - Some(0x3e) => Some(InstructionSet::Amd64), - Some(0xb7) => Some(InstructionSet::Aarch64), - Some(0xf3) => Some(InstructionSet::RiscV), - _ => return Err(ElfError), + 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).ok_or(ElfError)?; - let entry_offset = hdr.pop_word(endianness, width).ok_or(ElfError)?; - let pht_offset = hdr.pop_word(endianness, width).ok_or(ElfError)?; - let sht_offset = hdr.pop_word(endianness, width).ok_or(ElfError)?; - let flags = hdr.pop_u32(endianness).ok_or(ElfError)?; - let header_size = hdr.pop_u16(endianness).ok_or(ElfError)?; - let pht_entry_size = hdr.pop_u16(endianness).ok_or(ElfError)?; - let pht_len = hdr.pop_u16(endianness).ok_or(ElfError)?; - let sht_entry_size = hdr.pop_u16(endianness).ok_or(ElfError)?; - let sht_len = hdr.pop_u16(endianness).ok_or(ElfError)?; - let string_table_idx = hdr.pop_u16(endianness).ok_or(ElfError)?; + 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); @@ -178,14 +198,19 @@ impl<'a> Elf<'a> { dbg!(pht_len); dbg!(sht_entry_size); dbg!(sht_len); - dbg!(string_table_idx); + 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 { - bytes, - width, - endianness, + elf_bytes: bytes, + bl, header_version, abi, ty, @@ -195,6 +220,54 @@ impl<'a> Elf<'a> { } } +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, @@ -253,4 +326,24 @@ enum InstructionSet { } #[derive(Debug)] -struct ElfError; +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, +}