Rework internal symbol layout; support local records

This commit is contained in:
Luke Street
2025-09-24 17:30:45 -06:00
parent cbfb7e2b15
commit 3ceabdf081
8 changed files with 416 additions and 423 deletions
+5 -10
View File
@@ -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;
}
+1 -1
View File
@@ -1193,7 +1193,7 @@ where
),
),
#[cfg(feature = "omf")]
Omf((omf::OmfSymbolIterator<'data, 'file>, PhantomData<R>)),
Omf((omf::OmfSymbolIterator<'data, 'file, R>, PhantomData<R>)),
}
impl<'data, 'file, R: ReadRef<'data>> Iterator for SymbolIterator<'data, 'file, R> {
+27 -238
View File
@@ -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<Self::Symbol<'_>> {
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<alloc::vec::Vec<Import<'data>>> {
// 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<alloc::vec::Vec<Export<'data>>> {
// 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<Option<&'data [u8]>> {
Ok(None)
}
fn name_bytes(&self) -> Result<Option<&'data [u8]>> {
Ok(self
.file
.get_name(self.file.segments[self.index].name_index))
}
fn name(&self) -> Result<Option<&'data str>> {
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<Self::Item> {
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<Self::Item> {
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<Self::Item> {
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<usize>,
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<Self::Item> {
if !self.returned {
self.returned = true;
self.segment_index.map(|idx| SectionIndex(idx + 1))
} else {
None
}
}
}
+125 -119
View File
@@ -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<OmfSegment<'data>>,
/// Public symbols
publics: Vec<OmfSymbol<'data>>,
/// External symbols
externals: Vec<OmfSymbol<'data>>,
/// Communal symbols from COMDEF
communals: Vec<OmfSymbol<'data>>,
/// All symbols (publics, externals, communals, locals) in occurrence order
symbols: Vec<OmfSymbol<'data>>,
/// Maps external-name table index (1-based) to SymbolIndex
external_order: Vec<read::SymbolIndex>,
/// COMDAT sections
comdats: Vec<OmfComdatData<'data>>,
/// Name table (LNAMES/LLNAMES)
@@ -75,13 +95,15 @@ pub struct OmfSegment<'data> {
pub relocations: Vec<OmfRelocation>,
}
/// 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<read::SymbolKind> = 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::<Vec<_>>();
.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<Vec<u8>> {
fn expand_lidata_block(&self, data: &[u8]) -> Result<Vec<u8>> {
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<usize> {
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<OmfSymbol<'data>> {
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<usize> {
fn lidata_block_size(data: &[u8]) -> Result<usize> {
let mut offset = 0;
// Read repeat count
@@ -1398,8 +1405,7 @@ fn lidata_block_size_impl(data: &[u8]) -> Result<usize> {
} 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..])?;
}
}
+4 -12
View File
@@ -15,11 +15,7 @@ impl<'data, 'file, R: read::ReadRef<'data>> Iterator for OmfRelocationIterator<'
fn next(&mut self) -> Option<Self::Item> {
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
+128 -6
View File
@@ -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<Option<&'data [u8]>> {
@@ -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<Self::Item> {
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<Self::Item> {
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<usize>,
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<Self::Item> {
if !self.returned {
self.returned = true;
self.segment_index.map(|idx| SectionIndex(idx + 1))
} else {
None
}
}
}
+90
View File
@@ -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<Option<&'data [u8]>> {
Ok(None)
}
fn name_bytes(&self) -> Result<Option<&'data [u8]>> {
Ok(self
.file
.get_name(self.file.segments[self.index].name_index))
}
fn name(&self) -> Result<Option<&'data str>> {
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<Self::Item> {
if self.index < self.file.segments.len() {
let segment = OmfSegmentRef {
file: self.file,
index: self.index,
};
self.index += 1;
Some(segment)
} else {
None
}
}
}
+36 -37
View File
@@ -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<SectionIndex, SymbolIndex> {
@@ -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<Self::Item> {
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::Symbol> {
self.file.symbol_by_index(index)
self.file
.symbols
.get(index.0)
.cloned()
.ok_or(Error("Symbol index out of bounds"))
}
}