From eb1e5082116229f3d3674c14862d934593e16073 Mon Sep 17 00:00:00 2001 From: Nick Spinale Date: Tue, 2 Jul 2024 21:41:10 +0000 Subject: [PATCH 1/5] tool: return error on ELF word size other than 64 Signed-off-by: Nick Spinale --- tool/microkit/src/elf.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tool/microkit/src/elf.rs b/tool/microkit/src/elf.rs index 5c6a13882..0d4334597 100644 --- a/tool/microkit/src/elf.rs +++ b/tool/microkit/src/elf.rs @@ -245,6 +245,14 @@ 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) }; From 80429fd783b0c46ad27b8381f2c9e1e9272d19ea Mon Sep 17 00:00:00 2001 From: Nick Spinale Date: Thu, 30 Oct 2025 02:15:11 -0700 Subject: [PATCH 2/5] tool: refactor ELF parsing Signed-off-by: Nick Spinale --- tool/microkit/src/elf.rs | 67 +++++++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/tool/microkit/src/elf.rs b/tool/microkit/src/elf.rs index 0d4334597..5cb71bd54 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,29 @@ impl ElfFile { } pub fn from_path(path: &Path) -> Result { + let reader = ElfFileReader::from_path(path)?; + let segments = reader.segments()?; + let symbols = 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, + 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}")), @@ -255,7 +279,7 @@ impl ElfFile { // 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. @@ -268,7 +292,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 { @@ -279,7 +311,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) }; @@ -292,7 +324,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); @@ -308,13 +340,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 { @@ -330,12 +368,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(); @@ -367,16 +405,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 { @@ -413,7 +446,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) => { @@ -430,7 +465,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 From a3123eb50dd2ce065c0bca5eec081e66fb7b9a68 Mon Sep 17 00:00:00 2001 From: Nick Spinale Date: Thu, 30 Oct 2025 02:25:22 -0700 Subject: [PATCH 3/5] tool: add support for split program image Signed-off-by: Nick Spinale --- tool/microkit/src/elf.rs | 12 +++++++++++- tool/microkit/src/main.rs | 38 ++++++++++++++++++++++++++------------ tool/microkit/src/sdf.rs | 8 +++++++- 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/tool/microkit/src/elf.rs b/tool/microkit/src/elf.rs index 5cb71bd54..acd62195b 100644 --- a/tool/microkit/src/elf.rs +++ b/tool/microkit/src/elf.rs @@ -220,9 +220,19 @@ 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 = reader.symbols()?; + 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, 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, From 745fbe2727f4649cffe8a4f893f9c3bbe559318b Mon Sep 17 00:00:00 2001 From: Ivan Velickovic Date: Fri, 27 Mar 2026 15:36:56 +1100 Subject: [PATCH 4/5] tool: remove clone on ElfFileReader Signed-off-by: Ivan Velickovic --- tool/microkit/src/elf.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tool/microkit/src/elf.rs b/tool/microkit/src/elf.rs index acd62195b..4ef82d9e0 100644 --- a/tool/microkit/src/elf.rs +++ b/tool/microkit/src/elf.rs @@ -244,7 +244,6 @@ impl ElfFile { } } -#[derive(Clone)] struct ElfFileReader { bytes: Vec, word_size: usize, From 0bee8e6f58d27a423c70090de91d818a0c29622e Mon Sep 17 00:00:00 2001 From: Ivan Velickovic Date: Fri, 27 Mar 2026 15:40:07 +1100 Subject: [PATCH 5/5] manual: document `path_for_symbols` Signed-off-by: Ivan Velickovic --- docs/manual.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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: