Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 70 additions & 15 deletions tool/microkit/src/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ struct ElfProgramHeader64 {
}

#[repr(C, packed)]
#[derive(Copy, Clone)]
struct ElfHeader64 {
ident_magic: u32,
ident_class: u8,
Expand Down Expand Up @@ -219,6 +220,39 @@ impl ElfFile {
}

pub fn from_path(path: &Path) -> Result<ElfFile, String> {
Self::from_split_paths(path, None)
}

pub fn from_split_paths(
path: &Path,
path_for_symbols: Option<&Path>,
) -> Result<ElfFile, String> {
let reader = ElfFileReader::from_path(path)?;
let segments = reader.segments()?;
let symbols = match path_for_symbols {
Some(path_for_symbols) => ElfFileReader::from_path(path_for_symbols)?.symbols()?,
None => reader.symbols()?,
};
Ok(ElfFile {
path: path.to_owned(),
word_size: reader.word_size,
entry: reader.hdr.entry,
machine: reader.hdr.machine,
segments,
symbols,
})
}
}

#[derive(Clone)]
struct ElfFileReader {
bytes: Vec<u8>,
word_size: usize,
hdr: ElfHeader64,
}

impl ElfFileReader {
fn from_path(path: &Path) -> Result<Self, String> {
let bytes = match fs::read(path) {
Ok(bytes) => bytes,
Err(err) => return Err(format!("failed to read ELF: {err}")),
Expand All @@ -245,9 +279,17 @@ impl ElfFile {
_ => return Err(format!("invalid class '{class}'")),
};

if word_size != 64 {
return Err(format!(
"ELF '{}': unsupported word size: '{}'",
path.display(),
word_size
));
}

// 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 @@ -260,7 +302,15 @@ impl ElfFile {
);
}

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

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

// Read all the segments
if hdr.phnum == 0 {
Expand All @@ -271,7 +321,7 @@ impl ElfFile {
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) };

Expand All @@ -284,7 +334,7 @@ impl ElfFile {

let mut segment_data_bytes = vec![0; phent.memsz as usize];
segment_data_bytes[..phent.filesz as usize]
.copy_from_slice(&bytes[segment_start..segment_end]);
.copy_from_slice(&self.bytes[segment_start..segment_end]);

let segment_data = ElfSegmentData::RealData(segment_data_bytes);

Expand All @@ -300,13 +350,19 @@ impl ElfFile {
segments.push(segment)
}

Ok(segments)
}

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

// Read all the section headers
let mut shents = Vec::with_capacity(hdr.shnum as usize);
let mut symtab_shent: Option<&ElfSectionHeader64> = None;
for i in 0..hdr.shnum {
let shent_start = hdr.shoff + (i as u64 * 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) };
if shent.type_ == 2 {
Expand All @@ -322,12 +378,12 @@ impl ElfFile {
// Reading the symbol table
let symtab_start = symtab_shent.offset as usize;
let symtab_end = symtab_start + symtab_shent.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.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 @@ -359,16 +415,11 @@ impl ElfFile {
offset += symbol_size;
}

Ok(ElfFile {
path: path.to_owned(),
word_size,
entry,
machine: hdr.machine,
segments,
symbols,
})
Ok(symbols)
}
}

impl ElfFile {
pub fn find_symbol(&self, variable_name: &str) -> Result<(u64, u64), String> {
if let Some((sym, duplicate)) = self.symbols.get(variable_name) {
if *duplicate {
Expand Down Expand Up @@ -405,7 +456,9 @@ impl ElfFile {

None
}
}

impl ElfFileReader {
fn get_string(strtab: &[u8], idx: usize) -> Result<&str, String> {
match strtab[idx..].iter().position(|&b| b == 0) {
Some(null_byte_pos) => {
Expand All @@ -422,7 +475,9 @@ impl ElfFile {
)),
}
}
}

impl ElfFile {
pub fn lowest_vaddr(&self) -> u64 {
// This unwrap is safe as we have ensured that there will always be at least 1 segment when parsing the ELF.
let existing_vaddrs: Vec<u64> = self
Expand Down
38 changes: 26 additions & 12 deletions tool/microkit/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -629,18 +629,32 @@ fn main() -> Result<(), String> {
// Get the elf files for each pd:
for pd in &system.protection_domains {
match get_full_path(&pd.program_image, &search_paths) {
Some(path) => match ElfFile::from_path(&path) {
Ok(elf) => system_elfs.push(elf),
Err(e) => {
eprintln!(
"ERROR: failed to parse ELF '{}' for PD '{}': {}",
path.display(),
pd.name,
e
);
std::process::exit(1);
}
},
Some(path) => {
let path_for_symbols = pd
.program_image_for_symbols
.as_ref()
.map(|path_suffix| {
get_full_path(path_suffix, &search_paths).ok_or_else(|| {
format!(
"unable to find program image for symbols: '{}'",
path_suffix.display()
)
})
})
.transpose()?;
match ElfFile::from_split_paths(&path, path_for_symbols.as_deref()) {
Ok(elf) => system_elfs.push(elf),
Err(e) => {
eprintln!(
"ERROR: failed to parse ELF '{}' for PD '{}': {}",
path.display(),
pd.name,
e
);
std::process::exit(1);
}
};
}
None => {
return Err(format!(
"unable to find program image: '{}'",
Expand Down
8 changes: 7 additions & 1 deletion tool/microkit/src/sdf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ pub struct ProtectionDomain {
pub smc: bool,
pub cpu: CpuCore,
pub program_image: PathBuf,
pub program_image_for_symbols: Option<PathBuf>,
pub maps: Vec<SysMap>,
pub irqs: Vec<SysIrq>,
pub ioports: Vec<IOPort>,
Expand Down Expand Up @@ -590,6 +591,7 @@ impl ProtectionDomain {
let mut child_pds = Vec::new();

let mut program_image = None;
let mut program_image_for_symbols = None;
let mut virtual_machine = None;

// Default to minimum priority
Expand All @@ -614,7 +616,7 @@ impl ProtectionDomain {

match child.tag_name().name() {
"program_image" => {
check_attributes(xml_sdf, &child, &["path"])?;
check_attributes(xml_sdf, &child, &["path", "path_for_symbols"])?;
if program_image.is_some() {
return Err(value_error(
xml_sdf,
Expand All @@ -625,6 +627,9 @@ impl ProtectionDomain {

let program_image_path = checked_lookup(xml_sdf, &child, "path")?;
program_image = Some(Path::new(program_image_path).to_path_buf());

program_image_for_symbols =
child.attribute("path_for_symbols").map(PathBuf::from);
}
"map" => {
let map_max_vaddr = config.pd_map_max_vaddr(stack_size);
Expand Down Expand Up @@ -1063,6 +1068,7 @@ impl ProtectionDomain {
smc,
cpu,
program_image: program_image.unwrap(),
program_image_for_symbols,
maps,
irqs,
ioports,
Expand Down
Loading