diff --git a/src/omf.rs b/src/omf.rs index 7c4483c..41c8b73 100644 --- a/src/omf.rs +++ b/src/omf.rs @@ -92,7 +92,7 @@ pub mod record_type { pub const VENDEXT: u8 = 0xCE; } -/// OMF record header - common to all record types +/// OMF record header #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct RecordHeader { @@ -195,7 +195,7 @@ pub enum FrameMethod { } /// Check if a byte is a valid OMF record type -pub fn is_omf_record_type(byte: u8) -> bool { +pub(crate) fn is_omf_record_type(byte: u8) -> bool { use record_type::*; matches!( byte, @@ -242,13 +242,8 @@ pub fn is_omf_record_type(byte: u8) -> bool { ) } -/// Check if a record type uses 32-bit fields -pub fn is_32bit_record(record_type: u8) -> bool { - record_type & 0x01 != 0 -} - /// Helper to read an OMF index (1 or 2 bytes) -pub fn read_index(data: &[u8]) -> Option<(u16, usize)> { +pub(crate) fn read_index(data: &[u8]) -> Option<(u16, usize)> { if data.is_empty() { return None; } @@ -268,7 +263,7 @@ pub fn read_index(data: &[u8]) -> Option<(u16, usize)> { } /// Helper to read a counted string (length byte followed by string) -pub fn read_counted_string(data: &[u8]) -> Option<(&[u8], usize)> { +pub(crate) fn read_counted_string(data: &[u8]) -> Option<(&[u8], usize)> { if data.is_empty() { return None; } @@ -283,7 +278,7 @@ pub fn read_counted_string(data: &[u8]) -> Option<(&[u8], usize)> { /// Read an encoded value (used in LIDATA for repeat counts and block counts) /// Returns the value and number of bytes consumed -pub fn read_encoded_value(data: &[u8]) -> Option<(u32, usize)> { +pub(crate) fn read_encoded_value(data: &[u8]) -> Option<(u32, usize)> { if data.is_empty() { return None; } diff --git a/src/read/any.rs b/src/read/any.rs index afa6671..520f493 100644 --- a/src/read/any.rs +++ b/src/read/any.rs @@ -1193,7 +1193,7 @@ where ), ), #[cfg(feature = "omf")] - Omf((omf::OmfSymbolIterator<'data, 'file>, PhantomData)), + Omf((omf::OmfSymbolIterator<'data, 'file, R>, PhantomData)), } impl<'data, 'file, R: ReadRef<'data>> Iterator for SymbolIterator<'data, 'file, R> { diff --git a/src/read/omf/file.rs b/src/read/omf/file.rs index 806404e..056d977 100644 --- a/src/read/omf/file.rs +++ b/src/read/omf/file.rs @@ -1,13 +1,15 @@ //! OMF file implementation for the unified read API. use crate::read::{ - self, Architecture, ByteString, ComdatKind, Error, Export, FileFlags, Import, - NoDynamicRelocationIterator, Object, ObjectComdat, ObjectKind, ObjectSection, ObjectSegment, - ReadRef, Result, SectionIndex, SegmentFlags, SymbolIndex, + Architecture, ByteString, Error, Export, FileFlags, Import, NoDynamicRelocationIterator, + Object, ObjectKind, ObjectSection, ReadRef, Result, SectionIndex, SymbolIndex, }; use crate::SubArchitecture; -use super::{OmfFile, OmfSection, OmfSymbol, OmfSymbolIterator, OmfSymbolTable}; +use super::{ + OmfComdat, OmfComdatIterator, OmfFile, OmfSection, OmfSectionIterator, OmfSegmentIterator, + OmfSegmentRef, OmfSymbol, OmfSymbolIterator, OmfSymbolTable, +}; impl<'data, R: ReadRef<'data>> Object<'data> for OmfFile<'data, R> { type Segment<'file> @@ -46,7 +48,7 @@ impl<'data, R: ReadRef<'data>> Object<'data> for OmfFile<'data, R> { Self: 'file, 'data: 'file; type SymbolIterator<'file> - = OmfSymbolIterator<'data, 'file> + = OmfSymbolIterator<'data, 'file, R> where Self: 'file, 'data: 'file; @@ -127,26 +129,15 @@ impl<'data, R: ReadRef<'data>> Object<'data> for OmfFile<'data, R> { fn symbol_by_index(&self, index: SymbolIndex) -> Result> { let idx = index.0; - let total_publics = self.publics.len(); - let total_externals = self.externals.len(); - let total_before_communals = total_publics + total_externals; - - if idx < total_publics { - Ok(self.publics[idx].clone()) - } else if idx < total_before_communals { - Ok(self.externals[idx - total_publics].clone()) - } else if idx < total_before_communals + self.communals.len() { - Ok(self.communals[idx - total_before_communals].clone()) - } else { - Err(Error("Symbol index out of bounds")) + if idx >= self.symbols.len() { + return Err(Error("Symbol index out of bounds")); } + Ok(self.symbols[idx].clone()) } fn symbols(&self) -> Self::SymbolIterator<'_> { OmfSymbolIterator { - publics: &self.publics, - externals: &self.externals, - communals: &self.communals, + file: self, index: 0, } } @@ -157,10 +148,8 @@ impl<'data, R: ReadRef<'data>> Object<'data> for OmfFile<'data, R> { fn dynamic_symbols(&self) -> Self::SymbolIterator<'_> { OmfSymbolIterator { - publics: &[], - externals: &[], - communals: &[], - index: 0, + file: self, + index: usize::MAX, // Empty iterator } } @@ -173,10 +162,18 @@ impl<'data, R: ReadRef<'data>> Object<'data> for OmfFile<'data, R> { } fn imports(&self) -> Result>> { - // External symbols are imports in OMF + // Only true external symbols are imports in OMF + // LocalExternal (LEXTDEF) are module-local references that should be resolved + // within the same module by LocalPublic (LPUBDEF) symbols Ok(self - .externals + .all_symbols() .iter() + .filter(|sym| { + matches!( + sym.class, + super::OmfSymbolClass::External | super::OmfSymbolClass::ComdatExternal + ) + }) .map(|ext| Import { library: ByteString(b""), name: ByteString(ext.name), @@ -185,10 +182,12 @@ impl<'data, R: ReadRef<'data>> Object<'data> for OmfFile<'data, R> { } fn exports(&self) -> Result>> { - // Public symbols are exports in OMF + // Only true public symbols are exports in OMF + // LocalPublic (LPUBDEF) are module-local symbols not visible outside Ok(self - .publics + .all_symbols() .iter() + .filter(|sym| sym.class == super::OmfSymbolClass::Public) .map(|pub_sym| Export { name: ByteString(pub_sym.name), address: pub_sym.offset as u64, @@ -232,213 +231,3 @@ impl<'data, R: ReadRef<'data>> Object<'data> for OmfFile<'data, R> { FileFlags::None } } - -/// An OMF segment reference. -#[derive(Debug)] -pub struct OmfSegmentRef<'data, 'file, R: ReadRef<'data>> { - file: &'file OmfFile<'data, R>, - index: usize, -} - -impl<'data, 'file, R: ReadRef<'data>> read::private::Sealed for OmfSegmentRef<'data, 'file, R> {} - -impl<'data, 'file, R: ReadRef<'data>> ObjectSegment<'data> for OmfSegmentRef<'data, 'file, R> { - fn address(&self) -> u64 { - 0 - } - - fn size(&self) -> u64 { - self.file.segments[self.index].length as u64 - } - - fn align(&self) -> u64 { - match self.file.segments[self.index].alignment { - crate::omf::SegmentAlignment::Byte => 1, - crate::omf::SegmentAlignment::Word => 2, - crate::omf::SegmentAlignment::Paragraph => 16, - crate::omf::SegmentAlignment::Page => 256, - crate::omf::SegmentAlignment::DWord => 4, - crate::omf::SegmentAlignment::Page4K => 4096, - _ => 1, - } - } - - fn file_range(&self) -> (u64, u64) { - (0, 0) - } - - fn data(&self) -> Result<&'data [u8]> { - // OMF segments don't have direct file mapping - Ok(&[]) - } - - fn data_range(&self, _address: u64, _size: u64) -> Result> { - Ok(None) - } - - fn name_bytes(&self) -> Result> { - Ok(self - .file - .get_name(self.file.segments[self.index].name_index)) - } - - fn name(&self) -> Result> { - let index = self.file.segments[self.index].name_index; - let name_opt = self.file.get_name(index); - match name_opt { - Some(bytes) => Ok(core::str::from_utf8(bytes).ok()), - None => Ok(None), - } - } - - fn flags(&self) -> SegmentFlags { - SegmentFlags::None - } -} - -/// An iterator over OMF segments. -#[derive(Debug)] -pub struct OmfSegmentIterator<'data, 'file, R: ReadRef<'data>> { - file: &'file OmfFile<'data, R>, - index: usize, -} - -impl<'data, 'file, R: ReadRef<'data>> Iterator for OmfSegmentIterator<'data, 'file, R> { - type Item = OmfSegmentRef<'data, 'file, R>; - - fn next(&mut self) -> Option { - if self.index < self.file.segments.len() { - let segment = OmfSegmentRef { - file: self.file, - index: self.index, - }; - self.index += 1; - Some(segment) - } else { - None - } - } -} - -/// An iterator over OMF sections. -#[derive(Debug)] -pub struct OmfSectionIterator<'data, 'file, R: ReadRef<'data>> { - file: &'file OmfFile<'data, R>, - index: usize, -} - -impl<'data, 'file, R: ReadRef<'data>> Iterator for OmfSectionIterator<'data, 'file, R> { - type Item = OmfSection<'data, 'file, R>; - - fn next(&mut self) -> Option { - if self.index < self.file.segments.len() { - let section = OmfSection { - file: self.file, - index: self.index, - }; - self.index += 1; - Some(section) - } else { - None - } - } -} - -/// A COMDAT section in an OMF file. -#[derive(Debug)] -pub struct OmfComdat<'data, 'file, R: ReadRef<'data>> { - file: &'file OmfFile<'data, R>, - index: usize, - _phantom: core::marker::PhantomData<&'data ()>, -} - -impl<'data, 'file, R: ReadRef<'data>> read::private::Sealed for OmfComdat<'data, 'file, R> {} - -impl<'data, 'file, R: ReadRef<'data>> ObjectComdat<'data> for OmfComdat<'data, 'file, R> { - type SectionIterator = OmfComdatSectionIterator<'data, 'file, R>; - - fn kind(&self) -> ComdatKind { - let comdat = &self.file.comdats[self.index]; - match comdat.selection { - super::OmfComdatSelection::Explicit => ComdatKind::NoDuplicates, - super::OmfComdatSelection::UseAny => ComdatKind::Any, - super::OmfComdatSelection::SameSize => ComdatKind::SameSize, - super::OmfComdatSelection::ExactMatch => ComdatKind::ExactMatch, - } - } - - fn symbol(&self) -> SymbolIndex { - // COMDAT symbols don't have a direct symbol index in OMF - // Return an invalid index - SymbolIndex(usize::MAX) - } - - fn name_bytes(&self) -> Result<&'data [u8]> { - let comdat = &self.file.comdats[self.index]; - Ok(comdat.name) - } - - fn name(&self) -> Result<&'data str> { - let comdat = &self.file.comdats[self.index]; - core::str::from_utf8(comdat.name).map_err(|_| Error("Invalid UTF-8 in COMDAT name")) - } - - fn sections(&self) -> Self::SectionIterator { - let comdat = &self.file.comdats[self.index]; - OmfComdatSectionIterator { - segment_index: if comdat.segment_index > 0 { - Some(comdat.segment_index as usize - 1) - } else { - None - }, - returned: false, - _phantom: core::marker::PhantomData, - } - } -} - -/// An iterator over COMDAT sections. -#[derive(Debug)] -pub struct OmfComdatIterator<'data, 'file, R: ReadRef<'data>> { - file: &'file OmfFile<'data, R>, - index: usize, -} - -impl<'data, 'file, R: ReadRef<'data>> Iterator for OmfComdatIterator<'data, 'file, R> { - type Item = OmfComdat<'data, 'file, R>; - - fn next(&mut self) -> Option { - if self.index < self.file.comdats.len() { - let comdat = OmfComdat { - file: self.file, - index: self.index, - _phantom: core::marker::PhantomData, - }; - self.index += 1; - Some(comdat) - } else { - None - } - } -} - -/// An iterator over sections in a COMDAT. -#[derive(Debug)] -pub struct OmfComdatSectionIterator<'data, 'file, R: ReadRef<'data>> { - segment_index: Option, - returned: bool, - _phantom: core::marker::PhantomData<(&'data (), &'file (), R)>, -} - -impl<'data, 'file, R: ReadRef<'data>> Iterator for OmfComdatSectionIterator<'data, 'file, R> { - type Item = SectionIndex; - - fn next(&mut self) -> Option { - if !self.returned { - self.returned = true; - self.segment_index.map(|idx| SectionIndex(idx + 1)) - } else { - None - } - } -} diff --git a/src/read/omf/mod.rs b/src/read/omf/mod.rs index c67f436..606561c 100644 --- a/src/read/omf/mod.rs +++ b/src/read/omf/mod.rs @@ -9,14 +9,36 @@ use crate::read::{self, Error, ReadRef, Result}; mod file; pub use file::*; +mod relocation; +pub use relocation::*; + mod section; pub use section::*; +mod segment; +pub use segment::*; + mod symbol; pub use symbol::*; -mod relocation; -pub use relocation::*; +/// Symbol class for OMF symbols +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum OmfSymbolClass { + /// Public symbol (PUBDEF) + Public, + /// Local public symbol (LPUBDEF) + LocalPublic, + /// External symbol (EXTDEF) + External, + /// Local external symbol (LEXTDEF) + LocalExternal, + /// Communal symbol (COMDEF) + Communal, + /// Local communal symbol (LCOMDEF) + LocalCommunal, + /// COMDAT external symbol (CEXTDEF) + ComdatExternal, +} /// An OMF object file. /// @@ -28,12 +50,10 @@ pub struct OmfFile<'data, R: ReadRef<'data> = &'data [u8]> { module_name: Option<&'data str>, /// Segment definitions segments: Vec>, - /// Public symbols - publics: Vec>, - /// External symbols - externals: Vec>, - /// Communal symbols from COMDEF - communals: Vec>, + /// All symbols (publics, externals, communals, locals) in occurrence order + symbols: Vec>, + /// Maps external-name table index (1-based) to SymbolIndex + external_order: Vec, /// COMDAT sections comdats: Vec>, /// Name table (LNAMES/LLNAMES) @@ -75,13 +95,15 @@ pub struct OmfSegment<'data> { pub relocations: Vec, } -/// An OMF symbol (public or external) +/// An OMF symbol #[derive(Debug, Clone)] pub struct OmfSymbol<'data> { /// Symbol table index pub symbol_index: usize, /// Symbol name pub name: &'data [u8], + /// Symbol class (Public, External, etc.) + pub class: OmfSymbolClass, /// Group index (0 if none) pub group_index: u16, /// Segment index (0 if external) @@ -154,19 +176,6 @@ pub enum OmfComdatSelection { ExactMatch = 3, } -/// A weak extern definition -#[derive(Debug, Clone)] -pub struct OmfWeakExtern<'data> { - /// Weak symbol index (external symbol) - pub weak_symbol_index: u16, - /// Default resolution symbol index - pub default_symbol_index: u16, - /// Weak symbol name - pub weak_name: &'data [u8], - /// Default symbol name - pub default_name: &'data [u8], -} - /// Thread definition for FIXUPP parsing #[derive(Debug, Clone, Copy)] struct ThreadDef { @@ -210,56 +219,42 @@ impl<'data, R: ReadRef<'data>> OmfFile<'data, R> { data, module_name: None, segments: Vec::new(), - publics: Vec::new(), - externals: Vec::new(), - communals: Vec::new(), + symbols: Vec::new(), + external_order: Vec::new(), comdats: Vec::new(), names: Vec::new(), groups: Vec::new(), }; file.parse_records()?; - file.assign_symbol_indices(); + file.assign_symbol_kinds(); Ok(file) } - fn assign_symbol_indices(&mut self) { - let mut index = 0; - - // First, compute kinds for public symbols based on their segments - let public_kinds = self - .publics + fn assign_symbol_kinds(&mut self) { + // Compute kinds for symbols based on their segments + let kinds: Vec = self + .symbols .iter() - .map(|sym| { - if sym.segment_index > 0 && (sym.segment_index as usize) <= self.segments.len() { - let segment_idx = (sym.segment_index - 1) as usize; - let section_kind = self.segment_section_kind(segment_idx); - Self::symbol_kind_from_section_kind(section_kind) - } else { - read::SymbolKind::Unknown + .map(|sym| match sym.class { + OmfSymbolClass::Public | OmfSymbolClass::LocalPublic => { + if sym.segment_index > 0 && (sym.segment_index as usize) <= self.segments.len() + { + let segment_idx = (sym.segment_index - 1) as usize; + let section_kind = self.segment_section_kind(segment_idx); + Self::symbol_kind_from_section_kind(section_kind) + } else { + read::SymbolKind::Unknown + } } + OmfSymbolClass::Communal | OmfSymbolClass::LocalCommunal => read::SymbolKind::Data, + _ => read::SymbolKind::Unknown, }) - .collect::>(); + .collect(); - // Assign indices to public symbols and their pre-computed kinds - for (sym, kind) in self.publics.iter_mut().zip(public_kinds) { - sym.symbol_index = index; + // Apply computed kinds + for (sym, kind) in self.symbols.iter_mut().zip(kinds) { sym.kind = kind; - index += 1; - } - - // Assign indices to external symbols - for sym in self.externals.iter_mut() { - sym.symbol_index = index; - sym.kind = read::SymbolKind::Unknown; - index += 1; - } - - // Assign indices to communal symbols - for sym in self.communals.iter_mut() { - sym.symbol_index = index; - sym.kind = read::SymbolKind::Data; - index += 1; } } @@ -273,7 +268,7 @@ impl<'data, R: ReadRef<'data>> OmfFile<'data, R> { } /// Get the section kind for a segment (reusing logic from OmfSection) - pub fn segment_section_kind(&self, segment_index: usize) -> read::SectionKind { + fn segment_section_kind(&self, segment_index: usize) -> read::SectionKind { if segment_index >= self.segments.len() { return read::SectionKind::Unknown; } @@ -305,8 +300,11 @@ impl<'data, R: ReadRef<'data>> OmfFile<'data, R> { return read::SectionKind::Text; } else if name_upper == b"_DATA" || name_upper == b"DATA" || name_upper == b".DATA" { return read::SectionKind::Data; - } else if name_upper == b"_BSS" || name_upper == b"BSS" || name_upper == b".BSS" - || name_upper == b"STACK" { + } else if name_upper == b"_BSS" + || name_upper == b"BSS" + || name_upper == b".BSS" + || name_upper == b"STACK" + { return read::SectionKind::UninitializedData; } } @@ -431,13 +429,33 @@ impl<'data, R: ReadRef<'data>> OmfFile<'data, R> { self.parse_grpdef(record_data)?; } omf::record_type::PUBDEF | omf::record_type::PUBDEF32 => { - self.parse_pubdef(record_data, record_type == omf::record_type::PUBDEF32)?; + self.parse_pubdef( + record_data, + record_type == omf::record_type::PUBDEF32, + OmfSymbolClass::Public, + )?; + } + omf::record_type::LPUBDEF | omf::record_type::LPUBDEF32 => { + self.parse_pubdef( + record_data, + record_type == omf::record_type::LPUBDEF32, + OmfSymbolClass::LocalPublic, + )?; } omf::record_type::EXTDEF => { - self.parse_extdef(record_data)?; + self.parse_extdef(record_data, OmfSymbolClass::External)?; + } + omf::record_type::LEXTDEF | omf::record_type::LEXTDEF32 => { + self.parse_extdef(record_data, OmfSymbolClass::LocalExternal)?; + } + omf::record_type::CEXTDEF => { + self.parse_extdef(record_data, OmfSymbolClass::ComdatExternal)?; } omf::record_type::COMDEF => { - self.parse_comdef(record_data)?; + self.parse_comdef(record_data, OmfSymbolClass::Communal)?; + } + omf::record_type::LCOMDEF => { + self.parse_comdef(record_data, OmfSymbolClass::LocalCommunal)?; } omf::record_type::COMDAT | omf::record_type::COMDAT32 => { self.parse_comdat(record_data, record_type == omf::record_type::COMDAT32)?; @@ -624,7 +642,12 @@ impl<'data, R: ReadRef<'data>> OmfFile<'data, R> { Ok(()) } - fn parse_pubdef(&mut self, data: &'data [u8], is_32bit: bool) -> Result<()> { + fn parse_pubdef( + &mut self, + data: &'data [u8], + is_32bit: bool, + class: OmfSymbolClass, + ) -> Result<()> { let mut offset = 0; // Parse group index @@ -680,12 +703,13 @@ impl<'data, R: ReadRef<'data>> OmfFile<'data, R> { // Parse type index let (type_index, size) = omf::read_index(&data[offset..]) - .ok_or(Error("Invalid type index in EXTDEF record"))?; + .ok_or(Error("Invalid type index in PUBDEF/LPUBDEF record"))?; offset += size; - self.publics.push(OmfSymbol { - symbol_index: 0, // Will be assigned later + self.symbols.push(OmfSymbol { + symbol_index: self.symbols.len(), name, + class, group_index, segment_index, frame_number, @@ -698,7 +722,7 @@ impl<'data, R: ReadRef<'data>> OmfFile<'data, R> { Ok(()) } - fn parse_extdef(&mut self, data: &'data [u8]) -> Result<()> { + fn parse_extdef(&mut self, data: &'data [u8], class: OmfSymbolClass) -> Result<()> { let mut offset = 0; while offset < data.len() { @@ -710,12 +734,14 @@ impl<'data, R: ReadRef<'data>> OmfFile<'data, R> { // Parse type index let (type_index, size) = omf::read_index(&data[offset..]) - .ok_or(Error("Invalid type index in EXTDEF record"))?; + .ok_or(Error("Invalid type index in EXTDEF/LEXTDEF/CEXTDEF record"))?; offset += size; - self.externals.push(OmfSymbol { - symbol_index: 0, // Will be assigned later + let sym_idx = self.symbols.len(); + self.symbols.push(OmfSymbol { + symbol_index: sym_idx, name, + class, group_index: 0, segment_index: 0, frame_number: 0, @@ -723,12 +749,15 @@ impl<'data, R: ReadRef<'data>> OmfFile<'data, R> { type_index, kind: read::SymbolKind::Unknown, }); + + // Add to external_order for symbols that contribute to external-name table + self.external_order.push(read::SymbolIndex(sym_idx)); } Ok(()) } - fn parse_comdef(&mut self, data: &'data [u8]) -> Result<()> { + fn parse_comdef(&mut self, data: &'data [u8], class: OmfSymbolClass) -> Result<()> { let mut offset = 0; while offset < data.len() { @@ -740,7 +769,7 @@ impl<'data, R: ReadRef<'data>> OmfFile<'data, R> { // Parse type index let (type_index, size) = omf::read_index(&data[offset..]) - .ok_or(Error("Invalid type index in COMDEF record"))?; + .ok_or(Error("Invalid type index in COMDEF/LCOMDEF record"))?; offset += size; // Parse data type and communal length @@ -774,9 +803,11 @@ impl<'data, R: ReadRef<'data>> OmfFile<'data, R> { } }; - self.communals.push(OmfSymbol { - symbol_index: 0, // Will be assigned later + let sym_idx = self.symbols.len(); + self.symbols.push(OmfSymbol { + symbol_index: sym_idx, name, + class, group_index: 0, segment_index: 0, frame_number: 0, @@ -784,6 +815,9 @@ impl<'data, R: ReadRef<'data>> OmfFile<'data, R> { type_index, kind: read::SymbolKind::Data, }); + + // Add to external_order for symbols that contribute to external-name table + self.external_order.push(read::SymbolIndex(sym_idx)); } Ok(()) @@ -1238,7 +1272,7 @@ impl<'data, R: ReadRef<'data>> OmfFile<'data, R> { } /// Expand a LIDATA block into its uncompressed form - pub fn expand_lidata_block(&self, data: &[u8]) -> Result> { + fn expand_lidata_block(&self, data: &[u8]) -> Result> { let mut offset = 0; let mut result = Vec::new(); @@ -1278,7 +1312,7 @@ impl<'data, R: ReadRef<'data>> OmfFile<'data, R> { // Nested blocks: recurse for each block for _ in 0..block_count { let block_data = self.expand_lidata_block(&data[offset..])?; - let block_size = self.lidata_block_size(&data[offset..])?; + let block_size = lidata_block_size(&data[offset..])?; offset += block_size; // Repeat the expanded block @@ -1291,10 +1325,6 @@ impl<'data, R: ReadRef<'data>> OmfFile<'data, R> { Ok(result) } - fn lidata_block_size(&self, data: &[u8]) -> Result { - lidata_block_size_impl(data) - } - /// Get the module name pub fn module_name(&self) -> Option<&'data str> { self.module_name @@ -1305,46 +1335,23 @@ impl<'data, R: ReadRef<'data>> OmfFile<'data, R> { &self.segments } - /// Get the public symbols - pub fn publics(&self) -> &[OmfSymbol<'data>] { - &self.publics - } - - /// Get the external symbols - pub fn externals(&self) -> &[OmfSymbol<'data>] { - &self.externals + /// Get symbol by external-name index (1-based, as used in FIXUPP records) + pub fn external_symbol(&self, external_index: u16) -> Option<&OmfSymbol<'data>> { + let symbol_index = self + .external_order + .get(external_index.checked_sub(1)? as usize)?; + self.symbols.get(symbol_index.0) } /// Get a name by index (1-based) pub fn get_name(&self, index: u16) -> Option<&'data [u8]> { - if index > 0 && (index as usize) <= self.names.len() { - Some(self.names[(index - 1) as usize]) - } else { - None - } + let name_index = index.checked_sub(1)?; + self.names.get(name_index as usize).copied() } - /// Get a symbol by index - pub fn symbol_by_index(&self, index: read::SymbolIndex) -> Result> { - let idx = index.0; - let total_publics = self.publics.len(); - let total_externals = self.externals.len(); - let total_before_communals = total_publics + total_externals; - let total = total_before_communals + self.communals.len(); - - if idx >= total { - return Err(Error("Invalid symbol index")); - } - - let symbol = if idx < total_publics { - self.publics[idx].clone() - } else if idx < total_before_communals { - self.externals[idx - total_publics].clone() - } else { - self.communals[idx - total_before_communals].clone() - }; - - Ok(symbol) + /// Get all symbols (for iteration) + pub fn all_symbols(&self) -> &[OmfSymbol<'data>] { + &self.symbols } /// Verify the checksum of an OMF record @@ -1375,7 +1382,7 @@ impl<'data, R: ReadRef<'data>> OmfFile<'data, R> { } /// Helper function to calculate LIDATA block size -fn lidata_block_size_impl(data: &[u8]) -> Result { +fn lidata_block_size(data: &[u8]) -> Result { let mut offset = 0; // Read repeat count @@ -1398,8 +1405,7 @@ fn lidata_block_size_impl(data: &[u8]) -> Result { } else { // Nested blocks for _ in 0..block_count { - let nested_size = lidata_block_size_impl(&data[offset..])?; - offset += nested_size; + offset += lidata_block_size(&data[offset..])?; } } diff --git a/src/read/omf/relocation.rs b/src/read/omf/relocation.rs index 5874402..60d2941 100644 --- a/src/read/omf/relocation.rs +++ b/src/read/omf/relocation.rs @@ -15,11 +15,7 @@ impl<'data, 'file, R: read::ReadRef<'data>> Iterator for OmfRelocationIterator<' fn next(&mut self) -> Option { let relocations = &self.file.segments[self.segment_index].relocations; - if self.index >= relocations.len() { - return None; - } - - let reloc = &relocations[self.index]; + let reloc = relocations.get(self.index)?; self.index += 1; // Convert OMF relocation to generic relocation @@ -58,13 +54,9 @@ impl<'data, 'file, R: read::ReadRef<'data>> Iterator for OmfRelocationIterator<' read::RelocationTarget::Section(SectionIndex(reloc.target_index as usize)) } omf::TargetMethod::ExternalIndex => { - // External indices in OMF are 1-based indices into the EXTDEF table - // Our symbol table has publics first, then externals - // So we need to adjust: symbol_index = publics.len() + (external_idx - 1) - if reloc.target_index > 0 { - let symbol_idx = - self.file.publics.len() + (reloc.target_index as usize - 1); - read::RelocationTarget::Symbol(read::SymbolIndex(symbol_idx)) + // External indices in OMF are 1-based indices into the external-name table + if let Some(symbol) = self.file.external_symbol(reloc.target_index) { + read::RelocationTarget::Symbol(read::SymbolIndex(symbol.symbol_index)) } else { // Invalid external index read::RelocationTarget::Absolute diff --git a/src/read/omf/section.rs b/src/read/omf/section.rs index b8d5a7c..43970aa 100644 --- a/src/read/omf/section.rs +++ b/src/read/omf/section.rs @@ -4,9 +4,12 @@ use alloc::borrow::Cow; use alloc::vec; use core::str; -use crate::read::{ - self, CompressedData, CompressedFileRange, Error, ObjectSection, ReadRef, RelocationMap, - Result, SectionFlags, SectionIndex, SectionKind, +use crate::{ + read::{ + self, CompressedData, CompressedFileRange, Error, ObjectSection, ReadRef, RelocationMap, + Result, SectionFlags, SectionIndex, SectionKind, + }, + ComdatKind, ObjectComdat, SymbolIndex, }; use super::{relocation::OmfRelocationIterator, OmfDataChunk, OmfFile, OmfSegment}; @@ -71,8 +74,9 @@ impl<'data, 'file, R: ReadRef<'data>> ObjectSection<'data> for OmfSection<'data, } // For multiple chunks, LIDATA, or non-contiguous data, we can't return a reference - // Users should use uncompressed_data() instead for these cases - Err(Error("OMF segment data is not contiguous; use uncompressed_data() instead")) + Err(Error( + "OMF segment data is not contiguous; use uncompressed_data() instead", + )) } fn data_range(&self, address: u64, size: u64) -> Result> { @@ -139,7 +143,7 @@ impl<'data, 'file, R: ReadRef<'data>> ObjectSection<'data> for OmfSection<'data, match chunk { OmfDataChunk::Direct(data) => { - // Direct data - just copy it + // Direct data let end = start + data.len(); if end <= result.len() { result[start..end].copy_from_slice(data); @@ -217,3 +221,121 @@ impl<'data, 'file, R: ReadRef<'data>> ObjectSection<'data> for OmfSection<'data, flags } } + +/// An iterator over OMF sections. +#[derive(Debug)] +pub struct OmfSectionIterator<'data, 'file, R: ReadRef<'data>> { + pub(super) file: &'file OmfFile<'data, R>, + pub(super) index: usize, +} + +impl<'data, 'file, R: ReadRef<'data>> Iterator for OmfSectionIterator<'data, 'file, R> { + type Item = OmfSection<'data, 'file, R>; + + fn next(&mut self) -> Option { + if self.index < self.file.segments.len() { + let section = OmfSection { + file: self.file, + index: self.index, + }; + self.index += 1; + Some(section) + } else { + None + } + } +} + +/// A COMDAT section in an OMF file. +#[derive(Debug)] +pub struct OmfComdat<'data, 'file, R: ReadRef<'data>> { + file: &'file OmfFile<'data, R>, + index: usize, + _phantom: core::marker::PhantomData<&'data ()>, +} + +impl<'data, 'file, R: ReadRef<'data>> read::private::Sealed for OmfComdat<'data, 'file, R> {} + +impl<'data, 'file, R: ReadRef<'data>> ObjectComdat<'data> for OmfComdat<'data, 'file, R> { + type SectionIterator = OmfComdatSectionIterator<'data, 'file, R>; + + fn kind(&self) -> ComdatKind { + let comdat = &self.file.comdats[self.index]; + match comdat.selection { + super::OmfComdatSelection::Explicit => ComdatKind::NoDuplicates, + super::OmfComdatSelection::UseAny => ComdatKind::Any, + super::OmfComdatSelection::SameSize => ComdatKind::SameSize, + super::OmfComdatSelection::ExactMatch => ComdatKind::ExactMatch, + } + } + + fn symbol(&self) -> SymbolIndex { + // COMDAT symbols don't have a direct symbol index in OMF + SymbolIndex(usize::MAX) + } + + fn name_bytes(&self) -> Result<&'data [u8]> { + let comdat = &self.file.comdats[self.index]; + Ok(comdat.name) + } + + fn name(&self) -> Result<&'data str> { + let comdat = &self.file.comdats[self.index]; + core::str::from_utf8(comdat.name).map_err(|_| Error("Invalid UTF-8 in COMDAT name")) + } + + fn sections(&self) -> Self::SectionIterator { + let comdat = &self.file.comdats[self.index]; + OmfComdatSectionIterator { + segment_index: (comdat.segment_index as usize).checked_sub(1), + returned: false, + _phantom: core::marker::PhantomData, + } + } +} + +/// An iterator over COMDAT sections. +#[derive(Debug)] +pub struct OmfComdatIterator<'data, 'file, R: ReadRef<'data>> { + pub(super) file: &'file OmfFile<'data, R>, + pub(super) index: usize, +} + +impl<'data, 'file, R: ReadRef<'data>> Iterator for OmfComdatIterator<'data, 'file, R> { + type Item = OmfComdat<'data, 'file, R>; + + fn next(&mut self) -> Option { + if self.index < self.file.comdats.len() { + let comdat = OmfComdat { + file: self.file, + index: self.index, + _phantom: core::marker::PhantomData, + }; + self.index += 1; + Some(comdat) + } else { + None + } + } +} + +/// An iterator over sections in a COMDAT. +#[derive(Debug)] +pub struct OmfComdatSectionIterator<'data, 'file, R: ReadRef<'data>> { + segment_index: Option, + returned: bool, + _phantom: core::marker::PhantomData<(&'data (), &'file (), R)>, +} + +impl<'data, 'file, R: ReadRef<'data>> Iterator for OmfComdatSectionIterator<'data, 'file, R> { + type Item = SectionIndex; + + fn next(&mut self) -> Option { + if !self.returned { + self.returned = true; + self.segment_index.map(|idx| SectionIndex(idx + 1)) + } else { + None + } + } +} diff --git a/src/read/omf/segment.rs b/src/read/omf/segment.rs new file mode 100644 index 0000000..684a032 --- /dev/null +++ b/src/read/omf/segment.rs @@ -0,0 +1,90 @@ +use crate::{read, ObjectSegment, ReadRef, Result, SegmentFlags}; + +use super::OmfFile; + +/// An OMF segment reference. +#[derive(Debug)] +pub struct OmfSegmentRef<'data, 'file, R: ReadRef<'data>> { + file: &'file OmfFile<'data, R>, + index: usize, +} + +impl<'data, 'file, R: ReadRef<'data>> read::private::Sealed for OmfSegmentRef<'data, 'file, R> {} + +impl<'data, 'file, R: ReadRef<'data>> ObjectSegment<'data> for OmfSegmentRef<'data, 'file, R> { + fn address(&self) -> u64 { + 0 + } + + fn size(&self) -> u64 { + self.file.segments[self.index].length as u64 + } + + fn align(&self) -> u64 { + match self.file.segments[self.index].alignment { + crate::omf::SegmentAlignment::Byte => 1, + crate::omf::SegmentAlignment::Word => 2, + crate::omf::SegmentAlignment::Paragraph => 16, + crate::omf::SegmentAlignment::Page => 256, + crate::omf::SegmentAlignment::DWord => 4, + crate::omf::SegmentAlignment::Page4K => 4096, + _ => 1, + } + } + + fn file_range(&self) -> (u64, u64) { + (0, 0) + } + + fn data(&self) -> Result<&'data [u8]> { + // OMF segments don't have direct file mapping + Ok(&[]) + } + + fn data_range(&self, _address: u64, _size: u64) -> Result> { + Ok(None) + } + + fn name_bytes(&self) -> Result> { + Ok(self + .file + .get_name(self.file.segments[self.index].name_index)) + } + + fn name(&self) -> Result> { + let index = self.file.segments[self.index].name_index; + let name_opt = self.file.get_name(index); + match name_opt { + Some(bytes) => Ok(core::str::from_utf8(bytes).ok()), + None => Ok(None), + } + } + + fn flags(&self) -> SegmentFlags { + SegmentFlags::None + } +} + +/// An iterator over OMF segments. +#[derive(Debug)] +pub struct OmfSegmentIterator<'data, 'file, R: ReadRef<'data>> { + pub(super) file: &'file OmfFile<'data, R>, + pub(super) index: usize, +} + +impl<'data, 'file, R: ReadRef<'data>> Iterator for OmfSegmentIterator<'data, 'file, R> { + type Item = OmfSegmentRef<'data, 'file, R>; + + fn next(&mut self) -> Option { + if self.index < self.file.segments.len() { + let segment = OmfSegmentRef { + file: self.file, + index: self.index, + }; + self.index += 1; + Some(segment) + } else { + None + } + } +} diff --git a/src/read/omf/symbol.rs b/src/read/omf/symbol.rs index f009416..3c3dfa4 100644 --- a/src/read/omf/symbol.rs +++ b/src/read/omf/symbol.rs @@ -64,10 +64,10 @@ impl<'data> ObjectSymbol<'data> for OmfSymbol<'data> { } fn is_common(&self) -> bool { - // Communal symbols have segment_index == 0, frame_number == 0, but offset != 0 - // The offset field stores the size of the communal symbol - // This excludes both externals (offset == 0) and absolute symbols (frame_number != 0) - self.segment_index == 0 && self.frame_number == 0 && self.offset != 0 + matches!( + self.class, + super::OmfSymbolClass::Communal | super::OmfSymbolClass::LocalCommunal + ) } fn is_weak(&self) -> bool { @@ -75,19 +75,34 @@ impl<'data> ObjectSymbol<'data> for OmfSymbol<'data> { } fn scope(&self) -> SymbolScope { - if self.segment_index == 0 { - SymbolScope::Unknown - } else { - SymbolScope::Linkage + match self.class { + super::OmfSymbolClass::LocalPublic + | super::OmfSymbolClass::LocalExternal + | super::OmfSymbolClass::LocalCommunal => SymbolScope::Compilation, + super::OmfSymbolClass::Public + | super::OmfSymbolClass::External + | super::OmfSymbolClass::Communal + | super::OmfSymbolClass::ComdatExternal => { + if self.segment_index == 0 { + SymbolScope::Unknown + } else { + SymbolScope::Linkage + } + } } } fn is_global(&self) -> bool { - true + !self.is_local() } fn is_local(&self) -> bool { - false + matches!( + self.class, + super::OmfSymbolClass::LocalPublic + | super::OmfSymbolClass::LocalExternal + | super::OmfSymbolClass::LocalCommunal + ) } fn flags(&self) -> SymbolFlags { @@ -97,34 +112,16 @@ impl<'data> ObjectSymbol<'data> for OmfSymbol<'data> { /// An iterator over OMF symbols. #[derive(Debug)] -pub struct OmfSymbolIterator<'data, 'file> { - pub(super) publics: &'file [OmfSymbol<'data>], - pub(super) externals: &'file [OmfSymbol<'data>], - pub(super) communals: &'file [OmfSymbol<'data>], +pub struct OmfSymbolIterator<'data, 'file, R: ReadRef<'data> = &'data [u8]> { + pub(super) file: &'file OmfFile<'data, R>, pub(super) index: usize, } -impl<'data, 'file> Iterator for OmfSymbolIterator<'data, 'file> { +impl<'data, 'file, R: ReadRef<'data>> Iterator for OmfSymbolIterator<'data, 'file, R> { type Item = OmfSymbol<'data>; fn next(&mut self) -> Option { - let total_publics = self.publics.len(); - let total_externals = self.externals.len(); - let total_before_communals = total_publics + total_externals; - let total = total_before_communals + self.communals.len(); - - if self.index >= total { - return None; - } - - let symbol = if self.index < total_publics { - self.publics[self.index].clone() - } else if self.index < total_before_communals { - self.externals[self.index - total_publics].clone() - } else { - self.communals[self.index - total_before_communals].clone() - }; - + let symbol = self.file.symbols.get(self.index)?.clone(); self.index += 1; Some(symbol) } @@ -140,18 +137,20 @@ impl<'data, 'file, R: ReadRef<'data>> read::private::Sealed for OmfSymbolTable<' impl<'data, 'file, R: ReadRef<'data>> ObjectSymbolTable<'data> for OmfSymbolTable<'data, 'file, R> { type Symbol = OmfSymbol<'data>; - type SymbolIterator = OmfSymbolIterator<'data, 'file>; + type SymbolIterator = OmfSymbolIterator<'data, 'file, R>; fn symbols(&self) -> Self::SymbolIterator { OmfSymbolIterator { - publics: &self.file.publics, - externals: &self.file.externals, - communals: &self.file.communals, + file: self.file, index: 0, } } fn symbol_by_index(&self, index: SymbolIndex) -> Result { - self.file.symbol_by_index(index) + self.file + .symbols + .get(index.0) + .cloned() + .ok_or(Error("Symbol index out of bounds")) } }