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>, } 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.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> { Ok(read::util::data_range( self.bytes()?, self.address(), address, size, )) } fn compressed_file_range(&self) -> Result { Ok(CompressedFileRange::none(self.file_range())) } fn compressed_data(&self) -> Result> { 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> { Ok(None) } fn segment_name(&self) -> Result> { 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> { 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>(header: &Xcoff, data: R, offset: &mut u64) -> Result { 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; type HalfWord: Into; type Xcoff: FileHeader; type Rel: Rel; 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") } }