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