diff --git a/docs/manual.md b/docs/manual.md index c3ce9475b..646df4b79 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -1008,7 +1008,11 @@ Additionally, it supports the following child elements: * `virtual_machine`: (zero or one) Describes a child virtual machine. * `ioport`: (zero or more) Describes an I/O port, x86-64 only. -The `program_image` element has a single `path` attribute describing the path to an ELF file. +The `program_image` element has the following attributes: + +* `path`: path to an ELF file. +* `path_for_symbols`: (optional) path to an ELF that will be used just for searching up and patching symbols rather than + the ELF specified in `path`. The `map` element has the following attributes: diff --git a/tool/microkit/src/elf.rs b/tool/microkit/src/elf.rs index 5c6a13882..4ef82d9e0 100644 --- a/tool/microkit/src/elf.rs +++ b/tool/microkit/src/elf.rs @@ -74,6 +74,7 @@ struct ElfProgramHeader64 { } #[repr(C, packed)] +#[derive(Copy, Clone)] struct ElfHeader64 { ident_magic: u32, ident_class: u8, @@ -219,6 +220,38 @@ impl ElfFile { } pub fn from_path(path: &Path) -> Result { + Self::from_split_paths(path, None) + } + + pub fn from_split_paths( + path: &Path, + path_for_symbols: Option<&Path>, + ) -> Result { + 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, + }) + } +} + +struct ElfFileReader { + bytes: Vec, + word_size: usize, + hdr: ElfHeader64, +} + +impl ElfFileReader { + fn from_path(path: &Path) -> Result { let bytes = match fs::read(path) { Ok(bytes) => bytes, Err(err) => return Err(format!("failed to read ELF: {err}")), @@ -245,9 +278,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::(hdr_bytes) }; + let hdr = *unsafe { bytes_to_struct::(hdr_bytes) }; // We have checked this above but we should check again once we actually cast it to // a struct. @@ -260,7 +301,15 @@ impl ElfFile { ); } - let entry = hdr.entry; + Ok(Self { + bytes, + word_size, + hdr, + }) + } + + fn segments(&self) -> Result, String> { + let hdr = &self.hdr; // Read all the segments if hdr.phnum == 0 { @@ -271,7 +320,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::(phent_bytes) }; @@ -284,7 +333,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); @@ -300,13 +349,19 @@ impl ElfFile { segments.push(segment) } + Ok(segments) + } + + fn symbols(&self) -> Result, 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::(shent_bytes) }; if shent.type_ == 2 { @@ -322,12 +377,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 = HashMap::new(); @@ -359,16 +414,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 { @@ -405,7 +455,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) => { @@ -422,7 +474,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 = self diff --git a/tool/microkit/src/main.rs b/tool/microkit/src/main.rs index ae6a188ef..0ae4ae62f 100644 --- a/tool/microkit/src/main.rs +++ b/tool/microkit/src/main.rs @@ -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: '{}'", diff --git a/tool/microkit/src/sdf.rs b/tool/microkit/src/sdf.rs index e8093b7e1..ab5b6f3b3 100644 --- a/tool/microkit/src/sdf.rs +++ b/tool/microkit/src/sdf.rs @@ -257,6 +257,7 @@ pub struct ProtectionDomain { pub smc: bool, pub cpu: CpuCore, pub program_image: PathBuf, + pub program_image_for_symbols: Option, pub maps: Vec, pub irqs: Vec, pub ioports: Vec, @@ -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 @@ -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, @@ -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); @@ -1063,6 +1068,7 @@ impl ProtectionDomain { smc, cpu, program_image: program_image.unwrap(), + program_image_for_symbols, maps, irqs, ioports,