Skip to main content

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, Permissions, 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    #[inline]
160    fn permissions(&self) -> Permissions {
161        let maxprot = self.internal.segment.maxprot(self.file.endian);
162        Permissions::new(
163            maxprot & macho::VM_PROT_READ != 0,
164            maxprot & macho::VM_PROT_WRITE != 0,
165            maxprot & macho::VM_PROT_EXECUTE != 0,
166        )
167    }
168}
169
170#[derive(Debug, Clone, Copy)]
171pub(super) struct MachOSegmentInternal<'data, Mach: MachHeader, R: ReadRef<'data>> {
172    pub segment: &'data Mach::Segment,
173    /// The data for the file that contains the segment data.
174    ///
175    /// This is required for dyld caches, where this may be a different subcache
176    /// from the file containing the Mach-O load commands.
177    pub data: R,
178}
179
180/// A trait for generic access to [`macho::SegmentCommand32`] and [`macho::SegmentCommand64`].
181#[allow(missing_docs)]
182pub trait Segment: Debug + Pod {
183    type Word: Into<u64>;
184    type Endian: endian::Endian;
185    type Section: Section<Endian = Self::Endian>;
186
187    fn from_command(command: LoadCommandData<'_, Self::Endian>) -> Result<Option<(&Self, &[u8])>>;
188
189    fn cmd(&self, endian: Self::Endian) -> u32;
190    fn cmdsize(&self, endian: Self::Endian) -> u32;
191    fn segname(&self) -> &[u8; 16];
192    fn vmaddr(&self, endian: Self::Endian) -> Self::Word;
193    fn vmsize(&self, endian: Self::Endian) -> Self::Word;
194    fn fileoff(&self, endian: Self::Endian) -> Self::Word;
195    fn filesize(&self, endian: Self::Endian) -> Self::Word;
196    fn maxprot(&self, endian: Self::Endian) -> u32;
197    fn initprot(&self, endian: Self::Endian) -> u32;
198    fn nsects(&self, endian: Self::Endian) -> u32;
199    fn flags(&self, endian: Self::Endian) -> u32;
200
201    /// Return the `segname` bytes up until the null terminator.
202    fn name(&self) -> &[u8] {
203        let segname = &self.segname()[..];
204        match memchr::memchr(b'\0', segname) {
205            Some(end) => &segname[..end],
206            None => segname,
207        }
208    }
209
210    /// Return the offset and size of the segment in the file.
211    fn file_range(&self, endian: Self::Endian) -> (u64, u64) {
212        (self.fileoff(endian).into(), self.filesize(endian).into())
213    }
214
215    /// Get the segment data from the file data.
216    ///
217    /// Returns `Err` for invalid values.
218    fn data<'data, R: ReadRef<'data>>(
219        &self,
220        endian: Self::Endian,
221        data: R,
222    ) -> result::Result<&'data [u8], ()> {
223        let (offset, size) = self.file_range(endian);
224        data.read_bytes_at(offset, size)
225    }
226
227    /// Get the array of sections from the data following the segment command.
228    ///
229    /// Returns `Err` for invalid values.
230    fn sections<'data, R: ReadRef<'data>>(
231        &self,
232        endian: Self::Endian,
233        section_data: R,
234    ) -> Result<&'data [Self::Section]> {
235        section_data
236            .read_slice_at(0, self.nsects(endian) as usize)
237            .read_error("Invalid Mach-O number of sections")
238    }
239}
240
241impl<Endian: endian::Endian> Segment for macho::SegmentCommand32<Endian> {
242    type Word = u32;
243    type Endian = Endian;
244    type Section = macho::Section32<Self::Endian>;
245
246    fn from_command(command: LoadCommandData<'_, Self::Endian>) -> Result<Option<(&Self, &[u8])>> {
247        command.segment_32()
248    }
249
250    fn cmd(&self, endian: Self::Endian) -> u32 {
251        self.cmd.get(endian)
252    }
253    fn cmdsize(&self, endian: Self::Endian) -> u32 {
254        self.cmdsize.get(endian)
255    }
256    fn segname(&self) -> &[u8; 16] {
257        &self.segname
258    }
259    fn vmaddr(&self, endian: Self::Endian) -> Self::Word {
260        self.vmaddr.get(endian)
261    }
262    fn vmsize(&self, endian: Self::Endian) -> Self::Word {
263        self.vmsize.get(endian)
264    }
265    fn fileoff(&self, endian: Self::Endian) -> Self::Word {
266        self.fileoff.get(endian)
267    }
268    fn filesize(&self, endian: Self::Endian) -> Self::Word {
269        self.filesize.get(endian)
270    }
271    fn maxprot(&self, endian: Self::Endian) -> u32 {
272        self.maxprot.get(endian)
273    }
274    fn initprot(&self, endian: Self::Endian) -> u32 {
275        self.initprot.get(endian)
276    }
277    fn nsects(&self, endian: Self::Endian) -> u32 {
278        self.nsects.get(endian)
279    }
280    fn flags(&self, endian: Self::Endian) -> u32 {
281        self.flags.get(endian)
282    }
283}
284
285impl<Endian: endian::Endian> Segment for macho::SegmentCommand64<Endian> {
286    type Word = u64;
287    type Endian = Endian;
288    type Section = macho::Section64<Self::Endian>;
289
290    fn from_command(command: LoadCommandData<'_, Self::Endian>) -> Result<Option<(&Self, &[u8])>> {
291        command.segment_64()
292    }
293
294    fn cmd(&self, endian: Self::Endian) -> u32 {
295        self.cmd.get(endian)
296    }
297    fn cmdsize(&self, endian: Self::Endian) -> u32 {
298        self.cmdsize.get(endian)
299    }
300    fn segname(&self) -> &[u8; 16] {
301        &self.segname
302    }
303    fn vmaddr(&self, endian: Self::Endian) -> Self::Word {
304        self.vmaddr.get(endian)
305    }
306    fn vmsize(&self, endian: Self::Endian) -> Self::Word {
307        self.vmsize.get(endian)
308    }
309    fn fileoff(&self, endian: Self::Endian) -> Self::Word {
310        self.fileoff.get(endian)
311    }
312    fn filesize(&self, endian: Self::Endian) -> Self::Word {
313        self.filesize.get(endian)
314    }
315    fn maxprot(&self, endian: Self::Endian) -> u32 {
316        self.maxprot.get(endian)
317    }
318    fn initprot(&self, endian: Self::Endian) -> u32 {
319        self.initprot.get(endian)
320    }
321    fn nsects(&self, endian: Self::Endian) -> u32 {
322        self.nsects.get(endian)
323    }
324    fn flags(&self, endian: Self::Endian) -> u32 {
325        self.flags.get(endian)
326    }
327}