diff options
Diffstat (limited to 'vendor/object/src/read/xcoff/section.rs')
-rw-r--r-- | vendor/object/src/read/xcoff/section.rs | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/vendor/object/src/read/xcoff/section.rs b/vendor/object/src/read/xcoff/section.rs new file mode 100644 index 0000000..8a36bcf --- /dev/null +++ b/vendor/object/src/read/xcoff/section.rs @@ -0,0 +1,431 @@ +use core::fmt::Debug; +use core::{iter, result, slice, str}; + +use crate::{ + xcoff, BigEndian as BE, CompressedData, CompressedFileRange, Pod, SectionFlags, SectionKind, +}; + +use crate::read::{self, Error, ObjectSection, ReadError, ReadRef, Result, SectionIndex}; + +use super::{AuxHeader, FileHeader, Rel, XcoffFile, XcoffRelocationIterator}; + +/// An iterator for the sections in an [`XcoffFile32`](super::XcoffFile32). +pub type XcoffSectionIterator32<'data, 'file, R = &'data [u8]> = + XcoffSectionIterator<'data, 'file, xcoff::FileHeader32, R>; +/// An iterator for the sections in an [`XcoffFile64`](super::XcoffFile64). +pub type XcoffSectionIterator64<'data, 'file, R = &'data [u8]> = + XcoffSectionIterator<'data, 'file, xcoff::FileHeader64, R>; + +/// An iterator for the sections in an [`XcoffFile`]. +#[derive(Debug)] +pub struct XcoffSectionIterator<'data, 'file, Xcoff, R = &'data [u8]> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + pub(super) file: &'file XcoffFile<'data, Xcoff, R>, + pub(super) iter: iter::Enumerate<slice::Iter<'data, Xcoff::SectionHeader>>, +} + +impl<'data, 'file, Xcoff, R> Iterator for XcoffSectionIterator<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + type Item = XcoffSection<'data, 'file, Xcoff, R>; + + fn next(&mut self) -> Option<Self::Item> { + self.iter.next().map(|(index, section)| XcoffSection { + index: SectionIndex(index + 1), + file: self.file, + section, + }) + } +} + +/// A section in an [`XcoffFile32`](super::XcoffFile32). +pub type XcoffSection32<'data, 'file, R = &'data [u8]> = + XcoffSection<'data, 'file, xcoff::FileHeader32, R>; +/// A section in an [`XcoffFile64`](super::XcoffFile64). +pub type XcoffSection64<'data, 'file, R = &'data [u8]> = + XcoffSection<'data, 'file, xcoff::FileHeader64, R>; + +/// A section in an [`XcoffFile`]. +/// +/// Most functionality is provided by the [`ObjectSection`] trait implementation. +#[derive(Debug)] +pub struct XcoffSection<'data, 'file, Xcoff, R = &'data [u8]> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + pub(super) file: &'file XcoffFile<'data, Xcoff, R>, + pub(super) section: &'data Xcoff::SectionHeader, + pub(super) index: SectionIndex, +} + +impl<'data, 'file, Xcoff: FileHeader, R: ReadRef<'data>> XcoffSection<'data, 'file, Xcoff, R> { + fn bytes(&self) -> Result<&'data [u8]> { + self.section + .data(self.file.data) + .read_error("Invalid XCOFF section offset or size") + } +} + +impl<'data, 'file, Xcoff, R> read::private::Sealed for XcoffSection<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Xcoff, R> ObjectSection<'data> for XcoffSection<'data, 'file, Xcoff, R> +where + Xcoff: FileHeader, + R: ReadRef<'data>, +{ + type RelocationIterator = XcoffRelocationIterator<'data, 'file, Xcoff, R>; + + fn index(&self) -> SectionIndex { + self.index + } + + fn address(&self) -> u64 { + self.section.s_paddr().into() + } + + fn size(&self) -> u64 { + self.section.s_size().into() + } + + fn align(&self) -> u64 { + // The default section alignment is 4. + if let Some(aux_header) = self.file.aux_header { + match self.kind() { + SectionKind::Text => aux_header.o_algntext().into(), + SectionKind::Data => aux_header.o_algndata().into(), + _ => 4, + } + } else { + 4 + } + } + + fn file_range(&self) -> Option<(u64, u64)> { + self.section.file_range() + } + + fn data(&self) -> Result<&'data [u8]> { + self.bytes() + } + + fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> { + Ok(read::util::data_range( + self.bytes()?, + self.address(), + address, + size, + )) + } + + fn compressed_file_range(&self) -> Result<CompressedFileRange> { + Ok(CompressedFileRange::none(self.file_range())) + } + + fn compressed_data(&self) -> Result<CompressedData<'data>> { + self.data().map(CompressedData::none) + } + + fn name_bytes(&self) -> read::Result<&[u8]> { + Ok(self.section.name()) + } + + fn name(&self) -> read::Result<&str> { + let name = self.name_bytes()?; + str::from_utf8(name) + .ok() + .read_error("Non UTF-8 XCOFF section name") + } + + fn segment_name_bytes(&self) -> Result<Option<&[u8]>> { + Ok(None) + } + + fn segment_name(&self) -> Result<Option<&str>> { + Ok(None) + } + + fn kind(&self) -> SectionKind { + let section_type = self.section.s_flags() as u16; + if section_type & xcoff::STYP_TEXT != 0 { + SectionKind::Text + } else if section_type & xcoff::STYP_DATA != 0 { + SectionKind::Data + } else if section_type & xcoff::STYP_TDATA != 0 { + SectionKind::Tls + } else if section_type & xcoff::STYP_BSS != 0 { + SectionKind::UninitializedData + } else if section_type & xcoff::STYP_TBSS != 0 { + SectionKind::UninitializedTls + } else if section_type & (xcoff::STYP_DEBUG | xcoff::STYP_DWARF) != 0 { + SectionKind::Debug + } else if section_type & (xcoff::STYP_LOADER | xcoff::STYP_OVRFLO) != 0 { + SectionKind::Metadata + } else if section_type + & (xcoff::STYP_INFO | xcoff::STYP_EXCEPT | xcoff::STYP_PAD | xcoff::STYP_TYPCHK) + != 0 + { + SectionKind::Other + } else { + SectionKind::Unknown + } + } + + fn relocations(&self) -> Self::RelocationIterator { + let rel = self.section.relocations(self.file.data).unwrap_or(&[]); + XcoffRelocationIterator { + file: self.file, + relocations: rel.iter(), + } + } + + fn flags(&self) -> SectionFlags { + SectionFlags::Xcoff { + s_flags: self.section.s_flags(), + } + } + + fn uncompressed_data(&self) -> Result<alloc::borrow::Cow<'data, [u8]>> { + self.compressed_data()?.decompress() + } +} + +/// The table of section headers in an XCOFF file. +/// +/// Returned by [`FileHeader::sections`]. +#[derive(Debug, Clone, Copy)] +pub struct SectionTable<'data, Xcoff: FileHeader> { + sections: &'data [Xcoff::SectionHeader], +} + +impl<'data, Xcoff> Default for SectionTable<'data, Xcoff> +where + Xcoff: FileHeader, +{ + fn default() -> Self { + Self { sections: &[] } + } +} + +impl<'data, Xcoff> SectionTable<'data, Xcoff> +where + Xcoff: FileHeader, +{ + /// Parse the section table. + /// + /// `data` must be the entire file data. + /// `offset` must be after the optional file header. + pub fn parse<R: ReadRef<'data>>(header: &Xcoff, data: R, offset: &mut u64) -> Result<Self> { + let section_num = header.f_nscns(); + if section_num == 0 { + return Ok(SectionTable::default()); + } + let sections = data + .read_slice(offset, section_num as usize) + .read_error("Invalid XCOFF section headers")?; + Ok(SectionTable { sections }) + } + + /// Iterate over the section headers. + #[inline] + pub fn iter(&self) -> slice::Iter<'data, Xcoff::SectionHeader> { + self.sections.iter() + } + + /// Return true if the section table is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.sections.is_empty() + } + + /// The number of section headers. + #[inline] + pub fn len(&self) -> usize { + self.sections.len() + } + + /// Return the section header at the given index. + /// + /// The index is 1-based. + pub fn section(&self, index: SectionIndex) -> read::Result<&'data Xcoff::SectionHeader> { + self.sections + .get(index.0.wrapping_sub(1)) + .read_error("Invalid XCOFF section index") + } +} + +/// A trait for generic access to [`xcoff::SectionHeader32`] and [`xcoff::SectionHeader64`]. +#[allow(missing_docs)] +pub trait SectionHeader: Debug + Pod { + type Word: Into<u64>; + type HalfWord: Into<u32>; + type Xcoff: FileHeader<SectionHeader = Self, Word = Self::Word>; + type Rel: Rel<Word = Self::Word>; + + fn s_name(&self) -> &[u8; 8]; + fn s_paddr(&self) -> Self::Word; + fn s_vaddr(&self) -> Self::Word; + fn s_size(&self) -> Self::Word; + fn s_scnptr(&self) -> Self::Word; + fn s_relptr(&self) -> Self::Word; + fn s_lnnoptr(&self) -> Self::Word; + fn s_nreloc(&self) -> Self::HalfWord; + fn s_nlnno(&self) -> Self::HalfWord; + fn s_flags(&self) -> u32; + + /// Return the section name. + fn name(&self) -> &[u8] { + let sectname = &self.s_name()[..]; + match memchr::memchr(b'\0', sectname) { + Some(end) => §name[..end], + None => sectname, + } + } + + /// Return the offset and size of the section in the file. + fn file_range(&self) -> Option<(u64, u64)> { + Some((self.s_scnptr().into(), self.s_size().into())) + } + + /// Return the section data. + /// + /// Returns `Ok(&[])` if the section has no data. + /// Returns `Err` for invalid values. + fn data<'data, R: ReadRef<'data>>(&self, data: R) -> result::Result<&'data [u8], ()> { + if let Some((offset, size)) = self.file_range() { + data.read_bytes_at(offset, size) + } else { + Ok(&[]) + } + } + + /// Read the relocations. + fn relocations<'data, R: ReadRef<'data>>(&self, data: R) -> read::Result<&'data [Self::Rel]>; +} + +impl SectionHeader for xcoff::SectionHeader32 { + type Word = u32; + type HalfWord = u16; + type Xcoff = xcoff::FileHeader32; + type Rel = xcoff::Rel32; + + fn s_name(&self) -> &[u8; 8] { + &self.s_name + } + + fn s_paddr(&self) -> Self::Word { + self.s_paddr.get(BE) + } + + fn s_vaddr(&self) -> Self::Word { + self.s_vaddr.get(BE) + } + + fn s_size(&self) -> Self::Word { + self.s_size.get(BE) + } + + fn s_scnptr(&self) -> Self::Word { + self.s_scnptr.get(BE) + } + + fn s_relptr(&self) -> Self::Word { + self.s_relptr.get(BE) + } + + fn s_lnnoptr(&self) -> Self::Word { + self.s_lnnoptr.get(BE) + } + + fn s_nreloc(&self) -> Self::HalfWord { + self.s_nreloc.get(BE) + } + + fn s_nlnno(&self) -> Self::HalfWord { + self.s_nlnno.get(BE) + } + + fn s_flags(&self) -> u32 { + self.s_flags.get(BE) + } + + /// Read the relocations in a XCOFF32 file. + /// + /// `data` must be the entire file data. + fn relocations<'data, R: ReadRef<'data>>(&self, data: R) -> read::Result<&'data [Self::Rel]> { + let reloc_num = self.s_nreloc() as usize; + // TODO: If more than 65,534 relocation entries are required, the field value will be 65535, + // and an STYP_OVRFLO section header will contain the actual count of relocation entries in + // the s_paddr field. + if reloc_num == 65535 { + return Err(Error("Overflow section is not supported yet.")); + } + data.read_slice_at(self.s_relptr().into(), reloc_num) + .read_error("Invalid XCOFF relocation offset or number") + } +} + +impl SectionHeader for xcoff::SectionHeader64 { + type Word = u64; + type HalfWord = u32; + type Xcoff = xcoff::FileHeader64; + type Rel = xcoff::Rel64; + + fn s_name(&self) -> &[u8; 8] { + &self.s_name + } + + fn s_paddr(&self) -> Self::Word { + self.s_paddr.get(BE) + } + + fn s_vaddr(&self) -> Self::Word { + self.s_vaddr.get(BE) + } + + fn s_size(&self) -> Self::Word { + self.s_size.get(BE) + } + + fn s_scnptr(&self) -> Self::Word { + self.s_scnptr.get(BE) + } + + fn s_relptr(&self) -> Self::Word { + self.s_relptr.get(BE) + } + + fn s_lnnoptr(&self) -> Self::Word { + self.s_lnnoptr.get(BE) + } + + fn s_nreloc(&self) -> Self::HalfWord { + self.s_nreloc.get(BE) + } + + fn s_nlnno(&self) -> Self::HalfWord { + self.s_nlnno.get(BE) + } + + fn s_flags(&self) -> u32 { + self.s_flags.get(BE) + } + + /// Read the relocations in a XCOFF64 file. + /// + /// `data` must be the entire file data. + fn relocations<'data, R: ReadRef<'data>>(&self, data: R) -> read::Result<&'data [Self::Rel]> { + data.read_slice_at(self.s_relptr(), self.s_nreloc() as usize) + .read_error("Invalid XCOFF relocation offset or number") + } +} |