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: &MachOSegmentInternal<'_, …, …>| 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 fn bytes(&self) -> Result<&'data [u8]> {
70 self.internal
71 .segment
72 .data(self.file.endian, self.file.data)
73 .read_error("Invalid Mach-O segment size or offset")
74 }
75}
76
77impl<'data, 'file, Mach, R> read::private::Sealed for MachOSegment<'data, 'file, Mach, R>
78where
79 Mach: MachHeader,
80 R: ReadRef<'data>,
81{
82}
83
84impl<'data, 'file, Mach, R> ObjectSegment<'data> for MachOSegment<'data, 'file, Mach, R>
85where
86 Mach: MachHeader,
87 R: ReadRef<'data>,
88{
89 #[inline]
90 fn address(&self) -> u64 {
91 self.internal.segment.vmaddr(self.file.endian).into()
92 }
93
94 #[inline]
95 fn size(&self) -> u64 {
96 self.internal.segment.vmsize(self.file.endian).into()
97 }
98
99 #[inline]
100 fn align(&self) -> u64 {
101 // Page size.
102 0x1000
103 }
104
105 #[inline]
106 fn file_range(&self) -> (u64, u64) {
107 self.internal.segment.file_range(self.file.endian)
108 }
109
110 fn data(&self) -> Result<&'data [u8]> {
111 self.bytes()
112 }
113
114 fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> {
115 Ok(read::util::data_range(
116 self.bytes()?,
117 self.address(),
118 address,
119 size,
120 ))
121 }
122
123 #[inline]
124 fn name_bytes(&self) -> Result<Option<&[u8]>> {
125 Ok(Some(self.internal.segment.name()))
126 }
127
128 #[inline]
129 fn name(&self) -> Result<Option<&str>> {
130 Ok(Some(
131 str::from_utf8(self.internal.segment.name())
132 .ok()
133 .read_error("Non UTF-8 Mach-O segment name")?,
134 ))
135 }
136
137 #[inline]
138 fn flags(&self) -> SegmentFlags {
139 let flags = self.internal.segment.flags(self.file.endian);
140 let maxprot = self.internal.segment.maxprot(self.file.endian);
141 let initprot = self.internal.segment.initprot(self.file.endian);
142 SegmentFlags::MachO {
143 flags,
144 maxprot,
145 initprot,
146 }
147 }
148}
149
150#[derive(Debug, Clone, Copy)]
151pub(super) struct MachOSegmentInternal<'data, Mach: MachHeader, R: ReadRef<'data>> {
152 pub data: R,
153 pub segment: &'data Mach::Segment,
154}
155
156/// A trait for generic access to [`macho::SegmentCommand32`] and [`macho::SegmentCommand64`].
157#[allow(missing_docs)]
158pub trait Segment: Debug + Pod {
159 type Word: Into<u64>;
160 type Endian: endian::Endian;
161 type Section: Section<Endian = Self::Endian>;
162
163 fn from_command(command: LoadCommandData<'_, Self::Endian>) -> Result<Option<(&Self, &[u8])>>;
164
165 fn cmd(&self, endian: Self::Endian) -> u32;
166 fn cmdsize(&self, endian: Self::Endian) -> u32;
167 fn segname(&self) -> &[u8; 16];
168 fn vmaddr(&self, endian: Self::Endian) -> Self::Word;
169 fn vmsize(&self, endian: Self::Endian) -> Self::Word;
170 fn fileoff(&self, endian: Self::Endian) -> Self::Word;
171 fn filesize(&self, endian: Self::Endian) -> Self::Word;
172 fn maxprot(&self, endian: Self::Endian) -> u32;
173 fn initprot(&self, endian: Self::Endian) -> u32;
174 fn nsects(&self, endian: Self::Endian) -> u32;
175 fn flags(&self, endian: Self::Endian) -> u32;
176
177 /// Return the `segname` bytes up until the null terminator.
178 fn name(&self) -> &[u8] {
179 let segname = &self.segname()[..];
180 match memchr::memchr(b'\0', segname) {
181 Some(end) => &segname[..end],
182 None => segname,
183 }
184 }
185
186 /// Return the offset and size of the segment in the file.
187 fn file_range(&self, endian: Self::Endian) -> (u64, u64) {
188 (self.fileoff(endian).into(), self.filesize(endian).into())
189 }
190
191 /// Get the segment data from the file data.
192 ///
193 /// Returns `Err` for invalid values.
194 fn data<'data, R: ReadRef<'data>>(
195 &self,
196 endian: Self::Endian,
197 data: R,
198 ) -> result::Result<&'data [u8], ()> {
199 let (offset, size) = self.file_range(endian);
200 data.read_bytes_at(offset, size)
201 }
202
203 /// Get the array of sections from the data following the segment command.
204 ///
205 /// Returns `Err` for invalid values.
206 fn sections<'data, R: ReadRef<'data>>(
207 &self,
208 endian: Self::Endian,
209 section_data: R,
210 ) -> Result<&'data [Self::Section]> {
211 section_data
212 .read_slice_at(0, self.nsects(endian) as usize)
213 .read_error("Invalid Mach-O number of sections")
214 }
215}
216
217impl<Endian: endian::Endian> Segment for macho::SegmentCommand32<Endian> {
218 type Word = u32;
219 type Endian = Endian;
220 type Section = macho::Section32<Self::Endian>;
221
222 fn from_command(command: LoadCommandData<'_, Self::Endian>) -> Result<Option<(&Self, &[u8])>> {
223 command.segment_32()
224 }
225
226 fn cmd(&self, endian: Self::Endian) -> u32 {
227 self.cmd.get(endian)
228 }
229 fn cmdsize(&self, endian: Self::Endian) -> u32 {
230 self.cmdsize.get(endian)
231 }
232 fn segname(&self) -> &[u8; 16] {
233 &self.segname
234 }
235 fn vmaddr(&self, endian: Self::Endian) -> Self::Word {
236 self.vmaddr.get(endian)
237 }
238 fn vmsize(&self, endian: Self::Endian) -> Self::Word {
239 self.vmsize.get(endian)
240 }
241 fn fileoff(&self, endian: Self::Endian) -> Self::Word {
242 self.fileoff.get(endian)
243 }
244 fn filesize(&self, endian: Self::Endian) -> Self::Word {
245 self.filesize.get(endian)
246 }
247 fn maxprot(&self, endian: Self::Endian) -> u32 {
248 self.maxprot.get(endian)
249 }
250 fn initprot(&self, endian: Self::Endian) -> u32 {
251 self.initprot.get(endian)
252 }
253 fn nsects(&self, endian: Self::Endian) -> u32 {
254 self.nsects.get(endian)
255 }
256 fn flags(&self, endian: Self::Endian) -> u32 {
257 self.flags.get(endian)
258 }
259}
260
261impl<Endian: endian::Endian> Segment for macho::SegmentCommand64<Endian> {
262 type Word = u64;
263 type Endian = Endian;
264 type Section = macho::Section64<Self::Endian>;
265
266 fn from_command(command: LoadCommandData<'_, Self::Endian>) -> Result<Option<(&Self, &[u8])>> {
267 command.segment_64()
268 }
269
270 fn cmd(&self, endian: Self::Endian) -> u32 {
271 self.cmd.get(endian)
272 }
273 fn cmdsize(&self, endian: Self::Endian) -> u32 {
274 self.cmdsize.get(endian)
275 }
276 fn segname(&self) -> &[u8; 16] {
277 &self.segname
278 }
279 fn vmaddr(&self, endian: Self::Endian) -> Self::Word {
280 self.vmaddr.get(endian)
281 }
282 fn vmsize(&self, endian: Self::Endian) -> Self::Word {
283 self.vmsize.get(endian)
284 }
285 fn fileoff(&self, endian: Self::Endian) -> Self::Word {
286 self.fileoff.get(endian)
287 }
288 fn filesize(&self, endian: Self::Endian) -> Self::Word {
289 self.filesize.get(endian)
290 }
291 fn maxprot(&self, endian: Self::Endian) -> u32 {
292 self.maxprot.get(endian)
293 }
294 fn initprot(&self, endian: Self::Endian) -> u32 {
295 self.initprot.get(endian)
296 }
297 fn nsects(&self, endian: Self::Endian) -> u32 {
298 self.nsects.get(endian)
299 }
300 fn flags(&self, endian: Self::Endian) -> u32 {
301 self.flags.get(endian)
302 }
303}
304