diff --git a/examples/readobj.rs b/examples/readobj.rs index 8a2a5ac0..b849212b 100644 --- a/examples/readobj.rs +++ b/examples/readobj.rs @@ -437,7 +437,7 @@ mod elf { let mut dynstr = object::StringTable::default(); for s in segments { if let Ok(Some(data)) = s.data_range(endian, data, strtab, strsz) { - dynstr = object::StringTable::new(data); + dynstr = object::StringTable::new(data, 0, data.len() as u64); break; } } diff --git a/src/read/any.rs b/src/read/any.rs index c8773245..80526e99 100644 --- a/src/read/any.rs +++ b/src/read/any.rs @@ -890,11 +890,21 @@ where R: ReadRef<'data>, { #[cfg(feature = "coff")] - Coff((coff::CoffSymbolTable<'data, 'file>, PhantomData)), + Coff((coff::CoffSymbolTable<'data, 'file, R>, PhantomData)), #[cfg(feature = "elf")] - Elf32((elf::ElfSymbolTable32<'data, 'file>, PhantomData)), + Elf32( + ( + elf::ElfSymbolTable32<'data, 'file, Endianness, R>, + PhantomData, + ), + ), #[cfg(feature = "elf")] - Elf64((elf::ElfSymbolTable64<'data, 'file>, PhantomData)), + Elf64( + ( + elf::ElfSymbolTable64<'data, 'file, Endianness, R>, + PhantomData, + ), + ), #[cfg(feature = "macho")] MachO32( ( @@ -910,9 +920,9 @@ where ), ), #[cfg(feature = "pe")] - Pe32((coff::CoffSymbolTable<'data, 'file>, PhantomData)), + Pe32((coff::CoffSymbolTable<'data, 'file, R>, PhantomData)), #[cfg(feature = "pe")] - Pe64((coff::CoffSymbolTable<'data, 'file>, PhantomData)), + Pe64((coff::CoffSymbolTable<'data, 'file, R>, PhantomData)), #[cfg(feature = "wasm")] Wasm((wasm::WasmSymbolTable<'data, 'file>, PhantomData)), } @@ -960,11 +970,21 @@ where R: ReadRef<'data>, { #[cfg(feature = "coff")] - Coff((coff::CoffSymbolIterator<'data, 'file>, PhantomData)), + Coff((coff::CoffSymbolIterator<'data, 'file, R>, PhantomData)), #[cfg(feature = "elf")] - Elf32((elf::ElfSymbolIterator32<'data, 'file>, PhantomData)), + Elf32( + ( + elf::ElfSymbolIterator32<'data, 'file, Endianness, R>, + PhantomData, + ), + ), #[cfg(feature = "elf")] - Elf64((elf::ElfSymbolIterator64<'data, 'file>, PhantomData)), + Elf64( + ( + elf::ElfSymbolIterator64<'data, 'file, Endianness, R>, + PhantomData, + ), + ), #[cfg(feature = "macho")] MachO32( ( @@ -980,9 +1000,9 @@ where ), ), #[cfg(feature = "pe")] - Pe32((coff::CoffSymbolIterator<'data, 'file>, PhantomData)), + Pe32((coff::CoffSymbolIterator<'data, 'file, R>, PhantomData)), #[cfg(feature = "pe")] - Pe64((coff::CoffSymbolIterator<'data, 'file>, PhantomData)), + Pe64((coff::CoffSymbolIterator<'data, 'file, R>, PhantomData)), #[cfg(feature = "wasm")] Wasm((wasm::WasmSymbolIterator<'data, 'file>, PhantomData)), } @@ -1013,11 +1033,21 @@ where R: ReadRef<'data>, { #[cfg(feature = "coff")] - Coff((coff::CoffSymbol<'data, 'file>, PhantomData)), + Coff((coff::CoffSymbol<'data, 'file, R>, PhantomData)), #[cfg(feature = "elf")] - Elf32((elf::ElfSymbol32<'data, 'file>, PhantomData)), + Elf32( + ( + elf::ElfSymbol32<'data, 'file, Endianness, R>, + PhantomData, + ), + ), #[cfg(feature = "elf")] - Elf64((elf::ElfSymbol64<'data, 'file>, PhantomData)), + Elf64( + ( + elf::ElfSymbol64<'data, 'file, Endianness, R>, + PhantomData, + ), + ), #[cfg(feature = "macho")] MachO32( ( @@ -1033,9 +1063,9 @@ where ), ), #[cfg(feature = "pe")] - Pe32((coff::CoffSymbol<'data, 'file>, PhantomData)), + Pe32((coff::CoffSymbol<'data, 'file, R>, PhantomData)), #[cfg(feature = "pe")] - Pe64((coff::CoffSymbol<'data, 'file>, PhantomData)), + Pe64((coff::CoffSymbol<'data, 'file, R>, PhantomData)), #[cfg(feature = "wasm")] Wasm((wasm::WasmSymbol<'data, 'file>, PhantomData)), } diff --git a/src/read/coff/file.rs b/src/read/coff/file.rs index 3ab92ce2..fad2efd9 100644 --- a/src/read/coff/file.rs +++ b/src/read/coff/file.rs @@ -15,10 +15,10 @@ use super::{ /// The common parts of `PeFile` and `CoffFile`. #[derive(Debug)] -pub(crate) struct CoffCommon<'data> { +pub(crate) struct CoffCommon<'data, R: ReadRef<'data>> { pub(crate) sections: SectionTable<'data>, // TODO: ImageSymbolExBytes - pub(crate) symbols: SymbolTable<'data>, + pub(crate) symbols: SymbolTable<'data, R>, pub(crate) image_base: u64, } @@ -26,7 +26,7 @@ pub(crate) struct CoffCommon<'data> { #[derive(Debug)] pub struct CoffFile<'data, R: ReadRef<'data> = &'data [u8]> { pub(super) header: &'data pe::ImageFileHeader, - pub(super) common: CoffCommon<'data>, + pub(super) common: CoffCommon<'data, R>, pub(super) data: R, } @@ -63,9 +63,9 @@ where type SectionIterator = CoffSectionIterator<'data, 'file, R>; type Comdat = CoffComdat<'data, 'file, R>; type ComdatIterator = CoffComdatIterator<'data, 'file, R>; - type Symbol = CoffSymbol<'data, 'file>; - type SymbolIterator = CoffSymbolIterator<'data, 'file>; - type SymbolTable = CoffSymbolTable<'data, 'file>; + type Symbol = CoffSymbol<'data, 'file, R>; + type SymbolIterator = CoffSymbolIterator<'data, 'file, R>; + type SymbolTable = CoffSymbolTable<'data, 'file, R>; type DynamicRelocationIterator = NoDynamicRelocationIterator; fn architecture(&self) -> Architecture { @@ -124,7 +124,7 @@ where } } - fn symbol_by_index(&'file self, index: SymbolIndex) -> Result> { + fn symbol_by_index(&'file self, index: SymbolIndex) -> Result> { let symbol = self.common.symbols.symbol(index.0)?; Ok(CoffSymbol { file: &self.common, @@ -133,7 +133,7 @@ where }) } - fn symbols(&'file self) -> CoffSymbolIterator<'data, 'file> { + fn symbols(&'file self) -> CoffSymbolIterator<'data, 'file, R> { CoffSymbolIterator { file: &self.common, index: 0, @@ -141,11 +141,11 @@ where } #[inline] - fn symbol_table(&'file self) -> Option> { + fn symbol_table(&'file self) -> Option> { Some(CoffSymbolTable { file: &self.common }) } - fn dynamic_symbols(&'file self) -> CoffSymbolIterator<'data, 'file> { + fn dynamic_symbols(&'file self) -> CoffSymbolIterator<'data, 'file, R> { CoffSymbolIterator { file: &self.common, // Hack: don't return any. @@ -154,7 +154,7 @@ where } #[inline] - fn dynamic_symbol_table(&'file self) -> Option> { + fn dynamic_symbol_table(&'file self) -> Option> { None } @@ -232,7 +232,10 @@ impl pe::ImageFileHeader { /// /// `data` must be the entire file data. #[inline] - pub fn symbols<'data, R: ReadRef<'data>>(&self, data: R) -> read::Result> { + pub fn symbols<'data, R: ReadRef<'data>>( + &self, + data: R, + ) -> read::Result> { SymbolTable::parse(self, data) } } diff --git a/src/read/coff/section.rs b/src/read/coff/section.rs index 29d30969..8762e249 100644 --- a/src/read/coff/section.rs +++ b/src/read/coff/section.rs @@ -66,9 +66,9 @@ impl<'data> SectionTable<'data> { /// The returned index is 1-based. /// /// Ignores sections with invalid names. - pub fn section_by_name( + pub fn section_by_name>( &self, - strings: StringTable<'data>, + strings: StringTable<'data, R>, name: &[u8], ) -> Option<(usize, &'data pe::ImageSectionHeader)> { self.sections @@ -310,7 +310,10 @@ impl pe::ImageSectionHeader { /// Return the section name. /// /// This handles decoding names that are offsets into the symbol string table. - pub fn name<'data>(&'data self, strings: StringTable<'data>) -> Result<&'data [u8]> { + pub fn name<'data, R: ReadRef<'data>>( + &'data self, + strings: StringTable<'data, R>, + ) -> Result<&'data [u8]> { let bytes = &self.name; Ok(if bytes[0] == b'/' { let mut offset = 0; diff --git a/src/read/coff/symbol.rs b/src/read/coff/symbol.rs index f638248a..49ee2fc4 100644 --- a/src/read/coff/symbol.rs +++ b/src/read/coff/symbol.rs @@ -17,14 +17,17 @@ use crate::read::{ /// /// Also includes the string table used for the symbol names. #[derive(Debug)] -pub struct SymbolTable<'data> { +pub struct SymbolTable<'data, R = &'data [u8]> +where + R: ReadRef<'data>, +{ symbols: &'data [pe::ImageSymbolBytes], - strings: StringTable<'data>, + strings: StringTable<'data, R>, } -impl<'data> SymbolTable<'data> { +impl<'data, R: ReadRef<'data>> SymbolTable<'data, R> { /// Read the symbol table. - pub fn parse>(header: &pe::ImageFileHeader, data: R) -> Result { + pub fn parse(header: &pe::ImageFileHeader, data: R) -> Result { // The symbol table may not be present. let mut offset = header.pointer_to_symbol_table.get(LE).into(); let (symbols, strings) = if offset != 0 { @@ -37,24 +40,22 @@ impl<'data> SymbolTable<'data> { .read_at::>(offset) .read_error("Missing COFF string table")? .get(LE); - let strings = data - .read_bytes(&mut offset, length.into()) + let str_end = offset + .checked_add(length as u64) .read_error("Invalid COFF string table length")?; + let strings = StringTable::new(data, offset, str_end); (symbols, strings) } else { - (&[][..], &[][..]) + (&[][..], StringTable::default()) }; - Ok(SymbolTable { - symbols, - strings: StringTable::new(strings), - }) + Ok(SymbolTable { symbols, strings }) } /// Return the string table used for the symbol names. #[inline] - pub fn strings(&self) -> StringTable<'data> { + pub fn strings(&self) -> StringTable<'data, R> { self.strings } @@ -74,7 +75,7 @@ impl<'data> SymbolTable<'data> { /// Iterate over the symbols. #[inline] - pub fn iter<'table>(&'table self) -> SymbolIterator<'data, 'table> { + pub fn iter<'table>(&'table self) -> SymbolIterator<'data, 'table, R> { SymbolIterator { symbols: self, index: 0, @@ -153,12 +154,15 @@ impl<'data> SymbolTable<'data> { /// /// Yields the index and symbol structure for each symbol. #[derive(Debug)] -pub struct SymbolIterator<'data, 'table> { - symbols: &'table SymbolTable<'data>, +pub struct SymbolIterator<'data, 'table, R = &'data [u8]> +where + R: ReadRef<'data>, +{ + symbols: &'table SymbolTable<'data, R>, index: usize, } -impl<'data, 'table> Iterator for SymbolIterator<'data, 'table> { +impl<'data, 'table, R: ReadRef<'data>> Iterator for SymbolIterator<'data, 'table, R> { type Item = (usize, &'data pe::ImageSymbol); fn next(&mut self) -> Option { @@ -173,7 +177,10 @@ impl pe::ImageSymbol { /// Parse a COFF symbol name. /// /// `strings` must be the string table used for symbol names. - pub fn name<'data>(&'data self, strings: StringTable<'data>) -> Result<&'data [u8]> { + pub fn name<'data, R: ReadRef<'data>>( + &'data self, + strings: StringTable<'data, R>, + ) -> Result<&'data [u8]> { if self.name[0] == 0 { // If the name starts with 0 then the last 4 bytes are a string table offset. let offset = u32::from_le_bytes(self.name[4..8].try_into().unwrap()); @@ -236,15 +243,20 @@ impl pe::ImageSymbol { /// A symbol table of a `CoffFile`. #[derive(Debug, Clone, Copy)] -pub struct CoffSymbolTable<'data, 'file> { - pub(crate) file: &'file CoffCommon<'data>, +pub struct CoffSymbolTable<'data, 'file, R = &'data [u8]> +where + R: ReadRef<'data>, +{ + pub(crate) file: &'file CoffCommon<'data, R>, } -impl<'data, 'file> read::private::Sealed for CoffSymbolTable<'data, 'file> {} +impl<'data, 'file, R: ReadRef<'data>> read::private::Sealed for CoffSymbolTable<'data, 'file, R> {} -impl<'data, 'file> ObjectSymbolTable<'data> for CoffSymbolTable<'data, 'file> { - type Symbol = CoffSymbol<'data, 'file>; - type SymbolIterator = CoffSymbolIterator<'data, 'file>; +impl<'data, 'file, R: ReadRef<'data>> ObjectSymbolTable<'data> + for CoffSymbolTable<'data, 'file, R> +{ + type Symbol = CoffSymbol<'data, 'file, R>; + type SymbolIterator = CoffSymbolIterator<'data, 'file, R>; fn symbols(&self) -> Self::SymbolIterator { CoffSymbolIterator { @@ -264,19 +276,22 @@ impl<'data, 'file> ObjectSymbolTable<'data> for CoffSymbolTable<'data, 'file> { } /// An iterator over the symbols of a `CoffFile`. -pub struct CoffSymbolIterator<'data, 'file> { - pub(crate) file: &'file CoffCommon<'data>, +pub struct CoffSymbolIterator<'data, 'file, R = &'data [u8]> +where + R: ReadRef<'data>, +{ + pub(crate) file: &'file CoffCommon<'data, R>, pub(crate) index: usize, } -impl<'data, 'file> fmt::Debug for CoffSymbolIterator<'data, 'file> { +impl<'data, 'file, R: ReadRef<'data>> fmt::Debug for CoffSymbolIterator<'data, 'file, R> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("CoffSymbolIterator").finish() } } -impl<'data, 'file> Iterator for CoffSymbolIterator<'data, 'file> { - type Item = CoffSymbol<'data, 'file>; +impl<'data, 'file, R: ReadRef<'data>> Iterator for CoffSymbolIterator<'data, 'file, R> { + type Item = CoffSymbol<'data, 'file, R>; fn next(&mut self) -> Option { let index = self.index; @@ -292,15 +307,18 @@ impl<'data, 'file> Iterator for CoffSymbolIterator<'data, 'file> { /// A symbol of a `CoffFile`. #[derive(Debug, Clone, Copy)] -pub struct CoffSymbol<'data, 'file> { - pub(crate) file: &'file CoffCommon<'data>, +pub struct CoffSymbol<'data, 'file, R = &'data [u8]> +where + R: ReadRef<'data>, +{ + pub(crate) file: &'file CoffCommon<'data, R>, pub(crate) index: SymbolIndex, pub(crate) symbol: &'data pe::ImageSymbol, } -impl<'data, 'file> read::private::Sealed for CoffSymbol<'data, 'file> {} +impl<'data, 'file, R: ReadRef<'data>> read::private::Sealed for CoffSymbol<'data, 'file, R> {} -impl<'data, 'file> ObjectSymbol<'data> for CoffSymbol<'data, 'file> { +impl<'data, 'file, R: ReadRef<'data>> ObjectSymbol<'data> for CoffSymbol<'data, 'file, R> { #[inline] fn index(&self) -> SymbolIndex { self.index diff --git a/src/read/elf/file.rs b/src/read/elf/file.rs index e877e9f4..75f06579 100644 --- a/src/read/elf/file.rs +++ b/src/read/elf/file.rs @@ -36,10 +36,10 @@ where pub(super) data: R, pub(super) header: &'data Elf, pub(super) segments: &'data [Elf::ProgramHeader], - pub(super) sections: SectionTable<'data, Elf>, + pub(super) sections: SectionTable<'data, Elf, R>, pub(super) relocations: RelocationSections, - pub(super) symbols: SymbolTable<'data, Elf>, - pub(super) dynamic_symbols: SymbolTable<'data, Elf>, + pub(super) symbols: SymbolTable<'data, Elf, R>, + pub(super) dynamic_symbols: SymbolTable<'data, Elf, R>, } impl<'data, Elf, R> ElfFile<'data, Elf, R> @@ -143,9 +143,9 @@ where type SectionIterator = ElfSectionIterator<'data, 'file, Elf, R>; type Comdat = ElfComdat<'data, 'file, Elf, R>; type ComdatIterator = ElfComdatIterator<'data, 'file, Elf, R>; - type Symbol = ElfSymbol<'data, 'file, Elf>; - type SymbolIterator = ElfSymbolIterator<'data, 'file, Elf>; - type SymbolTable = ElfSymbolTable<'data, 'file, Elf>; + type Symbol = ElfSymbol<'data, 'file, Elf, R>; + type SymbolIterator = ElfSymbolIterator<'data, 'file, Elf, R>; + type SymbolTable = ElfSymbolTable<'data, 'file, Elf, R>; type DynamicRelocationIterator = ElfDynamicRelocationIterator<'data, 'file, Elf, R>; fn architecture(&self) -> Architecture { @@ -230,7 +230,7 @@ where fn symbol_by_index( &'file self, index: SymbolIndex, - ) -> read::Result> { + ) -> read::Result> { let symbol = self.symbols.symbol(index.0)?; Ok(ElfSymbol { endian: self.endian, @@ -240,7 +240,7 @@ where }) } - fn symbols(&'file self) -> ElfSymbolIterator<'data, 'file, Elf> { + fn symbols(&'file self) -> ElfSymbolIterator<'data, 'file, Elf, R> { ElfSymbolIterator { endian: self.endian, symbols: &self.symbols, @@ -248,14 +248,14 @@ where } } - fn symbol_table(&'file self) -> Option> { + fn symbol_table(&'file self) -> Option> { Some(ElfSymbolTable { endian: self.endian, symbols: &self.symbols, }) } - fn dynamic_symbols(&'file self) -> ElfSymbolIterator<'data, 'file, Elf> { + fn dynamic_symbols(&'file self) -> ElfSymbolIterator<'data, 'file, Elf, R> { ElfSymbolIterator { endian: self.endian, symbols: &self.dynamic_symbols, @@ -263,7 +263,7 @@ where } } - fn dynamic_symbol_table(&'file self) -> Option> { + fn dynamic_symbol_table(&'file self) -> Option> { Some(ElfSymbolTable { endian: self.endian, symbols: &self.dynamic_symbols, @@ -646,16 +646,21 @@ pub trait FileHeader: Debug + Pod { endian: Self::Endian, data: R, sections: &[Self::SectionHeader], - ) -> read::Result> { + ) -> read::Result> { if sections.is_empty() { return Ok(StringTable::default()); } let index = self.shstrndx(endian, data)? as usize; let shstrtab = sections.get(index).read_error("Invalid ELF e_shstrndx")?; - let data = shstrtab - .data(endian, data) - .read_error("Invalid ELF shstrtab data")?; - Ok(StringTable::new(data)) + let strings = if let Some((shstrtab_offset, shstrtab_size)) = shstrtab.file_range(endian) { + let shstrtab_end = shstrtab_offset + .checked_add(shstrtab_size) + .read_error("Invalid ELF shstrtab size")?; + StringTable::new(data, shstrtab_offset, shstrtab_end) + } else { + StringTable::default() + }; + Ok(strings) } /// Return the section table. @@ -663,7 +668,7 @@ pub trait FileHeader: Debug + Pod { &self, endian: Self::Endian, data: R, - ) -> read::Result> { + ) -> read::Result> { let sections = self.section_headers(endian, data)?; let strings = self.section_strings(endian, data, sections)?; Ok(SectionTable::new(sections, strings)) diff --git a/src/read/elf/relocation.rs b/src/read/elf/relocation.rs index afa6a70b..d3e24b2e 100644 --- a/src/read/elf/relocation.rs +++ b/src/read/elf/relocation.rs @@ -23,9 +23,9 @@ impl RelocationSections { /// Create a new mapping using the section table. /// /// Skips relocation sections that do not use the given symbol table section. - pub fn parse( + pub fn parse<'data, Elf: FileHeader, R: ReadRef<'data>>( endian: Elf::Endian, - sections: &SectionTable, + sections: &SectionTable<'data, Elf, R>, symbol_section: usize, ) -> read::Result { let mut relocations = vec![0; sections.len()]; diff --git a/src/read/elf/section.rs b/src/read/elf/section.rs index edbd10cb..4cea033c 100644 --- a/src/read/elf/section.rs +++ b/src/read/elf/section.rs @@ -18,15 +18,18 @@ use super::{ /// /// Also includes the string table used for the section names. #[derive(Debug, Default, Clone, Copy)] -pub struct SectionTable<'data, Elf: FileHeader> { +pub struct SectionTable<'data, Elf: FileHeader, R = &'data [u8]> +where + R: ReadRef<'data>, +{ sections: &'data [Elf::SectionHeader], - strings: StringTable<'data>, + strings: StringTable<'data, R>, } -impl<'data, Elf: FileHeader> SectionTable<'data, Elf> { +impl<'data, Elf: FileHeader, R: ReadRef<'data>> SectionTable<'data, Elf, R> { /// Create a new section table. #[inline] - pub fn new(sections: &'data [Elf::SectionHeader], strings: StringTable<'data>) -> Self { + pub fn new(sections: &'data [Elf::SectionHeader], strings: StringTable<'data, R>) -> Self { SectionTable { sections, strings } } @@ -82,12 +85,12 @@ impl<'data, Elf: FileHeader> SectionTable<'data, Elf> { /// /// Returns an empty symbol table if the symbol table does not exist. #[inline] - pub fn symbols>( + pub fn symbols( &self, endian: Elf::Endian, data: R, sh_type: u32, - ) -> read::Result> { + ) -> read::Result> { debug_assert!(sh_type == elf::SHT_DYNSYM || sh_type == elf::SHT_SYMTAB); let (index, section) = match self @@ -106,12 +109,12 @@ impl<'data, Elf: FileHeader> SectionTable<'data, Elf> { /// /// Returns an error if the section is not a symbol table. #[inline] - pub fn symbol_table_by_index>( + pub fn symbol_table_by_index( &self, endian: Elf::Endian, data: R, index: usize, - ) -> read::Result> { + ) -> read::Result> { let section = self.section(index)?; match section.sh_type(endian) { elf::SHT_DYNSYM | elf::SHT_SYMTAB => {} @@ -425,10 +428,10 @@ pub trait SectionHeader: Debug + Pod { fn sh_entsize(&self, endian: Self::Endian) -> Self::Word; /// Parse the section name from the string table. - fn name<'data>( + fn name<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, - strings: StringTable<'data>, + strings: StringTable<'data, R>, ) -> read::Result<&'data [u8]> { strings .get(self.sh_name(endian)) @@ -489,9 +492,9 @@ pub trait SectionHeader: Debug + Pod { &self, endian: Self::Endian, data: R, - sections: &SectionTable, + sections: &SectionTable<'data, Self::Elf, R>, section_index: usize, - ) -> read::Result>> { + ) -> read::Result>> { let sh_type = self.sh_type(endian); if sh_type != elf::SHT_SYMTAB && sh_type != elf::SHT_DYNSYM { return Ok(None); @@ -541,8 +544,8 @@ pub trait SectionHeader: Debug + Pod { &self, endian: Self::Endian, data: R, - sections: &SectionTable<'data, Self::Elf>, - ) -> read::Result> { + sections: &SectionTable<'data, Self::Elf, R>, + ) -> read::Result> { let sh_type = self.sh_type(endian); if sh_type != elf::SHT_REL && sh_type != elf::SHT_RELA { return Err(Error("Invalid ELF relocation section type")); diff --git a/src/read/elf/symbol.rs b/src/read/elf/symbol.rs index 91a1349e..56c79331 100644 --- a/src/read/elf/symbol.rs +++ b/src/read/elf/symbol.rs @@ -19,14 +19,17 @@ use super::{FileHeader, SectionHeader, SectionTable}; /// /// Also includes the string table used for the symbol names. #[derive(Debug, Clone, Copy)] -pub struct SymbolTable<'data, Elf: FileHeader> { +pub struct SymbolTable<'data, Elf: FileHeader, R = &'data [u8]> +where + R: ReadRef<'data>, +{ section: usize, symbols: &'data [Elf::Sym], - strings: StringTable<'data>, + strings: StringTable<'data, R>, shndx: &'data [u32], } -impl<'data, Elf: FileHeader> Default for SymbolTable<'data, Elf> { +impl<'data, Elf: FileHeader, R: ReadRef<'data>> Default for SymbolTable<'data, Elf, R> { fn default() -> Self { SymbolTable { section: 0, @@ -37,15 +40,15 @@ impl<'data, Elf: FileHeader> Default for SymbolTable<'data, Elf> { } } -impl<'data, Elf: FileHeader> SymbolTable<'data, Elf> { +impl<'data, Elf: FileHeader, R: ReadRef<'data>> SymbolTable<'data, Elf, R> { /// Parse the given symbol table section. - pub fn parse>( + pub fn parse( endian: Elf::Endian, data: R, - sections: &SectionTable, + sections: &SectionTable<'data, Elf, R>, section_index: usize, section: &Elf::SectionHeader, - ) -> read::Result> { + ) -> read::Result> { debug_assert!( section.sh_type(endian) == elf::SHT_DYNSYM || section.sh_type(endian) == elf::SHT_SYMTAB @@ -56,10 +59,14 @@ impl<'data, Elf: FileHeader> SymbolTable<'data, Elf> { .read_error("Invalid ELF symbol table data")?; let strtab = sections.section(section.sh_link(endian) as usize)?; - let strtab_data = strtab - .data(endian, data) - .read_error("Invalid ELF string table data")?; - let strings = StringTable::new(strtab_data); + let strings = if let Some((str_offset, str_size)) = strtab.file_range(endian) { + let str_end = str_offset + .checked_add(str_size) + .read_error("Invalid str_size")?; + StringTable::new(data, str_offset, str_end) + } else { + StringTable::default() + }; let shndx = sections .iter() @@ -91,7 +98,7 @@ impl<'data, Elf: FileHeader> SymbolTable<'data, Elf> { /// Return the string table used for the symbol names. #[inline] - pub fn strings(&self) -> StringTable<'data> { + pub fn strings(&self) -> StringTable<'data, R> { self.strings } @@ -155,28 +162,34 @@ impl<'data, Elf: FileHeader> SymbolTable<'data, Elf> { } /// A symbol table of an `ElfFile32`. -pub type ElfSymbolTable32<'data, 'file, Endian = Endianness> = - ElfSymbolTable<'data, 'file, elf::FileHeader32>; +pub type ElfSymbolTable32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSymbolTable<'data, 'file, elf::FileHeader32, R>; /// A symbol table of an `ElfFile32`. -pub type ElfSymbolTable64<'data, 'file, Endian = Endianness> = - ElfSymbolTable<'data, 'file, elf::FileHeader64>; +pub type ElfSymbolTable64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSymbolTable<'data, 'file, elf::FileHeader64, R>; /// A symbol table of an `ElfFile`. #[derive(Debug, Clone, Copy)] -pub struct ElfSymbolTable<'data, 'file, Elf> +pub struct ElfSymbolTable<'data, 'file, Elf, R = &'data [u8]> where 'data: 'file, Elf: FileHeader, + R: ReadRef<'data>, { pub(super) endian: Elf::Endian, - pub(super) symbols: &'file SymbolTable<'data, Elf>, + pub(super) symbols: &'file SymbolTable<'data, Elf, R>, } -impl<'data, 'file, Elf: FileHeader> read::private::Sealed for ElfSymbolTable<'data, 'file, Elf> {} +impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> read::private::Sealed + for ElfSymbolTable<'data, 'file, Elf, R> +{ +} -impl<'data, 'file, Elf: FileHeader> ObjectSymbolTable<'data> for ElfSymbolTable<'data, 'file, Elf> { - type Symbol = ElfSymbol<'data, 'file, Elf>; - type SymbolIterator = ElfSymbolIterator<'data, 'file, Elf>; +impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> ObjectSymbolTable<'data> + for ElfSymbolTable<'data, 'file, Elf, R> +{ + type Symbol = ElfSymbol<'data, 'file, Elf, R>; + type SymbolIterator = ElfSymbolIterator<'data, 'file, Elf, R>; fn symbols(&self) -> Self::SymbolIterator { ElfSymbolIterator { @@ -198,31 +211,36 @@ impl<'data, 'file, Elf: FileHeader> ObjectSymbolTable<'data> for ElfSymbolTable< } /// An iterator over the symbols of an `ElfFile32`. -pub type ElfSymbolIterator32<'data, 'file, Endian = Endianness> = - ElfSymbolIterator<'data, 'file, elf::FileHeader32>; +pub type ElfSymbolIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSymbolIterator<'data, 'file, elf::FileHeader32, R>; /// An iterator over the symbols of an `ElfFile64`. -pub type ElfSymbolIterator64<'data, 'file, Endian = Endianness> = - ElfSymbolIterator<'data, 'file, elf::FileHeader64>; +pub type ElfSymbolIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSymbolIterator<'data, 'file, elf::FileHeader64, R>; /// An iterator over the symbols of an `ElfFile`. -pub struct ElfSymbolIterator<'data, 'file, Elf> +pub struct ElfSymbolIterator<'data, 'file, Elf, R = &'data [u8]> where 'data: 'file, Elf: FileHeader, + R: ReadRef<'data>, { pub(super) endian: Elf::Endian, - pub(super) symbols: &'file SymbolTable<'data, Elf>, + pub(super) symbols: &'file SymbolTable<'data, Elf, R>, pub(super) index: usize, } -impl<'data, 'file, Elf: FileHeader> fmt::Debug for ElfSymbolIterator<'data, 'file, Elf> { +impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> fmt::Debug + for ElfSymbolIterator<'data, 'file, Elf, R> +{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ElfSymbolIterator").finish() } } -impl<'data, 'file, Elf: FileHeader> Iterator for ElfSymbolIterator<'data, 'file, Elf> { - type Item = ElfSymbol<'data, 'file, Elf>; +impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> Iterator + for ElfSymbolIterator<'data, 'file, Elf, R> +{ + type Item = ElfSymbol<'data, 'file, Elf, R>; fn next(&mut self) -> Option { let index = self.index; @@ -238,28 +256,34 @@ impl<'data, 'file, Elf: FileHeader> Iterator for ElfSymbolIterator<'data, 'file, } /// A symbol of an `ElfFile32`. -pub type ElfSymbol32<'data, 'file, Endian = Endianness> = - ElfSymbol<'data, 'file, elf::FileHeader32>; +pub type ElfSymbol32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSymbol<'data, 'file, elf::FileHeader32, R>; /// A symbol of an `ElfFile64`. -pub type ElfSymbol64<'data, 'file, Endian = Endianness> = - ElfSymbol<'data, 'file, elf::FileHeader64>; +pub type ElfSymbol64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSymbol<'data, 'file, elf::FileHeader64, R>; /// A symbol of an `ElfFile`. #[derive(Debug, Clone, Copy)] -pub struct ElfSymbol<'data, 'file, Elf> +pub struct ElfSymbol<'data, 'file, Elf, R = &'data [u8]> where 'data: 'file, Elf: FileHeader, + R: ReadRef<'data>, { pub(super) endian: Elf::Endian, - pub(super) symbols: &'file SymbolTable<'data, Elf>, + pub(super) symbols: &'file SymbolTable<'data, Elf, R>, pub(super) index: SymbolIndex, pub(super) symbol: &'data Elf::Sym, } -impl<'data, 'file, Elf: FileHeader> read::private::Sealed for ElfSymbol<'data, 'file, Elf> {} +impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> read::private::Sealed + for ElfSymbol<'data, 'file, Elf, R> +{ +} -impl<'data, 'file, Elf: FileHeader> ObjectSymbol<'data> for ElfSymbol<'data, 'file, Elf> { +impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> ObjectSymbol<'data> + for ElfSymbol<'data, 'file, Elf, R> +{ #[inline] fn index(&self) -> SymbolIndex { self.index @@ -390,10 +414,10 @@ pub trait Sym: Debug + Pod { fn st_size(&self, endian: Self::Endian) -> Self::Word; /// Parse the symbol name from the string table. - fn name<'data>( + fn name<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, - strings: StringTable<'data>, + strings: StringTable<'data, R>, ) -> read::Result<&'data [u8]> { strings .get(self.st_name(endian)) diff --git a/src/read/macho/dyld_cache.rs b/src/read/macho/dyld_cache.rs index b1e4b9a4..ee758ce0 100644 --- a/src/read/macho/dyld_cache.rs +++ b/src/read/macho/dyld_cache.rs @@ -192,7 +192,9 @@ impl macho::DyldCacheHeader { impl macho::DyldCacheImageInfo { /// The file system path of this image. pub fn path<'data, R: ReadRef<'data>>(&self, endian: E, data: R) -> Result<&'data [u8]> { - data.read_bytes_at_until(self.path_file_offset.get(endian).into(), 0) + let r_start = self.path_file_offset.get(endian).into(); + let r_end = data.len().read_error("Couldn't get data len()")?; + data.read_bytes_at_until(r_start..r_end, 0) .read_error("Couldn't read dyld cache image path") } diff --git a/src/read/macho/file.rs b/src/read/macho/file.rs index 8b280392..54ae2d83 100644 --- a/src/read/macho/file.rs +++ b/src/read/macho/file.rs @@ -36,7 +36,7 @@ where pub(super) header_offset: u64, pub(super) header: &'data Mach, pub(super) sections: Vec>, - pub(super) symbols: SymbolTable<'data, Mach>, + pub(super) symbols: SymbolTable<'data, Mach, R>, } impl<'data, Mach, R> MachOFile<'data, Mach, R> diff --git a/src/read/macho/load_command.rs b/src/read/macho/load_command.rs index 91e6649a..d7b3041a 100644 --- a/src/read/macho/load_command.rs +++ b/src/read/macho/load_command.rs @@ -331,20 +331,18 @@ impl macho::SymtabCommand { &self, endian: E, data: R, - ) -> Result> { + ) -> Result> { let symbols = data .read_slice_at( self.symoff.get(endian).into(), self.nsyms.get(endian) as usize, ) .read_error("Invalid Mach-O symbol table offset or size")?; - let strings = data - .read_bytes_at( - self.stroff.get(endian).into(), - self.strsize.get(endian).into(), - ) - .read_error("Invalid Mach-O string table offset or size")?; - let strings = StringTable::new(strings); + let str_start: u64 = self.stroff.get(endian).into(); + let str_end = str_start + .checked_add(self.strsize.get(endian).into()) + .read_error("Invalid Mach-O string table length")?; + let strings = StringTable::new(data, str_start, str_end); Ok(SymbolTable::new(symbols, strings)) } } diff --git a/src/read/macho/symbol.rs b/src/read/macho/symbol.rs index 686cc79c..1528d52e 100644 --- a/src/read/macho/symbol.rs +++ b/src/read/macho/symbol.rs @@ -18,12 +18,15 @@ use super::{MachHeader, MachOFile}; /// /// Also includes the string table used for the symbol names. #[derive(Debug, Clone, Copy)] -pub struct SymbolTable<'data, Mach: MachHeader> { +pub struct SymbolTable<'data, Mach: MachHeader, R = &'data [u8]> +where + R: ReadRef<'data>, +{ symbols: &'data [Mach::Nlist], - strings: StringTable<'data>, + strings: StringTable<'data, R>, } -impl<'data, Mach: MachHeader> Default for SymbolTable<'data, Mach> { +impl<'data, Mach: MachHeader, R: ReadRef<'data>> Default for SymbolTable<'data, Mach, R> { fn default() -> Self { SymbolTable { symbols: &[], @@ -32,15 +35,15 @@ impl<'data, Mach: MachHeader> Default for SymbolTable<'data, Mach> { } } -impl<'data, Mach: MachHeader> SymbolTable<'data, Mach> { +impl<'data, Mach: MachHeader, R: ReadRef<'data>> SymbolTable<'data, Mach, R> { #[inline] - pub(super) fn new(symbols: &'data [Mach::Nlist], strings: StringTable<'data>) -> Self { + pub(super) fn new(symbols: &'data [Mach::Nlist], strings: StringTable<'data, R>) -> Self { SymbolTable { symbols, strings } } /// Return the string table used for the symbol names. #[inline] - pub fn strings(&self) -> StringTable<'data> { + pub fn strings(&self) -> StringTable<'data, R> { self.strings } @@ -401,10 +404,10 @@ pub trait Nlist: Debug + Pod { fn n_desc(&self, endian: Self::Endian) -> u16; fn n_value(&self, endian: Self::Endian) -> Self::Word; - fn name<'data>( + fn name<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, - strings: StringTable<'data>, + strings: StringTable<'data, R>, ) -> Result<&'data [u8]> { strings .get(self.n_strx(endian)) diff --git a/src/read/pe/file.rs b/src/read/pe/file.rs index dc09b0ad..429db9b7 100644 --- a/src/read/pe/file.rs +++ b/src/read/pe/file.rs @@ -30,7 +30,7 @@ where pub(super) dos_header: &'data pe::ImageDosHeader, pub(super) nt_headers: &'data Pe, pub(super) data_directories: &'data [pe::ImageDataDirectory], - pub(super) common: CoffCommon<'data>, + pub(super) common: CoffCommon<'data, R>, pub(super) data: R, } @@ -116,9 +116,9 @@ where type SectionIterator = PeSectionIterator<'data, 'file, Pe, R>; type Comdat = PeComdat<'data, 'file, Pe, R>; type ComdatIterator = PeComdatIterator<'data, 'file, Pe, R>; - type Symbol = CoffSymbol<'data, 'file>; - type SymbolIterator = CoffSymbolIterator<'data, 'file>; - type SymbolTable = CoffSymbolTable<'data, 'file>; + type Symbol = CoffSymbol<'data, 'file, R>; + type SymbolIterator = CoffSymbolIterator<'data, 'file, R>; + type SymbolTable = CoffSymbolTable<'data, 'file, R>; type DynamicRelocationIterator = NoDynamicRelocationIterator; fn architecture(&self) -> Architecture { @@ -183,7 +183,7 @@ where PeComdatIterator { file: self } } - fn symbol_by_index(&'file self, index: SymbolIndex) -> Result> { + fn symbol_by_index(&'file self, index: SymbolIndex) -> Result> { let symbol = self.common.symbols.symbol(index.0)?; Ok(CoffSymbol { file: &self.common, @@ -192,18 +192,18 @@ where }) } - fn symbols(&'file self) -> CoffSymbolIterator<'data, 'file> { + fn symbols(&'file self) -> CoffSymbolIterator<'data, 'file, R> { CoffSymbolIterator { file: &self.common, index: 0, } } - fn symbol_table(&'file self) -> Option> { + fn symbol_table(&'file self) -> Option> { Some(CoffSymbolTable { file: &self.common }) } - fn dynamic_symbols(&'file self) -> CoffSymbolIterator<'data, 'file> { + fn dynamic_symbols(&'file self) -> CoffSymbolIterator<'data, 'file, R> { CoffSymbolIterator { file: &self.common, // Hack: don't return any. @@ -211,7 +211,7 @@ where } } - fn dynamic_symbol_table(&'file self) -> Option> { + fn dynamic_symbol_table(&'file self) -> Option> { None } @@ -647,7 +647,7 @@ pub trait ImageNtHeaders: Debug + Pod { /// /// `data` must be the entire file data. #[inline] - fn symbols<'data, R: ReadRef<'data>>(&self, data: R) -> read::Result> { + fn symbols<'data, R: ReadRef<'data>>(&self, data: R) -> read::Result> { SymbolTable::parse(self.file_header(), data) } } diff --git a/src/read/read_cache.rs b/src/read/read_cache.rs index 1eaacf69..19a98a44 100644 --- a/src/read/read_cache.rs +++ b/src/read/read_cache.rs @@ -1,3 +1,4 @@ +use core::ops::Range; use std::boxed::Box; use std::cell::RefCell; use std::collections::hash_map::Entry; @@ -90,19 +91,25 @@ impl<'a, R: Read + Seek> ReadRef<'a> for &'a ReadCache { Ok(unsafe { mem::transmute::<&[u8], &[u8]>(buf) }) } - fn read_bytes_at_until(self, offset: u64, delimiter: u8) -> Result<&'a [u8], ()> { + fn read_bytes_at_until(self, range: Range, delimiter: u8) -> Result<&'a [u8], ()> { let cache = &mut *self.cache.borrow_mut(); - let buf = match cache.strings.entry((offset, delimiter)) { + let buf = match cache.strings.entry((range.start, delimiter)) { Entry::Occupied(entry) => entry.into_mut(), Entry::Vacant(entry) => { cache .read - .seek(SeekFrom::Start(offset as u64)) + .seek(SeekFrom::Start(range.start)) .map_err(|_| ())?; + + let max_check: usize = (range.end - range.start).try_into().map_err(|_| ())?; + // Strings should be relatively small. + // TODO: make this configurable? + let max_check = max_check.min(4096); + let mut bytes = Vec::new(); let mut checked = 0; loop { - bytes.resize(checked + 256, 0); + bytes.resize((checked + 256).min(max_check), 0); let read = cache.read.read(&mut bytes[checked..]).map_err(|_| ())?; if read == 0 { return Err(()); @@ -112,9 +119,7 @@ impl<'a, R: Read + Seek> ReadRef<'a> for &'a ReadCache { break entry.insert(bytes.into_boxed_slice()); } checked += read; - // Strings should be relatively small. - // TODO: make this configurable? - if checked > 4096 { + if checked >= max_check { return Err(()); } } @@ -166,11 +171,12 @@ impl<'a, R: Read + Seek> ReadRef<'a> for ReadCacheRange<'a, R> { self.r.read_bytes_at(r_offset, size) } - fn read_bytes_at_until(self, offset: u64, delimiter: u8) -> Result<&'a [u8], ()> { - let r_offset = self.offset.checked_add(offset).ok_or(())?; - let bytes = self.r.read_bytes_at_until(r_offset, delimiter)?; + fn read_bytes_at_until(self, range: Range, delimiter: u8) -> Result<&'a [u8], ()> { + let r_start = self.offset.checked_add(range.start).ok_or(())?; + let r_end = self.offset.checked_add(range.end).ok_or(())?; + let bytes = self.r.read_bytes_at_until(r_start..r_end, delimiter)?; let size = bytes.len().try_into().map_err(|_| ())?; - let end = offset.checked_add(size).ok_or(())?; + let end = range.start.checked_add(size).ok_or(())?; if end > self.size { return Err(()); } diff --git a/src/read/read_ref.rs b/src/read/read_ref.rs index 55f58755..dc597514 100644 --- a/src/read/read_ref.rs +++ b/src/read/read_ref.rs @@ -1,6 +1,7 @@ #![allow(clippy::len_without_is_empty)] use core::convert::TryInto; +use core::ops::Range; use core::{mem, result}; use crate::pod::{from_bytes, slice_from_bytes, Pod}; @@ -42,13 +43,13 @@ pub trait ReadRef<'a>: Clone + Copy { /// Returns an error if offset or size are out of bounds. fn read_bytes_at(self, offset: u64, size: u64) -> Result<&'a [u8]>; - /// Get a reference to a delimited `u8` slice at the given offset. + /// Get a reference to a delimited `u8` slice which starts at range.start. /// /// Does not include the delimiter. /// /// Returns an error if offset is out of bounds or the delimiter is - /// not found. - fn read_bytes_at_until(self, offset: u64, delimiter: u8) -> Result<&'a [u8]>; + /// not found in the range. + fn read_bytes_at_until(self, range: Range, delimiter: u8) -> Result<&'a [u8]>; /// Get a reference to a `u8` slice at the given offset, and update the offset. /// @@ -121,9 +122,10 @@ impl<'a> ReadRef<'a> for &'a [u8] { self.get(offset..).ok_or(())?.get(..size).ok_or(()) } - fn read_bytes_at_until(self, offset: u64, delimiter: u8) -> Result<&'a [u8]> { - let offset: usize = offset.try_into().map_err(|_| ())?; - let bytes = self.get(offset..).ok_or(())?; + fn read_bytes_at_until(self, range: Range, delimiter: u8) -> Result<&'a [u8]> { + let start: usize = range.start.try_into().map_err(|_| ())?; + let end: usize = range.end.try_into().map_err(|_| ())?; + let bytes = self.get(start..end).ok_or(())?; match memchr::memchr(delimiter, bytes) { Some(len) => { // This will never fail. diff --git a/src/read/util.rs b/src/read/util.rs index 1e303bc2..e851b7a2 100644 --- a/src/read/util.rs +++ b/src/read/util.rs @@ -1,6 +1,6 @@ -use core::convert::TryInto; +use core::{convert::TryInto, marker::PhantomData}; -use crate::pod::Bytes; +use crate::ReadRef; #[allow(dead_code)] #[inline] @@ -23,19 +23,47 @@ pub(crate) fn data_range( /// A table of zero-terminated strings. /// /// This is used for most file formats. -#[derive(Debug, Default, Clone, Copy)] -pub struct StringTable<'data> { - data: Bytes<'data>, +#[derive(Debug, Clone, Copy)] +pub struct StringTable<'data, R = &'data [u8]> +where + R: ReadRef<'data>, +{ + data: Option, + start: u64, + end: u64, + marker: PhantomData<&'data ()>, } -impl<'data> StringTable<'data> { +impl<'data, R: ReadRef<'data>> StringTable<'data, R> { /// Interpret the given data as a string table. - pub fn new(data: &'data [u8]) -> Self { - StringTable { data: Bytes(data) } + pub fn new(data: R, start: u64, end: u64) -> Self { + StringTable { + data: Some(data), + start, + end, + marker: PhantomData, + } } /// Return the string at the given offset. pub fn get(&self, offset: u32) -> Result<&'data [u8], ()> { - self.data.read_string_at(offset as usize) + match self.data { + Some(data) => { + let r_start = self.start.checked_add(offset.into()).ok_or(())?; + data.read_bytes_at_until(r_start..self.end, 0) + } + None => Err(()), + } + } +} + +impl<'data, R: ReadRef<'data>> Default for StringTable<'data, R> { + fn default() -> Self { + StringTable { + data: None, + start: 0, + end: 0, + marker: PhantomData, + } } }