Skip to content

Commit

Permalink
tool: refactor ELF parsing
Browse files Browse the repository at this point in the history
Signed-off-by: Nick Spinale <[email protected]>
  • Loading branch information
nspin committed Jul 9, 2024
1 parent 97bf3cf commit 751ed08
Showing 1 changed file with 103 additions and 48 deletions.
151 changes: 103 additions & 48 deletions tool/microkit/src/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use std::collections::HashMap;
use crate::util::bytes_to_struct;

#[repr(C, packed)]
#[derive(Copy, Clone)]
struct ElfHeader32 {
ident_magic: u32,
ident_class: u8,
Expand Down Expand Up @@ -45,6 +46,7 @@ struct ElfSymbol64 {
}

#[repr(C, packed)]
#[derive(Copy, Clone)]
struct ElfSectionHeader64 {
name: u32,
type_: u32,
Expand All @@ -59,6 +61,7 @@ struct ElfSectionHeader64 {
}

#[repr(C, packed)]
#[derive(Copy, Clone)]
struct ElfProgramHeader64 {
type_: u32,
flags: u32,
Expand All @@ -71,6 +74,7 @@ struct ElfProgramHeader64 {
}

#[repr(C, packed)]
#[derive(Copy, Clone)]
struct ElfHeader64 {
ident_magic: u32,
ident_class: u8,
Expand Down Expand Up @@ -140,6 +144,64 @@ pub struct ElfFile {

impl ElfFile {
pub fn from_path(path: &Path) -> Result<ElfFile, String> {
let reader = ElfFileReader::from_path(path)?;
let segments = reader.segments();
let symbols = reader.symbols()?;
Ok(ElfFile {
word_size: reader.word_size(),
entry: reader.file_header().entry,
segments,
symbols,
})
}

pub fn find_symbol(&self, variable_name: &str) -> Result<(u64, u64), String> {
if let Some((sym, duplicate)) = self.symbols.get(variable_name) {
if *duplicate {
Err(format!("Found multiple symbols with name '{variable_name}'"))
} else {
Ok((sym.value, sym.size))
}
} else {
Err(format!("No symbol named '{variable_name}' not found"))
}
}

pub fn write_symbol(&mut self, variable_name: &str, data: &[u8]) -> Result<(), String> {
let (vaddr, size) = self.find_symbol(variable_name)?;
for seg in &mut self.segments {
if vaddr >= seg.virt_addr && vaddr + size <= seg.virt_addr + seg.data.len() as u64 {
let offset = (vaddr - seg.virt_addr) as usize;
assert!(data.len() as u64 <= size);
seg.data[offset..offset + data.len()].copy_from_slice(data);
return Ok(());
}
}

Err(format!("No symbol named {} found", variable_name))
}

pub fn get_data(&self, vaddr: u64, size: u64) -> Option<&[u8]> {
for seg in &self.segments {
if vaddr >= seg.virt_addr && vaddr + size <= seg.virt_addr + seg.data.len() as u64 {
let offset = (vaddr - seg.virt_addr) as usize;
return Some(&seg.data[offset..offset + size as usize]);
}
}

None
}
}

struct ElfFileReader<'a> {
path: &'a Path,
bytes: Vec<u8>,
word_size: usize,
hdr: ElfHeader64
}

impl<'a> ElfFileReader<'a> {
fn from_path(path: &'a Path) -> Result<Self, String> {
let bytes = match fs::read(path) {
Ok(bytes) => bytes,
Err(err) => return Err(format!("Failed to read ELF '{}': {}", path.display(), err))
Expand Down Expand Up @@ -172,7 +234,7 @@ impl ElfFile {

// Now need to read the header into a struct
let hdr_bytes = &bytes[..hdr_size];
let hdr = unsafe { bytes_to_struct::<ElfHeader64>(hdr_bytes) };
let hdr = *unsafe { bytes_to_struct::<ElfHeader64>(hdr_bytes) };

// We have checked this above but we should check again once we actually cast it to
// a struct.
Expand All @@ -183,20 +245,44 @@ impl ElfFile {
return Err(format!("ELF '{}': incorrect endianness, only little endian architectures are supported", path.display()));
}

let entry = hdr.entry;
Ok(Self {
path,
bytes,
word_size,
hdr,
})
}

fn path(&self) -> &Path {
self.path
}

fn bytes(&self) -> &[u8] {
&self.bytes
}

fn word_size(&self) -> usize {
self.word_size
}

fn file_header(&self) -> &ElfHeader64 {
&self.hdr
}

fn segments(&self) -> Vec<ElfSegment> {
let hdr = self.file_header();

// Read all the segments
let mut segments = Vec::with_capacity(hdr.phnum as usize);
for i in 0..hdr.phnum {
let phent_start = hdr.phoff + (i * hdr.phentsize) as u64;
let phent_end = phent_start + (hdr.phentsize as u64);
let phent_bytes = &bytes[phent_start as usize..phent_end as usize];
let phent_bytes = &self.bytes()[phent_start as usize..phent_end as usize];

let phent = unsafe { bytes_to_struct::<ElfProgramHeader64>(phent_bytes) };

let segment_start = phent.offset as usize;
let segment_end = phent.offset as usize + phent.filesz as usize;
let mut segment_data = Vec::from(&bytes[segment_start..segment_end]);
let mut segment_data = Vec::from(&self.bytes()[segment_start..segment_end]);
let num_zeroes = (phent.memsz - phent.filesz) as usize;
segment_data.resize(segment_data.len() + num_zeroes, 0);

Expand All @@ -211,14 +297,20 @@ impl ElfFile {
segments.push(segment)
}

segments
}

fn symbols(&self) -> Result<HashMap<String, (ElfSymbol64, bool)>, String> {
let hdr = self.file_header();

// Read all the section headers
let mut shents = Vec::with_capacity(hdr.shnum as usize);
let mut symtab_shent: Option<&ElfSectionHeader64> = None;
let mut shstrtab_shent: Option<&ElfSectionHeader64> = None;
for i in 0..hdr.shnum {
let shent_start = hdr.shoff + (i * hdr.shentsize) as u64;
let shent_end = shent_start + hdr.shentsize as u64;
let shent_bytes = &bytes[shent_start as usize..shent_end as usize];
let shent_bytes = &self.bytes()[shent_start as usize..shent_end as usize];

let shent = unsafe { bytes_to_struct::<ElfSectionHeader64>(shent_bytes) };
match shent.type_ {
Expand All @@ -230,23 +322,23 @@ impl ElfFile {
}

if shstrtab_shent.is_none() {
return Err(format!("ELF '{}': unable to find string table section", path.display()));
return Err(format!("ELF '{}': unable to find string table section", self.path().display()));
}

assert!(symtab_shent.is_some());
if symtab_shent.is_none() {
return Err(format!("ELF '{}': unable to find symbol table section", path.display()));
return Err(format!("ELF '{}': unable to find symbol table section", self.path().display()));
}

// Reading the symbol table
let symtab_start = symtab_shent.unwrap().offset as usize;
let symtab_end = symtab_start + symtab_shent.unwrap().size as usize;
let symtab = &bytes[symtab_start..symtab_end];
let symtab = &self.bytes()[symtab_start..symtab_end];

let symtab_str_shent = shents[symtab_shent.unwrap().link as usize];
let symtab_str_start = symtab_str_shent.offset as usize;
let symtab_str_end = symtab_str_start + symtab_str_shent.size as usize;
let symtab_str = &bytes[symtab_str_start..symtab_str_end];
let symtab_str = &self.bytes()[symtab_str_start..symtab_str_end];

// Read all the symbols
let mut symbols: HashMap<String, (ElfSymbol64, bool)> = HashMap::new();
Expand Down Expand Up @@ -278,44 +370,7 @@ impl ElfFile {
offset += symbol_size;
}

Ok(ElfFile { word_size, entry, segments, symbols })
}

pub fn find_symbol(&self, variable_name: &str) -> Result<(u64, u64), String> {
if let Some((sym, duplicate)) = self.symbols.get(variable_name) {
if *duplicate {
Err(format!("Found multiple symbols with name '{variable_name}'"))
} else {
Ok((sym.value, sym.size))
}
} else {
Err(format!("No symbol named '{variable_name}' not found"))
}
}

pub fn write_symbol(&mut self, variable_name: &str, data: &[u8]) -> Result<(), String> {
let (vaddr, size) = self.find_symbol(variable_name)?;
for seg in &mut self.segments {
if vaddr >= seg.virt_addr && vaddr + size <= seg.virt_addr + seg.data.len() as u64 {
let offset = (vaddr - seg.virt_addr) as usize;
assert!(data.len() as u64 <= size);
seg.data[offset..offset + data.len()].copy_from_slice(data);
return Ok(());
}
}

Err(format!("No symbol named {} found", variable_name))
}

pub fn get_data(&self, vaddr: u64, size: u64) -> Option<&[u8]> {
for seg in &self.segments {
if vaddr >= seg.virt_addr && vaddr + size <= seg.virt_addr + seg.data.len() as u64 {
let offset = (vaddr - seg.virt_addr) as usize;
return Some(&seg.data[offset..offset + size as usize]);
}
}

None
Ok(symbols)
}

fn get_string(strtab: &[u8], idx: usize) -> Result<&str, String> {
Expand Down

0 comments on commit 751ed08

Please sign in to comment.