From 0474360fef69366df75871c7f222677efe085684 Mon Sep 17 00:00:00 2001 From: pantonshire Date: Tue, 30 Jul 2024 09:42:07 +0100 Subject: [PATCH] start elf parser --- .gitignore | 4 + Cargo.lock | 7 ++ Cargo.toml | 6 ++ elf/Cargo.toml | 8 ++ elf/src/main.rs | 211 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 236 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 elf/Cargo.toml create mode 100644 elf/src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7f6b588 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.bin +.pc +target/ + diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..b245edb --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "elf" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..94e426c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[workspace] +resolver = "2" +members = [ + "elf" +] + diff --git a/elf/Cargo.toml b/elf/Cargo.toml new file mode 100644 index 0000000..4b0f643 --- /dev/null +++ b/elf/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "elf" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/elf/src/main.rs b/elf/src/main.rs new file mode 100644 index 0000000..22e432d --- /dev/null +++ b/elf/src/main.rs @@ -0,0 +1,211 @@ +use std::{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(InputBytes::new(&bytes)).unwrap(); + + println!("{:?}", elf); +} + +struct InputBytes<'a> { + bytes: &'a [u8], + offset: usize, +} + +impl<'a> InputBytes<'a> { + fn new(bytes: &'a [u8]) -> Self { + Self { bytes, offset: 0 } + } + + fn peek(&self, n: usize) -> Option<&'a [u8]> { + self.bytes.get(self.offset .. (self.offset + n)) + } + + fn pop(&mut self, n: usize) -> Option<&'a [u8]> { + let res = self.peek(n); + self.offset = self.bytes.len().min(self.offset + 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_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_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_width(&mut self, endianness: Endianness, width: Width) -> Option { + match width { + Width::Bits32 => self.pop_u32(endianness).map(u64::from), + Width::Bits64 => self.pop_u64(endianness), + } + } +} + +#[derive(Debug)] +struct Elf { + width: Width, + endianness: Endianness, + header_version: u8, + abi: u8, + ty: ElfType, + instruction_set: Option, + elf_version: u32, +} + +impl Elf { + fn parse(mut bytes: InputBytes) -> Result { + let magic = bytes.pop(4).ok_or(ElfError::Invalid)?; + if magic != &[0x7f, b'E', b'L', b'F'] { + return Err(ElfError::Invalid); + } + + let width = match bytes.pop(1) { + Some(&[1]) => Width::Bits32, + Some(&[2]) => Width::Bits64, + _ => return Err(ElfError::Invalid), + }; + + let endianness = match bytes.pop(1) { + Some(&[1]) => Endianness::Little, + Some(&[2]) => Endianness::Big, + _ => return Err(ElfError::Invalid), + }; + + let Some(&[header_version]) = bytes.pop(1) else { + return Err(ElfError::Invalid) + }; + + let Some(&[abi]) = bytes.pop(1) else { + return Err(ElfError::Invalid) + }; + + bytes.pop(8); + + let ty = match bytes.pop_u16(endianness) { + Some(1) => ElfType::Relocatable, + Some(2) => ElfType::Executable, + Some(3) => ElfType::Shared, + Some(4) => ElfType::Core, + _ => return Err(ElfError::Invalid), + }; + + let instruction_set = match bytes.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::Invalid), + }; + + let elf_version = bytes.pop_u32(endianness).ok_or(ElfError::Invalid)?; + + let entry_offset = bytes.pop_width(endianness, width).ok_or(ElfError::Invalid)?; + let program_header_table_offset = bytes.pop_width(endianness, width).ok_or(ElfError::Invalid)?; + let section_header_table_offset = bytes.pop_width(endianness, width).ok_or(ElfError::Invalid)?; + + let flags = bytes.pop_u32(endianness).ok_or(ElfError::Invalid)?; + + let header_size = bytes.pop_u16(endianness).ok_or(ElfError::Invalid)?; + + dbg!(entry_offset); + dbg!(program_header_table_offset); + dbg!(section_header_table_offset); + dbg!(flags); + dbg!(header_size); + + // TODO + + Ok(Self { + width, + endianness, + header_version, + abi, + ty, + instruction_set, + elf_version, + }) + } +} + +#[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 { + Invalid, + Unsupported, +}