Skip to content

Commit e051070

Browse files
committed
tool: refactor ELF parsing
Signed-off-by: Nick Spinale <nick@nickspinale.com>
1 parent 6308e63 commit e051070

File tree

1 file changed

+63
-18
lines changed

1 file changed

+63
-18
lines changed

tool/microkit/src/elf.rs

Lines changed: 63 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use std::path::{Path, PathBuf};
1313
use std::slice::from_raw_parts;
1414

1515
#[repr(C, packed)]
16+
#[derive(Copy, Clone)]
1617
struct ElfHeader32 {
1718
ident_magic: u32,
1819
ident_class: u8,
@@ -48,6 +49,7 @@ struct ElfSymbol64 {
4849
}
4950

5051
#[repr(C, packed)]
52+
#[derive(Copy, Clone)]
5153
struct ElfSectionHeader64 {
5254
name: u32,
5355
type_: u32,
@@ -62,6 +64,7 @@ struct ElfSectionHeader64 {
6264
}
6365

6466
#[repr(C, packed)]
67+
#[derive(Copy, Clone)]
6568
struct ElfProgramHeader64 {
6669
type_: u32,
6770
flags: u32,
@@ -74,6 +77,7 @@ struct ElfProgramHeader64 {
7477
}
7578

7679
#[repr(C, packed)]
80+
#[derive(Copy, Clone)]
7781
struct ElfHeader64 {
7882
ident_magic: u32,
7983
ident_class: u8,
@@ -186,6 +190,30 @@ pub struct ElfFile {
186190

187191
impl ElfFile {
188192
pub fn from_path(path: &Path) -> Result<ElfFile, String> {
193+
let reader = ElfFileReader::from_path(path)?;
194+
let segments = reader.segments()?;
195+
let symbols = reader.symbols()?;
196+
Ok(ElfFile {
197+
path: path.to_owned(),
198+
word_size: reader.word_size,
199+
entry: reader.hdr.entry,
200+
machine: reader.hdr.machine,
201+
segments,
202+
symbols,
203+
})
204+
}
205+
}
206+
207+
#[derive(Clone)]
208+
struct ElfFileReader<'a> {
209+
path: &'a Path,
210+
bytes: Vec<u8>,
211+
word_size: usize,
212+
hdr: ElfHeader64,
213+
}
214+
215+
impl<'a> ElfFileReader<'a> {
216+
fn from_path(path: &'a Path) -> Result<Self, String> {
189217
let bytes = match fs::read(path) {
190218
Ok(bytes) => bytes,
191219
Err(err) => return Err(format!("Failed to read ELF '{}': {}", path.display(), err)),
@@ -228,7 +256,7 @@ impl ElfFile {
228256

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

233261
// We have checked this above but we should check again once we actually cast it to
234262
// a struct.
@@ -242,18 +270,30 @@ impl ElfFile {
242270
));
243271
}
244272

245-
let entry = hdr.entry;
273+
Ok(Self {
274+
path,
275+
bytes,
276+
word_size,
277+
hdr,
278+
})
279+
}
280+
281+
fn segments(&self) -> Result<Vec<ElfSegment>, String> {
282+
let hdr = &self.hdr;
246283

247284
// Read all the segments
248285
if hdr.phnum == 0 {
249-
return Err(format!("ELF '{}': has no program headers", path.display()));
286+
return Err(format!(
287+
"ELF '{}': has no program headers",
288+
self.path.display()
289+
));
250290
}
251291

252292
let mut segments = Vec::with_capacity(hdr.phnum as usize);
253293
for i in 0..hdr.phnum {
254294
let phent_start = hdr.phoff + (i * hdr.phentsize) as u64;
255295
let phent_end = phent_start + (hdr.phentsize as u64);
256-
let phent_bytes = &bytes[phent_start as usize..phent_end as usize];
296+
let phent_bytes = &self.bytes[phent_start as usize..phent_end as usize];
257297

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

@@ -266,7 +306,7 @@ impl ElfFile {
266306

267307
let mut segment_data_bytes = vec![0; phent.memsz as usize];
268308
segment_data_bytes[..phent.filesz as usize]
269-
.copy_from_slice(&bytes[segment_start..segment_end]);
309+
.copy_from_slice(&self.bytes[segment_start..segment_end]);
270310

271311
let segment_data = ElfSegmentData::RealData(segment_data_bytes);
272312

@@ -282,14 +322,20 @@ impl ElfFile {
282322
segments.push(segment)
283323
}
284324

325+
Ok(segments)
326+
}
327+
328+
fn symbols(&self) -> Result<HashMap<String, (ElfSymbol64, bool)>, String> {
329+
let hdr = &self.hdr;
330+
285331
// Read all the section headers
286332
let mut shents = Vec::with_capacity(hdr.shnum as usize);
287333
let mut symtab_shent: Option<&ElfSectionHeader64> = None;
288334
let mut shstrtab_shent: Option<&ElfSectionHeader64> = None;
289335
for i in 0..hdr.shnum {
290336
let shent_start = hdr.shoff + (i as u64 * hdr.shentsize as u64);
291337
let shent_end = shent_start + hdr.shentsize as u64;
292-
let shent_bytes = &bytes[shent_start as usize..shent_end as usize];
338+
let shent_bytes = &self.bytes[shent_start as usize..shent_end as usize];
293339

294340
let shent = unsafe { bytes_to_struct::<ElfSectionHeader64>(shent_bytes) };
295341
match shent.type_ {
@@ -303,27 +349,27 @@ impl ElfFile {
303349
if shstrtab_shent.is_none() {
304350
return Err(format!(
305351
"ELF '{}': unable to find string table section",
306-
path.display()
352+
self.path.display()
307353
));
308354
}
309355

310356
assert!(symtab_shent.is_some());
311357
if symtab_shent.is_none() {
312358
return Err(format!(
313359
"ELF '{}': unable to find symbol table section",
314-
path.display()
360+
self.path.display()
315361
));
316362
}
317363

318364
// Reading the symbol table
319365
let symtab_start = symtab_shent.unwrap().offset as usize;
320366
let symtab_end = symtab_start + symtab_shent.unwrap().size as usize;
321-
let symtab = &bytes[symtab_start..symtab_end];
367+
let symtab = &self.bytes[symtab_start..symtab_end];
322368

323369
let symtab_str_shent = shents[symtab_shent.unwrap().link as usize];
324370
let symtab_str_start = symtab_str_shent.offset as usize;
325371
let symtab_str_end = symtab_str_start + symtab_str_shent.size as usize;
326-
let symtab_str = &bytes[symtab_str_start..symtab_str_end];
372+
let symtab_str = &self.bytes[symtab_str_start..symtab_str_end];
327373

328374
// Read all the symbols
329375
let mut symbols: HashMap<String, (ElfSymbol64, bool)> = HashMap::new();
@@ -355,16 +401,11 @@ impl ElfFile {
355401
offset += symbol_size;
356402
}
357403

358-
Ok(ElfFile {
359-
path: path.to_owned(),
360-
word_size,
361-
entry,
362-
machine: hdr.machine,
363-
segments,
364-
symbols,
365-
})
404+
Ok(symbols)
366405
}
406+
}
367407

408+
impl ElfFile {
368409
pub fn find_symbol(&self, variable_name: &str) -> Result<(u64, u64), String> {
369410
if let Some((sym, duplicate)) = self.symbols.get(variable_name) {
370411
if *duplicate {
@@ -401,7 +442,9 @@ impl ElfFile {
401442

402443
None
403444
}
445+
}
404446

447+
impl<'a> ElfFileReader<'a> {
405448
fn get_string(strtab: &[u8], idx: usize) -> Result<&str, String> {
406449
match strtab[idx..].iter().position(|&b| b == 0) {
407450
Some(null_byte_pos) => {
@@ -418,7 +461,9 @@ impl ElfFile {
418461
)),
419462
}
420463
}
464+
}
421465

466+
impl ElfFile {
422467
pub fn lowest_vaddr(&self) -> u64 {
423468
// This unwrap is safe as we have ensured that there will always be at least 1 segment when parsing the ELF.
424469
let existing_vaddrs: Vec<u64> = self

0 commit comments

Comments
 (0)