object/read/xcoff/
section.rs

1use core::fmt::Debug;
2use core::{iter, result, slice, str};
3
4use crate::endian::BigEndian as BE;
5use crate::pod::Pod;
6use crate::read::{
7    self, CompressedData, CompressedFileRange, Error, ObjectSection, ReadError, ReadRef,
8    RelocationMap, Result, SectionFlags, SectionIndex, SectionKind,
9};
10use crate::xcoff;
11
12use super::{AuxHeader, FileHeader, Rel, XcoffFile, XcoffRelocationIterator};
13
14/// An iterator for the sections in an [`XcoffFile32`](super::XcoffFile32).
15pub type XcoffSectionIterator32<'data, 'file, R = &'data [u8]> =
16    XcoffSectionIterator<'data, 'file, xcoff::FileHeader32, R>;
17/// An iterator for the sections in an [`XcoffFile64`](super::XcoffFile64).
18pub type XcoffSectionIterator64<'data, 'file, R = &'data [u8]> =
19    XcoffSectionIterator<'data, 'file, xcoff::FileHeader64, R>;
20
21/// An iterator for the sections in an [`XcoffFile`].
22#[derive(Debug)]
23pub struct XcoffSectionIterator<'data, 'file, Xcoff, R = &'data [u8]>
24where
25    Xcoff: FileHeader,
26    R: ReadRef<'data>,
27{
28    pub(super) file: &'file XcoffFile<'data, Xcoff, R>,
29    pub(super) iter: iter::Enumerate<slice::Iter<'data, Xcoff::SectionHeader>>,
30}
31
32impl<'data, 'file, Xcoff, R> Iterator for XcoffSectionIterator<'data, 'file, Xcoff, R>
33where
34    Xcoff: FileHeader,
35    R: ReadRef<'data>,
36{
37    type Item = XcoffSection<'data, 'file, Xcoff, R>;
38
39    fn next(&mut self) -> Option<Self::Item> {
40        self.iter.next().map(|(index, section)| XcoffSection {
41            index: SectionIndex(index + 1),
42            file: self.file,
43            section,
44        })
45    }
46}
47
48/// A section in an [`XcoffFile32`](super::XcoffFile32).
49pub type XcoffSection32<'data, 'file, R = &'data [u8]> =
50    XcoffSection<'data, 'file, xcoff::FileHeader32, R>;
51/// A section in an [`XcoffFile64`](super::XcoffFile64).
52pub type XcoffSection64<'data, 'file, R = &'data [u8]> =
53    XcoffSection<'data, 'file, xcoff::FileHeader64, R>;
54
55/// A section in an [`XcoffFile`].
56///
57/// Most functionality is provided by the [`ObjectSection`] trait implementation.
58#[derive(Debug)]
59pub struct XcoffSection<'data, 'file, Xcoff, R = &'data [u8]>
60where
61    Xcoff: FileHeader,
62    R: ReadRef<'data>,
63{
64    pub(super) file: &'file XcoffFile<'data, Xcoff, R>,
65    pub(super) section: &'data Xcoff::SectionHeader,
66    pub(super) index: SectionIndex,
67}
68
69impl<'data, 'file, Xcoff: FileHeader, R: ReadRef<'data>> XcoffSection<'data, 'file, Xcoff, R> {
70    /// Get the XCOFF file containing this section.
71    pub fn xcoff_file(&self) -> &'file XcoffFile<'data, Xcoff, R> {
72        self.file
73    }
74
75    /// Get the raw XCOFF section header.
76    pub fn xcoff_section(&self) -> &'data Xcoff::SectionHeader {
77        self.section
78    }
79
80    /// Get the raw XCOFF relocation entries for this section.
81    pub fn xcoff_relocations(&self) -> Result<&'data [Xcoff::Rel]> {
82        self.section.relocations(self.file.data)
83    }
84
85    fn bytes(&self) -> Result<&'data [u8]> {
86        self.section
87            .data(self.file.data)
88            .read_error("Invalid XCOFF section offset or size")
89    }
90}
91
92impl<'data, 'file, Xcoff, R> read::private::Sealed for XcoffSection<'data, 'file, Xcoff, R>
93where
94    Xcoff: FileHeader,
95    R: ReadRef<'data>,
96{
97}
98
99impl<'data, 'file, Xcoff, R> ObjectSection<'data> for XcoffSection<'data, 'file, Xcoff, R>
100where
101    Xcoff: FileHeader,
102    R: ReadRef<'data>,
103{
104    type RelocationIterator = XcoffRelocationIterator<'data, 'file, Xcoff, R>;
105
106    fn index(&self) -> SectionIndex {
107        self.index
108    }
109
110    fn address(&self) -> u64 {
111        self.section.s_paddr().into()
112    }
113
114    fn size(&self) -> u64 {
115        self.section.s_size().into()
116    }
117
118    fn align(&self) -> u64 {
119        // The default section alignment is 4.
120        if let Some(aux_header) = self.file.aux_header {
121            match self.kind() {
122                SectionKind::Text => aux_header.o_algntext().into(),
123                SectionKind::Data => aux_header.o_algndata().into(),
124                _ => 4,
125            }
126        } else {
127            4
128        }
129    }
130
131    fn file_range(&self) -> Option<(u64, u64)> {
132        self.section.file_range()
133    }
134
135    fn data(&self) -> Result<&'data [u8]> {
136        self.bytes()
137    }
138
139    fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> {
140        Ok(read::util::data_range(
141            self.bytes()?,
142            self.address(),
143            address,
144            size,
145        ))
146    }
147
148    fn compressed_file_range(&self) -> Result<CompressedFileRange> {
149        Ok(CompressedFileRange::none(self.file_range()))
150    }
151
152    fn compressed_data(&self) -> Result<CompressedData<'data>> {
153        self.data().map(CompressedData::none)
154    }
155
156    fn name_bytes(&self) -> read::Result<&'data [u8]> {
157        Ok(self.section.name())
158    }
159
160    fn name(&self) -> read::Result<&'data str> {
161        let name = self.name_bytes()?;
162        str::from_utf8(name)
163            .ok()
164            .read_error("Non UTF-8 XCOFF section name")
165    }
166
167    fn segment_name_bytes(&self) -> Result<Option<&[u8]>> {
168        Ok(None)
169    }
170
171    fn segment_name(&self) -> Result<Option<&str>> {
172        Ok(None)
173    }
174
175    fn kind(&self) -> SectionKind {
176        let section_type = self.section.s_flags() as u16;
177        if section_type & xcoff::STYP_TEXT != 0 {
178            SectionKind::Text
179        } else if section_type & xcoff::STYP_DATA != 0 {
180            SectionKind::Data
181        } else if section_type & xcoff::STYP_TDATA != 0 {
182            SectionKind::Tls
183        } else if section_type & xcoff::STYP_BSS != 0 {
184            SectionKind::UninitializedData
185        } else if section_type & xcoff::STYP_TBSS != 0 {
186            SectionKind::UninitializedTls
187        } else if section_type & (xcoff::STYP_DEBUG | xcoff::STYP_DWARF) != 0 {
188            SectionKind::Debug
189        } else if section_type & (xcoff::STYP_LOADER | xcoff::STYP_OVRFLO) != 0 {
190            SectionKind::Metadata
191        } else if section_type
192            & (xcoff::STYP_INFO | xcoff::STYP_EXCEPT | xcoff::STYP_PAD | xcoff::STYP_TYPCHK)
193            != 0
194        {
195            SectionKind::Other
196        } else {
197            SectionKind::Unknown
198        }
199    }
200
201    fn relocations(&self) -> Self::RelocationIterator {
202        let rel = self.xcoff_relocations().unwrap_or(&[]);
203        XcoffRelocationIterator {
204            file: self.file,
205            relocations: rel.iter(),
206        }
207    }
208
209    fn relocation_map(&self) -> read::Result<RelocationMap> {
210        RelocationMap::new(self.file, self)
211    }
212
213    fn flags(&self) -> SectionFlags {
214        SectionFlags::Xcoff {
215            s_flags: self.section.s_flags(),
216        }
217    }
218
219    fn uncompressed_data(&self) -> Result<alloc::borrow::Cow<'data, [u8]>> {
220        self.compressed_data()?.decompress()
221    }
222}
223
224/// The table of section headers in an XCOFF file.
225///
226/// Returned by [`FileHeader::sections`].
227#[derive(Debug, Clone, Copy)]
228pub struct SectionTable<'data, Xcoff: FileHeader> {
229    sections: &'data [Xcoff::SectionHeader],
230}
231
232impl<'data, Xcoff> Default for SectionTable<'data, Xcoff>
233where
234    Xcoff: FileHeader,
235{
236    fn default() -> Self {
237        Self { sections: &[] }
238    }
239}
240
241impl<'data, Xcoff> SectionTable<'data, Xcoff>
242where
243    Xcoff: FileHeader,
244{
245    /// Parse the section table.
246    ///
247    /// `data` must be the entire file data.
248    /// `offset` must be after the optional file header.
249    pub fn parse<R: ReadRef<'data>>(header: &Xcoff, data: R, offset: &mut u64) -> Result<Self> {
250        let section_num = header.f_nscns();
251        if section_num == 0 {
252            return Ok(SectionTable::default());
253        }
254        let sections = data
255            .read_slice(offset, section_num as usize)
256            .read_error("Invalid XCOFF section headers")?;
257        Ok(SectionTable { sections })
258    }
259
260    /// Iterate over the section headers.
261    #[inline]
262    pub fn iter(&self) -> slice::Iter<'data, Xcoff::SectionHeader> {
263        self.sections.iter()
264    }
265
266    /// Return true if the section table is empty.
267    #[inline]
268    pub fn is_empty(&self) -> bool {
269        self.sections.is_empty()
270    }
271
272    /// The number of section headers.
273    #[inline]
274    pub fn len(&self) -> usize {
275        self.sections.len()
276    }
277
278    /// Return the section header at the given index.
279    ///
280    /// The index is 1-based.
281    pub fn section(&self, index: SectionIndex) -> read::Result<&'data Xcoff::SectionHeader> {
282        self.sections
283            .get(index.0.wrapping_sub(1))
284            .read_error("Invalid XCOFF section index")
285    }
286}
287
288/// A trait for generic access to [`xcoff::SectionHeader32`] and [`xcoff::SectionHeader64`].
289#[allow(missing_docs)]
290pub trait SectionHeader: Debug + Pod {
291    type Word: Into<u64>;
292    type HalfWord: Into<u32>;
293    type Xcoff: FileHeader<SectionHeader = Self, Word = Self::Word>;
294    type Rel: Rel<Word = Self::Word>;
295
296    fn s_name(&self) -> &[u8; 8];
297    fn s_paddr(&self) -> Self::Word;
298    fn s_vaddr(&self) -> Self::Word;
299    fn s_size(&self) -> Self::Word;
300    fn s_scnptr(&self) -> Self::Word;
301    fn s_relptr(&self) -> Self::Word;
302    fn s_lnnoptr(&self) -> Self::Word;
303    fn s_nreloc(&self) -> Self::HalfWord;
304    fn s_nlnno(&self) -> Self::HalfWord;
305    fn s_flags(&self) -> u32;
306
307    /// Return the section name.
308    fn name(&self) -> &[u8] {
309        let sectname = &self.s_name()[..];
310        match memchr::memchr(b'\0', sectname) {
311            Some(end) => &sectname[..end],
312            None => sectname,
313        }
314    }
315
316    /// Return the offset and size of the section in the file.
317    fn file_range(&self) -> Option<(u64, u64)> {
318        Some((self.s_scnptr().into(), self.s_size().into()))
319    }
320
321    /// Return the section data.
322    ///
323    /// Returns `Ok(&[])` if the section has no data.
324    /// Returns `Err` for invalid values.
325    fn data<'data, R: ReadRef<'data>>(&self, data: R) -> result::Result<&'data [u8], ()> {
326        if let Some((offset, size)) = self.file_range() {
327            data.read_bytes_at(offset, size)
328        } else {
329            Ok(&[])
330        }
331    }
332
333    /// Read the relocations.
334    fn relocations<'data, R: ReadRef<'data>>(&self, data: R) -> read::Result<&'data [Self::Rel]>;
335}
336
337impl SectionHeader for xcoff::SectionHeader32 {
338    type Word = u32;
339    type HalfWord = u16;
340    type Xcoff = xcoff::FileHeader32;
341    type Rel = xcoff::Rel32;
342
343    fn s_name(&self) -> &[u8; 8] {
344        &self.s_name
345    }
346
347    fn s_paddr(&self) -> Self::Word {
348        self.s_paddr.get(BE)
349    }
350
351    fn s_vaddr(&self) -> Self::Word {
352        self.s_vaddr.get(BE)
353    }
354
355    fn s_size(&self) -> Self::Word {
356        self.s_size.get(BE)
357    }
358
359    fn s_scnptr(&self) -> Self::Word {
360        self.s_scnptr.get(BE)
361    }
362
363    fn s_relptr(&self) -> Self::Word {
364        self.s_relptr.get(BE)
365    }
366
367    fn s_lnnoptr(&self) -> Self::Word {
368        self.s_lnnoptr.get(BE)
369    }
370
371    fn s_nreloc(&self) -> Self::HalfWord {
372        self.s_nreloc.get(BE)
373    }
374
375    fn s_nlnno(&self) -> Self::HalfWord {
376        self.s_nlnno.get(BE)
377    }
378
379    fn s_flags(&self) -> u32 {
380        self.s_flags.get(BE)
381    }
382
383    /// Read the relocations in a XCOFF32 file.
384    ///
385    /// `data` must be the entire file data.
386    fn relocations<'data, R: ReadRef<'data>>(&self, data: R) -> read::Result<&'data [Self::Rel]> {
387        let reloc_num = self.s_nreloc() as usize;
388        // TODO: If more than 65,534 relocation entries are required, the field value will be 65535,
389        // and an STYP_OVRFLO section header will contain the actual count of relocation entries in
390        // the s_paddr field.
391        if reloc_num == 65535 {
392            return Err(Error("Overflow section is not supported yet."));
393        }
394        data.read_slice_at(self.s_relptr().into(), reloc_num)
395            .read_error("Invalid XCOFF relocation offset or number")
396    }
397}
398
399impl SectionHeader for xcoff::SectionHeader64 {
400    type Word = u64;
401    type HalfWord = u32;
402    type Xcoff = xcoff::FileHeader64;
403    type Rel = xcoff::Rel64;
404
405    fn s_name(&self) -> &[u8; 8] {
406        &self.s_name
407    }
408
409    fn s_paddr(&self) -> Self::Word {
410        self.s_paddr.get(BE)
411    }
412
413    fn s_vaddr(&self) -> Self::Word {
414        self.s_vaddr.get(BE)
415    }
416
417    fn s_size(&self) -> Self::Word {
418        self.s_size.get(BE)
419    }
420
421    fn s_scnptr(&self) -> Self::Word {
422        self.s_scnptr.get(BE)
423    }
424
425    fn s_relptr(&self) -> Self::Word {
426        self.s_relptr.get(BE)
427    }
428
429    fn s_lnnoptr(&self) -> Self::Word {
430        self.s_lnnoptr.get(BE)
431    }
432
433    fn s_nreloc(&self) -> Self::HalfWord {
434        self.s_nreloc.get(BE)
435    }
436
437    fn s_nlnno(&self) -> Self::HalfWord {
438        self.s_nlnno.get(BE)
439    }
440
441    fn s_flags(&self) -> u32 {
442        self.s_flags.get(BE)
443    }
444
445    /// Read the relocations in a XCOFF64 file.
446    ///
447    /// `data` must be the entire file data.
448    fn relocations<'data, R: ReadRef<'data>>(&self, data: R) -> read::Result<&'data [Self::Rel]> {
449        data.read_slice_at(self.s_relptr(), self.s_nreloc() as usize)
450            .read_error("Invalid XCOFF relocation offset or number")
451    }
452}