object/read/macho/
segment.rs

1use core::fmt::Debug;
2use core::{result, slice, str};
3
4use crate::endian::{self, Endianness};
5use crate::macho;
6use crate::pod::Pod;
7use crate::read::{self, ObjectSegment, ReadError, ReadRef, Result, SegmentFlags};
8
9use super::{LoadCommandData, MachHeader, MachOFile, Section};
10
11/// An iterator for the segments in a [`MachOFile32`](super::MachOFile32).
12pub type MachOSegmentIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
13    MachOSegmentIterator<'data, 'file, macho::MachHeader32<Endian>, R>;
14/// An iterator for the segments in a [`MachOFile64`](super::MachOFile64).
15pub type MachOSegmentIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
16    MachOSegmentIterator<'data, 'file, macho::MachHeader64<Endian>, R>;
17
18/// An iterator for the segments in a [`MachOFile`].
19#[derive(Debug)]
20pub struct MachOSegmentIterator<'data, 'file, Mach, R = &'data [u8]>
21where
22    Mach: MachHeader,
23    R: ReadRef<'data>,
24{
25    pub(super) file: &'file MachOFile<'data, Mach, R>,
26    pub(super) iter: slice::Iter<'file, MachOSegmentInternal<'data, Mach, R>>,
27}
28
29impl<'data, 'file, Mach, R> Iterator for MachOSegmentIterator<'data, 'file, Mach, R>
30where
31    Mach: MachHeader,
32    R: ReadRef<'data>,
33{
34    type Item = MachOSegment<'data, 'file, Mach, R>;
35
36    fn next(&mut self) -> Option<Self::Item> {
37        self.iter.next().map(|internal| MachOSegment {
38            file: self.file,
39            internal,
40        })
41    }
42}
43
44/// A segment in a [`MachOFile32`](super::MachOFile32).
45pub type MachOSegment32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
46    MachOSegment<'data, 'file, macho::MachHeader32<Endian>, R>;
47/// A segment in a [`MachOFile64`](super::MachOFile64).
48pub type MachOSegment64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
49    MachOSegment<'data, 'file, macho::MachHeader64<Endian>, R>;
50
51/// A segment in a [`MachOFile`].
52///
53/// Most functionality is provided by the [`ObjectSegment`] trait implementation.
54#[derive(Debug)]
55pub struct MachOSegment<'data, 'file, Mach, R = &'data [u8]>
56where
57    Mach: MachHeader,
58    R: ReadRef<'data>,
59{
60    file: &'file MachOFile<'data, Mach, R>,
61    internal: &'file MachOSegmentInternal<'data, Mach, R>,
62}
63
64impl<'data, 'file, Mach, R> MachOSegment<'data, 'file, Mach, R>
65where
66    Mach: MachHeader,
67    R: ReadRef<'data>,
68{
69    /// Get the Mach-O file containing this segment.
70    pub fn macho_file(&self) -> &'file MachOFile<'data, Mach, R> {
71        self.file
72    }
73
74    /// Get the raw Mach-O segment structure.
75    pub fn macho_segment(&self) -> &'data Mach::Segment {
76        self.internal.segment
77    }
78
79    fn bytes(&self) -> Result<&'data [u8]> {
80        self.internal
81            .segment
82            .data(self.file.endian, self.internal.data)
83            .read_error("Invalid Mach-O segment size or offset")
84    }
85}
86
87impl<'data, 'file, Mach, R> read::private::Sealed for MachOSegment<'data, 'file, Mach, R>
88where
89    Mach: MachHeader,
90    R: ReadRef<'data>,
91{
92}
93
94impl<'data, 'file, Mach, R> ObjectSegment<'data> for MachOSegment<'data, 'file, Mach, R>
95where
96    Mach: MachHeader,
97    R: ReadRef<'data>,
98{
99    #[inline]
100    fn address(&self) -> u64 {
101        self.internal.segment.vmaddr(self.file.endian).into()
102    }
103
104    #[inline]
105    fn size(&self) -> u64 {
106        self.internal.segment.vmsize(self.file.endian).into()
107    }
108
109    #[inline]
110    fn align(&self) -> u64 {
111        // Page size.
112        0x1000
113    }
114
115    #[inline]
116    fn file_range(&self) -> (u64, u64) {
117        self.internal.segment.file_range(self.file.endian)
118    }
119
120    fn data(&self) -> Result<&'data [u8]> {
121        self.bytes()
122    }
123
124    fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> {
125        Ok(read::util::data_range(
126            self.bytes()?,
127            self.address(),
128            address,
129            size,
130        ))
131    }
132
133    #[inline]
134    fn name_bytes(&self) -> Result<Option<&[u8]>> {
135        Ok(Some(self.internal.segment.name()))
136    }
137
138    #[inline]
139    fn name(&self) -> Result<Option<&str>> {
140        Ok(Some(
141            str::from_utf8(self.internal.segment.name())
142                .ok()
143                .read_error("Non UTF-8 Mach-O segment name")?,
144        ))
145    }
146
147    #[inline]
148    fn flags(&self) -> SegmentFlags {
149        let flags = self.internal.segment.flags(self.file.endian);
150        let maxprot = self.internal.segment.maxprot(self.file.endian);
151        let initprot = self.internal.segment.initprot(self.file.endian);
152        SegmentFlags::MachO {
153            flags,
154            maxprot,
155            initprot,
156        }
157    }
158}
159
160#[derive(Debug, Clone, Copy)]
161pub(super) struct MachOSegmentInternal<'data, Mach: MachHeader, R: ReadRef<'data>> {
162    pub segment: &'data Mach::Segment,
163    /// The data for the file that contains the segment data.
164    ///
165    /// This is required for dyld caches, where this may be a different subcache
166    /// from the file containing the Mach-O load commands.
167    pub data: R,
168}
169
170/// A trait for generic access to [`macho::SegmentCommand32`] and [`macho::SegmentCommand64`].
171#[allow(missing_docs)]
172pub trait Segment: Debug + Pod {
173    type Word: Into<u64>;
174    type Endian: endian::Endian;
175    type Section: Section<Endian = Self::Endian>;
176
177    fn from_command(command: LoadCommandData<'_, Self::Endian>) -> Result<Option<(&Self, &[u8])>>;
178
179    fn cmd(&self, endian: Self::Endian) -> u32;
180    fn cmdsize(&self, endian: Self::Endian) -> u32;
181    fn segname(&self) -> &[u8; 16];
182    fn vmaddr(&self, endian: Self::Endian) -> Self::Word;
183    fn vmsize(&self, endian: Self::Endian) -> Self::Word;
184    fn fileoff(&self, endian: Self::Endian) -> Self::Word;
185    fn filesize(&self, endian: Self::Endian) -> Self::Word;
186    fn maxprot(&self, endian: Self::Endian) -> u32;
187    fn initprot(&self, endian: Self::Endian) -> u32;
188    fn nsects(&self, endian: Self::Endian) -> u32;
189    fn flags(&self, endian: Self::Endian) -> u32;
190
191    /// Return the `segname` bytes up until the null terminator.
192    fn name(&self) -> &[u8] {
193        let segname = &self.segname()[..];
194        match memchr::memchr(b'\0', segname) {
195            Some(end) => &segname[..end],
196            None => segname,
197        }
198    }
199
200    /// Return the offset and size of the segment in the file.
201    fn file_range(&self, endian: Self::Endian) -> (u64, u64) {
202        (self.fileoff(endian).into(), self.filesize(endian).into())
203    }
204
205    /// Get the segment data from the file data.
206    ///
207    /// Returns `Err` for invalid values.
208    fn data<'data, R: ReadRef<'data>>(
209        &self,
210        endian: Self::Endian,
211        data: R,
212    ) -> result::Result<&'data [u8], ()> {
213        let (offset, size) = self.file_range(endian);
214        data.read_bytes_at(offset, size)
215    }
216
217    /// Get the array of sections from the data following the segment command.
218    ///
219    /// Returns `Err` for invalid values.
220    fn sections<'data, R: ReadRef<'data>>(
221        &self,
222        endian: Self::Endian,
223        section_data: R,
224    ) -> Result<&'data [Self::Section]> {
225        section_data
226            .read_slice_at(0, self.nsects(endian) as usize)
227            .read_error("Invalid Mach-O number of sections")
228    }
229}
230
231impl<Endian: endian::Endian> Segment for macho::SegmentCommand32<Endian> {
232    type Word = u32;
233    type Endian = Endian;
234    type Section = macho::Section32<Self::Endian>;
235
236    fn from_command(command: LoadCommandData<'_, Self::Endian>) -> Result<Option<(&Self, &[u8])>> {
237        command.segment_32()
238    }
239
240    fn cmd(&self, endian: Self::Endian) -> u32 {
241        self.cmd.get(endian)
242    }
243    fn cmdsize(&self, endian: Self::Endian) -> u32 {
244        self.cmdsize.get(endian)
245    }
246    fn segname(&self) -> &[u8; 16] {
247        &self.segname
248    }
249    fn vmaddr(&self, endian: Self::Endian) -> Self::Word {
250        self.vmaddr.get(endian)
251    }
252    fn vmsize(&self, endian: Self::Endian) -> Self::Word {
253        self.vmsize.get(endian)
254    }
255    fn fileoff(&self, endian: Self::Endian) -> Self::Word {
256        self.fileoff.get(endian)
257    }
258    fn filesize(&self, endian: Self::Endian) -> Self::Word {
259        self.filesize.get(endian)
260    }
261    fn maxprot(&self, endian: Self::Endian) -> u32 {
262        self.maxprot.get(endian)
263    }
264    fn initprot(&self, endian: Self::Endian) -> u32 {
265        self.initprot.get(endian)
266    }
267    fn nsects(&self, endian: Self::Endian) -> u32 {
268        self.nsects.get(endian)
269    }
270    fn flags(&self, endian: Self::Endian) -> u32 {
271        self.flags.get(endian)
272    }
273}
274
275impl<Endian: endian::Endian> Segment for macho::SegmentCommand64<Endian> {
276    type Word = u64;
277    type Endian = Endian;
278    type Section = macho::Section64<Self::Endian>;
279
280    fn from_command(command: LoadCommandData<'_, Self::Endian>) -> Result<Option<(&Self, &[u8])>> {
281        command.segment_64()
282    }
283
284    fn cmd(&self, endian: Self::Endian) -> u32 {
285        self.cmd.get(endian)
286    }
287    fn cmdsize(&self, endian: Self::Endian) -> u32 {
288        self.cmdsize.get(endian)
289    }
290    fn segname(&self) -> &[u8; 16] {
291        &self.segname
292    }
293    fn vmaddr(&self, endian: Self::Endian) -> Self::Word {
294        self.vmaddr.get(endian)
295    }
296    fn vmsize(&self, endian: Self::Endian) -> Self::Word {
297        self.vmsize.get(endian)
298    }
299    fn fileoff(&self, endian: Self::Endian) -> Self::Word {
300        self.fileoff.get(endian)
301    }
302    fn filesize(&self, endian: Self::Endian) -> Self::Word {
303        self.filesize.get(endian)
304    }
305    fn maxprot(&self, endian: Self::Endian) -> u32 {
306        self.maxprot.get(endian)
307    }
308    fn initprot(&self, endian: Self::Endian) -> u32 {
309        self.initprot.get(endian)
310    }
311    fn nsects(&self, endian: Self::Endian) -> u32 {
312        self.nsects.get(endian)
313    }
314    fn flags(&self, endian: Self::Endian) -> u32 {
315        self.flags.get(endian)
316    }
317}