1 | use core::fmt::Debug; |
2 | use core::{result, slice, str}; |
3 | |
4 | use crate::endian::{self, Endianness}; |
5 | use crate::macho; |
6 | use crate::pod::Pod; |
7 | use crate::read::{self, ObjectSegment, ReadError, ReadRef, Result, SegmentFlags}; |
8 | |
9 | use super::{LoadCommandData, MachHeader, MachOFile, Section}; |
10 | |
11 | /// An iterator for the segments in a [`MachOFile32`](super::MachOFile32). |
12 | pub 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). |
15 | pub 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)] |
20 | pub struct MachOSegmentIterator<'data, 'file, Mach, R = &'data [u8]> |
21 | where |
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 | |
29 | impl<'data, 'file, Mach, R> Iterator for MachOSegmentIterator<'data, 'file, Mach, R> |
30 | where |
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: &'file MachOSegmentInternal<'_, …, …>| MachOSegment { |
38 | file: self.file, |
39 | internal, |
40 | }) |
41 | } |
42 | } |
43 | |
44 | /// A segment in a [`MachOFile32`](super::MachOFile32). |
45 | pub 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). |
48 | pub 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)] |
55 | pub struct MachOSegment<'data, 'file, Mach, R = &'data [u8]> |
56 | where |
57 | Mach: MachHeader, |
58 | R: ReadRef<'data>, |
59 | { |
60 | file: &'file MachOFile<'data, Mach, R>, |
61 | internal: &'file MachOSegmentInternal<'data, Mach, R>, |
62 | } |
63 | |
64 | impl<'data, 'file, Mach, R> MachOSegment<'data, 'file, Mach, R> |
65 | where |
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 | |
87 | impl<'data, 'file, Mach, R> read::private::Sealed for MachOSegment<'data, 'file, Mach, R> |
88 | where |
89 | Mach: MachHeader, |
90 | R: ReadRef<'data>, |
91 | { |
92 | } |
93 | |
94 | impl<'data, 'file, Mach, R> ObjectSegment<'data> for MachOSegment<'data, 'file, Mach, R> |
95 | where |
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)] |
161 | pub(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)] |
172 | pub 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 | |
231 | impl<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 | |
275 | impl<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 | } |
318 | |