object/read/macho/
section.rs

1use core::fmt::Debug;
2use core::{fmt, result, slice, str};
3
4use crate::endian::{self, Endianness};
5use crate::macho;
6use crate::pod::Pod;
7use crate::read::{
8    self, gnu_compression, CompressedData, CompressedFileRange, ObjectSection, ReadError, ReadRef,
9    RelocationMap, Result, SectionFlags, SectionIndex, SectionKind,
10};
11
12use super::{MachHeader, MachOFile, MachORelocationIterator};
13
14/// An iterator for the sections in a [`MachOFile32`](super::MachOFile32).
15pub type MachOSectionIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
16    MachOSectionIterator<'data, 'file, macho::MachHeader32<Endian>, R>;
17/// An iterator for the sections in a [`MachOFile64`](super::MachOFile64).
18pub type MachOSectionIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
19    MachOSectionIterator<'data, 'file, macho::MachHeader64<Endian>, R>;
20
21/// An iterator for the sections in a [`MachOFile`].
22pub struct MachOSectionIterator<'data, 'file, Mach, R = &'data [u8]>
23where
24    Mach: MachHeader,
25    R: ReadRef<'data>,
26{
27    pub(super) file: &'file MachOFile<'data, Mach, R>,
28    pub(super) iter: slice::Iter<'file, MachOSectionInternal<'data, Mach, R>>,
29}
30
31impl<'data, 'file, Mach, R> fmt::Debug for MachOSectionIterator<'data, 'file, Mach, R>
32where
33    Mach: MachHeader,
34    R: ReadRef<'data>,
35{
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        // It's painful to do much better than this
38        f.debug_struct("MachOSectionIterator").finish()
39    }
40}
41
42impl<'data, 'file, Mach, R> Iterator for MachOSectionIterator<'data, 'file, Mach, R>
43where
44    Mach: MachHeader,
45    R: ReadRef<'data>,
46{
47    type Item = MachOSection<'data, 'file, Mach, R>;
48
49    fn next(&mut self) -> Option<Self::Item> {
50        self.iter.next().map(|&internal| MachOSection {
51            file: self.file,
52            internal,
53        })
54    }
55}
56
57/// A section in a [`MachOFile32`](super::MachOFile32).
58pub type MachOSection32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
59    MachOSection<'data, 'file, macho::MachHeader32<Endian>, R>;
60/// A section in a [`MachOFile64`](super::MachOFile64).
61pub type MachOSection64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
62    MachOSection<'data, 'file, macho::MachHeader64<Endian>, R>;
63
64/// A section in a [`MachOFile`].
65///
66/// Most functionality is provided by the [`ObjectSection`] trait implementation.
67#[derive(Debug)]
68pub struct MachOSection<'data, 'file, Mach, R = &'data [u8]>
69where
70    Mach: MachHeader,
71    R: ReadRef<'data>,
72{
73    pub(super) file: &'file MachOFile<'data, Mach, R>,
74    pub(super) internal: MachOSectionInternal<'data, Mach, R>,
75}
76
77impl<'data, 'file, Mach, R> MachOSection<'data, 'file, Mach, R>
78where
79    Mach: MachHeader,
80    R: ReadRef<'data>,
81{
82    /// Get the Mach-O file containing this section.
83    pub fn macho_file(&self) -> &'file MachOFile<'data, Mach, R> {
84        self.file
85    }
86
87    /// Get the raw Mach-O section structure.
88    pub fn macho_section(&self) -> &'data Mach::Section {
89        self.internal.section
90    }
91
92    /// Get the raw Mach-O relocation entries.
93    pub fn macho_relocations(&self) -> Result<&'data [macho::Relocation<Mach::Endian>]> {
94        self.internal
95            .section
96            .relocations(self.file.endian, self.internal.data)
97    }
98
99    fn bytes(&self) -> Result<&'data [u8]> {
100        self.internal
101            .section
102            .data(self.file.endian, self.internal.data)
103            .read_error("Invalid Mach-O section size or offset")
104    }
105
106    // Try GNU-style "ZLIB" header decompression.
107    fn maybe_compressed_gnu(&self) -> Result<Option<CompressedFileRange>> {
108        if !self
109            .name()
110            .map_or(false, |name| name.starts_with("__zdebug_"))
111        {
112            return Ok(None);
113        }
114        let (section_offset, section_size) = self
115            .file_range()
116            .read_error("Invalid ELF GNU compressed section type")?;
117        gnu_compression::compressed_file_range(self.internal.data, section_offset, section_size)
118            .map(Some)
119    }
120}
121
122impl<'data, 'file, Mach, R> read::private::Sealed for MachOSection<'data, 'file, Mach, R>
123where
124    Mach: MachHeader,
125    R: ReadRef<'data>,
126{
127}
128
129impl<'data, 'file, Mach, R> ObjectSection<'data> for MachOSection<'data, 'file, Mach, R>
130where
131    Mach: MachHeader,
132    R: ReadRef<'data>,
133{
134    type RelocationIterator = MachORelocationIterator<'data, 'file, Mach, R>;
135
136    #[inline]
137    fn index(&self) -> SectionIndex {
138        self.internal.index
139    }
140
141    #[inline]
142    fn address(&self) -> u64 {
143        self.internal.section.addr(self.file.endian).into()
144    }
145
146    #[inline]
147    fn size(&self) -> u64 {
148        self.internal.section.size(self.file.endian).into()
149    }
150
151    #[inline]
152    fn align(&self) -> u64 {
153        let align = self.internal.section.align(self.file.endian);
154        if align < 64 {
155            1 << align
156        } else {
157            0
158        }
159    }
160
161    #[inline]
162    fn file_range(&self) -> Option<(u64, u64)> {
163        self.internal.section.file_range(self.file.endian)
164    }
165
166    #[inline]
167    fn data(&self) -> Result<&'data [u8]> {
168        self.bytes()
169    }
170
171    fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> {
172        Ok(read::util::data_range(
173            self.bytes()?,
174            self.address(),
175            address,
176            size,
177        ))
178    }
179
180    fn compressed_file_range(&self) -> Result<CompressedFileRange> {
181        Ok(if let Some(data) = self.maybe_compressed_gnu()? {
182            data
183        } else {
184            CompressedFileRange::none(self.file_range())
185        })
186    }
187
188    fn compressed_data(&self) -> read::Result<CompressedData<'data>> {
189        self.compressed_file_range()?.data(self.file.data)
190    }
191
192    #[inline]
193    fn name_bytes(&self) -> Result<&'data [u8]> {
194        Ok(self.internal.section.name())
195    }
196
197    #[inline]
198    fn name(&self) -> Result<&'data str> {
199        str::from_utf8(self.internal.section.name())
200            .ok()
201            .read_error("Non UTF-8 Mach-O section name")
202    }
203
204    #[inline]
205    fn segment_name_bytes(&self) -> Result<Option<&[u8]>> {
206        Ok(Some(self.internal.section.segment_name()))
207    }
208
209    #[inline]
210    fn segment_name(&self) -> Result<Option<&str>> {
211        Ok(Some(
212            str::from_utf8(self.internal.section.segment_name())
213                .ok()
214                .read_error("Non UTF-8 Mach-O segment name")?,
215        ))
216    }
217
218    fn kind(&self) -> SectionKind {
219        self.internal.kind
220    }
221
222    fn relocations(&self) -> MachORelocationIterator<'data, 'file, Mach, R> {
223        MachORelocationIterator {
224            file: self.file,
225            relocations: self.macho_relocations().unwrap_or(&[]).iter(),
226        }
227    }
228
229    fn relocation_map(&self) -> read::Result<RelocationMap> {
230        RelocationMap::new(self.file, self)
231    }
232
233    fn flags(&self) -> SectionFlags {
234        SectionFlags::MachO {
235            flags: self.internal.section.flags(self.file.endian),
236        }
237    }
238}
239
240#[derive(Debug, Clone, Copy)]
241pub(super) struct MachOSectionInternal<'data, Mach: MachHeader, R: ReadRef<'data>> {
242    pub index: SectionIndex,
243    pub kind: SectionKind,
244    pub section: &'data Mach::Section,
245    /// The data for the file that contains the section data.
246    ///
247    /// This is required for dyld caches, where this may be a different subcache
248    /// from the file containing the Mach-O load commands.
249    pub data: R,
250}
251
252impl<'data, Mach: MachHeader, R: ReadRef<'data>> MachOSectionInternal<'data, Mach, R> {
253    pub(super) fn parse(index: SectionIndex, section: &'data Mach::Section, data: R) -> Self {
254        // TODO: we don't validate flags, should we?
255        let kind = match (section.segment_name(), section.name()) {
256            (b"__TEXT", b"__text") => SectionKind::Text,
257            (b"__TEXT", b"__const") => SectionKind::ReadOnlyData,
258            (b"__TEXT", b"__cstring") => SectionKind::ReadOnlyString,
259            (b"__TEXT", b"__literal4") => SectionKind::ReadOnlyData,
260            (b"__TEXT", b"__literal8") => SectionKind::ReadOnlyData,
261            (b"__TEXT", b"__literal16") => SectionKind::ReadOnlyData,
262            (b"__TEXT", b"__eh_frame") => SectionKind::ReadOnlyData,
263            (b"__TEXT", b"__gcc_except_tab") => SectionKind::ReadOnlyData,
264            (b"__DATA", b"__data") => SectionKind::Data,
265            (b"__DATA", b"__const") => SectionKind::ReadOnlyData,
266            (b"__DATA", b"__bss") => SectionKind::UninitializedData,
267            (b"__DATA", b"__common") => SectionKind::Common,
268            (b"__DATA", b"__thread_data") => SectionKind::Tls,
269            (b"__DATA", b"__thread_bss") => SectionKind::UninitializedTls,
270            (b"__DATA", b"__thread_vars") => SectionKind::TlsVariables,
271            (b"__DWARF", _) => SectionKind::Debug,
272            _ => SectionKind::Unknown,
273        };
274        MachOSectionInternal {
275            index,
276            kind,
277            section,
278            data,
279        }
280    }
281}
282
283/// A trait for generic access to [`macho::Section32`] and [`macho::Section64`].
284#[allow(missing_docs)]
285pub trait Section: Debug + Pod {
286    type Word: Into<u64>;
287    type Endian: endian::Endian;
288
289    fn sectname(&self) -> &[u8; 16];
290    fn segname(&self) -> &[u8; 16];
291    fn addr(&self, endian: Self::Endian) -> Self::Word;
292    fn size(&self, endian: Self::Endian) -> Self::Word;
293    fn offset(&self, endian: Self::Endian) -> u32;
294    fn align(&self, endian: Self::Endian) -> u32;
295    fn reloff(&self, endian: Self::Endian) -> u32;
296    fn nreloc(&self, endian: Self::Endian) -> u32;
297    fn flags(&self, endian: Self::Endian) -> u32;
298
299    /// Return the `sectname` bytes up until the null terminator.
300    fn name(&self) -> &[u8] {
301        let sectname = &self.sectname()[..];
302        match memchr::memchr(b'\0', sectname) {
303            Some(end) => &sectname[..end],
304            None => sectname,
305        }
306    }
307
308    /// Return the `segname` bytes up until the null terminator.
309    fn segment_name(&self) -> &[u8] {
310        let segname = &self.segname()[..];
311        match memchr::memchr(b'\0', segname) {
312            Some(end) => &segname[..end],
313            None => segname,
314        }
315    }
316
317    /// Return the offset and size of the section in the file.
318    ///
319    /// Returns `None` for sections that have no data in the file.
320    fn file_range(&self, endian: Self::Endian) -> Option<(u64, u64)> {
321        match self.flags(endian) & macho::SECTION_TYPE {
322            macho::S_ZEROFILL | macho::S_GB_ZEROFILL | macho::S_THREAD_LOCAL_ZEROFILL => None,
323            _ => Some((self.offset(endian).into(), self.size(endian).into())),
324        }
325    }
326
327    /// Return the section data.
328    ///
329    /// Returns `Ok(&[])` if the section has no data.
330    /// Returns `Err` for invalid values.
331    fn data<'data, R: ReadRef<'data>>(
332        &self,
333        endian: Self::Endian,
334        data: R,
335    ) -> result::Result<&'data [u8], ()> {
336        if let Some((offset, size)) = self.file_range(endian) {
337            data.read_bytes_at(offset, size)
338        } else {
339            Ok(&[])
340        }
341    }
342
343    /// Return the relocation array.
344    ///
345    /// Returns `Err` for invalid values.
346    fn relocations<'data, R: ReadRef<'data>>(
347        &self,
348        endian: Self::Endian,
349        data: R,
350    ) -> Result<&'data [macho::Relocation<Self::Endian>]> {
351        data.read_slice_at(self.reloff(endian).into(), self.nreloc(endian) as usize)
352            .read_error("Invalid Mach-O relocations offset or number")
353    }
354}
355
356impl<Endian: endian::Endian> Section for macho::Section32<Endian> {
357    type Word = u32;
358    type Endian = Endian;
359
360    fn sectname(&self) -> &[u8; 16] {
361        &self.sectname
362    }
363    fn segname(&self) -> &[u8; 16] {
364        &self.segname
365    }
366    fn addr(&self, endian: Self::Endian) -> Self::Word {
367        self.addr.get(endian)
368    }
369    fn size(&self, endian: Self::Endian) -> Self::Word {
370        self.size.get(endian)
371    }
372    fn offset(&self, endian: Self::Endian) -> u32 {
373        self.offset.get(endian)
374    }
375    fn align(&self, endian: Self::Endian) -> u32 {
376        self.align.get(endian)
377    }
378    fn reloff(&self, endian: Self::Endian) -> u32 {
379        self.reloff.get(endian)
380    }
381    fn nreloc(&self, endian: Self::Endian) -> u32 {
382        self.nreloc.get(endian)
383    }
384    fn flags(&self, endian: Self::Endian) -> u32 {
385        self.flags.get(endian)
386    }
387}
388
389impl<Endian: endian::Endian> Section for macho::Section64<Endian> {
390    type Word = u64;
391    type Endian = Endian;
392
393    fn sectname(&self) -> &[u8; 16] {
394        &self.sectname
395    }
396    fn segname(&self) -> &[u8; 16] {
397        &self.segname
398    }
399    fn addr(&self, endian: Self::Endian) -> Self::Word {
400        self.addr.get(endian)
401    }
402    fn size(&self, endian: Self::Endian) -> Self::Word {
403        self.size.get(endian)
404    }
405    fn offset(&self, endian: Self::Endian) -> u32 {
406        self.offset.get(endian)
407    }
408    fn align(&self, endian: Self::Endian) -> u32 {
409        self.align.get(endian)
410    }
411    fn reloff(&self, endian: Self::Endian) -> u32 {
412        self.reloff.get(endian)
413    }
414    fn nreloc(&self, endian: Self::Endian) -> u32 {
415        self.nreloc.get(endian)
416    }
417    fn flags(&self, endian: Self::Endian) -> u32 {
418        self.flags.get(endian)
419    }
420}