1 | use core::fmt::Debug; |
2 | use core::{fmt, result, slice, str}; |
3 | |
4 | use crate::endian::{self, Endianness}; |
5 | use crate::macho; |
6 | use crate::pod::Pod; |
7 | use crate::read::{ |
8 | self, CompressedData, CompressedFileRange, ObjectSection, ReadError, ReadRef, Result, |
9 | SectionFlags, SectionIndex, SectionKind, |
10 | }; |
11 | |
12 | use super::{MachHeader, MachOFile, MachORelocationIterator}; |
13 | |
14 | /// An iterator for the sections in a [`MachOFile32`](super::MachOFile32). |
15 | pub type MachOSectionIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = |
16 | MachOSectionIterator<'data, 'file, macho::MachHeader32<Endian>, R>; |
17 | /// An iterator for the sections in a [`MachOFile64`](super::MachOFile64). |
18 | pub type MachOSectionIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = |
19 | MachOSectionIterator<'data, 'file, macho::MachHeader64<Endian>, R>; |
20 | |
21 | /// An iterator for the sections in a [`MachOFile`]. |
22 | pub struct MachOSectionIterator<'data, 'file, Mach, R = &'data [u8]> |
23 | where |
24 | Mach: MachHeader, |
25 | R: ReadRef<'data>, |
26 | { |
27 | pub(super) file: &'file MachOFile<'data, Mach, R>, |
28 | pub(super) iter: slice::Iter<'file, MachOSectionInternal<'data, Mach>>, |
29 | } |
30 | |
31 | impl<'data, 'file, Mach, R> fmt::Debug for MachOSectionIterator<'data, 'file, Mach, R> |
32 | where |
33 | Mach: MachHeader, |
34 | R: ReadRef<'data>, |
35 | { |
36 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
37 | // It's painful to do much better than this |
38 | f.debug_struct(name:"MachOSectionIterator" ).finish() |
39 | } |
40 | } |
41 | |
42 | impl<'data, 'file, Mach, R> Iterator for MachOSectionIterator<'data, 'file, Mach, R> |
43 | where |
44 | Mach: MachHeader, |
45 | R: ReadRef<'data>, |
46 | { |
47 | type Item = MachOSection<'data, 'file, Mach, R>; |
48 | |
49 | fn next(&mut self) -> Option<Self::Item> { |
50 | self.iter.next().map(|&internal: MachOSectionInternal<'_, …>| MachOSection { |
51 | file: self.file, |
52 | internal, |
53 | }) |
54 | } |
55 | } |
56 | |
57 | /// A section in a [`MachOFile32`](super::MachOFile32). |
58 | pub type MachOSection32<'data, 'file, Endian = Endianness, R = &'data [u8]> = |
59 | MachOSection<'data, 'file, macho::MachHeader32<Endian>, R>; |
60 | /// A section in a [`MachOFile64`](super::MachOFile64). |
61 | pub type MachOSection64<'data, 'file, Endian = Endianness, R = &'data [u8]> = |
62 | MachOSection<'data, 'file, macho::MachHeader64<Endian>, R>; |
63 | |
64 | /// A section in a [`MachOFile`]. |
65 | /// |
66 | /// Most functionality is provided by the [`ObjectSection`] trait implementation. |
67 | #[derive (Debug)] |
68 | pub struct MachOSection<'data, 'file, Mach, R = &'data [u8]> |
69 | where |
70 | Mach: MachHeader, |
71 | R: ReadRef<'data>, |
72 | { |
73 | pub(super) file: &'file MachOFile<'data, Mach, R>, |
74 | pub(super) internal: MachOSectionInternal<'data, Mach>, |
75 | } |
76 | |
77 | impl<'data, 'file, Mach, R> MachOSection<'data, 'file, Mach, R> |
78 | where |
79 | Mach: MachHeader, |
80 | R: ReadRef<'data>, |
81 | { |
82 | fn bytes(&self) -> Result<&'data [u8]> { |
83 | let segment_index: usize = self.internal.segment_index; |
84 | let segment: &MachOSegmentInternal<'_, …, …> = self.file.segment_internal(segment_index)?; |
85 | self.internal |
86 | .section |
87 | .data(self.file.endian, segment.data) |
88 | .read_error("Invalid Mach-O section size or offset" ) |
89 | } |
90 | } |
91 | |
92 | impl<'data, 'file, Mach, R> read::private::Sealed for MachOSection<'data, 'file, Mach, R> |
93 | where |
94 | Mach: MachHeader, |
95 | R: ReadRef<'data>, |
96 | { |
97 | } |
98 | |
99 | impl<'data, 'file, Mach, R> ObjectSection<'data> for MachOSection<'data, 'file, Mach, R> |
100 | where |
101 | Mach: MachHeader, |
102 | R: ReadRef<'data>, |
103 | { |
104 | type RelocationIterator = MachORelocationIterator<'data, 'file, Mach, R>; |
105 | |
106 | #[inline ] |
107 | fn index(&self) -> SectionIndex { |
108 | self.internal.index |
109 | } |
110 | |
111 | #[inline ] |
112 | fn address(&self) -> u64 { |
113 | self.internal.section.addr(self.file.endian).into() |
114 | } |
115 | |
116 | #[inline ] |
117 | fn size(&self) -> u64 { |
118 | self.internal.section.size(self.file.endian).into() |
119 | } |
120 | |
121 | #[inline ] |
122 | fn align(&self) -> u64 { |
123 | let align = self.internal.section.align(self.file.endian); |
124 | if align < 64 { |
125 | 1 << align |
126 | } else { |
127 | 0 |
128 | } |
129 | } |
130 | |
131 | #[inline ] |
132 | fn file_range(&self) -> Option<(u64, u64)> { |
133 | self.internal.section.file_range(self.file.endian) |
134 | } |
135 | |
136 | #[inline ] |
137 | fn data(&self) -> Result<&'data [u8]> { |
138 | self.bytes() |
139 | } |
140 | |
141 | fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> { |
142 | Ok(read::util::data_range( |
143 | self.bytes()?, |
144 | self.address(), |
145 | address, |
146 | size, |
147 | )) |
148 | } |
149 | |
150 | #[inline ] |
151 | fn compressed_file_range(&self) -> Result<CompressedFileRange> { |
152 | Ok(CompressedFileRange::none(self.file_range())) |
153 | } |
154 | |
155 | #[inline ] |
156 | fn compressed_data(&self) -> Result<CompressedData<'data>> { |
157 | self.data().map(CompressedData::none) |
158 | } |
159 | |
160 | #[inline ] |
161 | fn name_bytes(&self) -> Result<&[u8]> { |
162 | Ok(self.internal.section.name()) |
163 | } |
164 | |
165 | #[inline ] |
166 | fn name(&self) -> Result<&str> { |
167 | str::from_utf8(self.internal.section.name()) |
168 | .ok() |
169 | .read_error("Non UTF-8 Mach-O section name" ) |
170 | } |
171 | |
172 | #[inline ] |
173 | fn segment_name_bytes(&self) -> Result<Option<&[u8]>> { |
174 | Ok(Some(self.internal.section.segment_name())) |
175 | } |
176 | |
177 | #[inline ] |
178 | fn segment_name(&self) -> Result<Option<&str>> { |
179 | Ok(Some( |
180 | str::from_utf8(self.internal.section.segment_name()) |
181 | .ok() |
182 | .read_error("Non UTF-8 Mach-O segment name" )?, |
183 | )) |
184 | } |
185 | |
186 | fn kind(&self) -> SectionKind { |
187 | self.internal.kind |
188 | } |
189 | |
190 | fn relocations(&self) -> MachORelocationIterator<'data, 'file, Mach, R> { |
191 | MachORelocationIterator { |
192 | file: self.file, |
193 | relocations: self |
194 | .internal |
195 | .section |
196 | .relocations(self.file.endian, self.file.data) |
197 | .unwrap_or(&[]) |
198 | .iter(), |
199 | } |
200 | } |
201 | |
202 | fn flags(&self) -> SectionFlags { |
203 | SectionFlags::MachO { |
204 | flags: self.internal.section.flags(self.file.endian), |
205 | } |
206 | } |
207 | } |
208 | |
209 | #[derive (Debug, Clone, Copy)] |
210 | pub(super) struct MachOSectionInternal<'data, Mach: MachHeader> { |
211 | pub index: SectionIndex, |
212 | pub segment_index: usize, |
213 | pub kind: SectionKind, |
214 | pub section: &'data Mach::Section, |
215 | } |
216 | |
217 | impl<'data, Mach: MachHeader> MachOSectionInternal<'data, Mach> { |
218 | pub(super) fn parse( |
219 | index: SectionIndex, |
220 | segment_index: usize, |
221 | section: &'data Mach::Section, |
222 | ) -> Self { |
223 | // TODO: we don't validate flags, should we? |
224 | let kind = match (section.segment_name(), section.name()) { |
225 | (b"__TEXT" , b"__text" ) => SectionKind::Text, |
226 | (b"__TEXT" , b"__const" ) => SectionKind::ReadOnlyData, |
227 | (b"__TEXT" , b"__cstring" ) => SectionKind::ReadOnlyString, |
228 | (b"__TEXT" , b"__literal4" ) => SectionKind::ReadOnlyData, |
229 | (b"__TEXT" , b"__literal8" ) => SectionKind::ReadOnlyData, |
230 | (b"__TEXT" , b"__literal16" ) => SectionKind::ReadOnlyData, |
231 | (b"__TEXT" , b"__eh_frame" ) => SectionKind::ReadOnlyData, |
232 | (b"__TEXT" , b"__gcc_except_tab" ) => SectionKind::ReadOnlyData, |
233 | (b"__DATA" , b"__data" ) => SectionKind::Data, |
234 | (b"__DATA" , b"__const" ) => SectionKind::ReadOnlyData, |
235 | (b"__DATA" , b"__bss" ) => SectionKind::UninitializedData, |
236 | (b"__DATA" , b"__common" ) => SectionKind::Common, |
237 | (b"__DATA" , b"__thread_data" ) => SectionKind::Tls, |
238 | (b"__DATA" , b"__thread_bss" ) => SectionKind::UninitializedTls, |
239 | (b"__DATA" , b"__thread_vars" ) => SectionKind::TlsVariables, |
240 | (b"__DWARF" , _) => SectionKind::Debug, |
241 | _ => SectionKind::Unknown, |
242 | }; |
243 | MachOSectionInternal { |
244 | index, |
245 | segment_index, |
246 | kind, |
247 | section, |
248 | } |
249 | } |
250 | } |
251 | |
252 | /// A trait for generic access to [`macho::Section32`] and [`macho::Section64`]. |
253 | #[allow (missing_docs)] |
254 | pub trait Section: Debug + Pod { |
255 | type Word: Into<u64>; |
256 | type Endian: endian::Endian; |
257 | |
258 | fn sectname(&self) -> &[u8; 16]; |
259 | fn segname(&self) -> &[u8; 16]; |
260 | fn addr(&self, endian: Self::Endian) -> Self::Word; |
261 | fn size(&self, endian: Self::Endian) -> Self::Word; |
262 | fn offset(&self, endian: Self::Endian) -> u32; |
263 | fn align(&self, endian: Self::Endian) -> u32; |
264 | fn reloff(&self, endian: Self::Endian) -> u32; |
265 | fn nreloc(&self, endian: Self::Endian) -> u32; |
266 | fn flags(&self, endian: Self::Endian) -> u32; |
267 | |
268 | /// Return the `sectname` bytes up until the null terminator. |
269 | fn name(&self) -> &[u8] { |
270 | let sectname = &self.sectname()[..]; |
271 | match memchr::memchr(b' \0' , sectname) { |
272 | Some(end) => §name[..end], |
273 | None => sectname, |
274 | } |
275 | } |
276 | |
277 | /// Return the `segname` bytes up until the null terminator. |
278 | fn segment_name(&self) -> &[u8] { |
279 | let segname = &self.segname()[..]; |
280 | match memchr::memchr(b' \0' , segname) { |
281 | Some(end) => &segname[..end], |
282 | None => segname, |
283 | } |
284 | } |
285 | |
286 | /// Return the offset and size of the section in the file. |
287 | /// |
288 | /// Returns `None` for sections that have no data in the file. |
289 | fn file_range(&self, endian: Self::Endian) -> Option<(u64, u64)> { |
290 | match self.flags(endian) & macho::SECTION_TYPE { |
291 | macho::S_ZEROFILL | macho::S_GB_ZEROFILL | macho::S_THREAD_LOCAL_ZEROFILL => None, |
292 | _ => Some((self.offset(endian).into(), self.size(endian).into())), |
293 | } |
294 | } |
295 | |
296 | /// Return the section data. |
297 | /// |
298 | /// Returns `Ok(&[])` if the section has no data. |
299 | /// Returns `Err` for invalid values. |
300 | fn data<'data, R: ReadRef<'data>>( |
301 | &self, |
302 | endian: Self::Endian, |
303 | data: R, |
304 | ) -> result::Result<&'data [u8], ()> { |
305 | if let Some((offset, size)) = self.file_range(endian) { |
306 | data.read_bytes_at(offset, size) |
307 | } else { |
308 | Ok(&[]) |
309 | } |
310 | } |
311 | |
312 | /// Return the relocation array. |
313 | /// |
314 | /// Returns `Err` for invalid values. |
315 | fn relocations<'data, R: ReadRef<'data>>( |
316 | &self, |
317 | endian: Self::Endian, |
318 | data: R, |
319 | ) -> Result<&'data [macho::Relocation<Self::Endian>]> { |
320 | data.read_slice_at(self.reloff(endian).into(), self.nreloc(endian) as usize) |
321 | .read_error("Invalid Mach-O relocations offset or number" ) |
322 | } |
323 | } |
324 | |
325 | impl<Endian: endian::Endian> Section for macho::Section32<Endian> { |
326 | type Word = u32; |
327 | type Endian = Endian; |
328 | |
329 | fn sectname(&self) -> &[u8; 16] { |
330 | &self.sectname |
331 | } |
332 | fn segname(&self) -> &[u8; 16] { |
333 | &self.segname |
334 | } |
335 | fn addr(&self, endian: Self::Endian) -> Self::Word { |
336 | self.addr.get(endian) |
337 | } |
338 | fn size(&self, endian: Self::Endian) -> Self::Word { |
339 | self.size.get(endian) |
340 | } |
341 | fn offset(&self, endian: Self::Endian) -> u32 { |
342 | self.offset.get(endian) |
343 | } |
344 | fn align(&self, endian: Self::Endian) -> u32 { |
345 | self.align.get(endian) |
346 | } |
347 | fn reloff(&self, endian: Self::Endian) -> u32 { |
348 | self.reloff.get(endian) |
349 | } |
350 | fn nreloc(&self, endian: Self::Endian) -> u32 { |
351 | self.nreloc.get(endian) |
352 | } |
353 | fn flags(&self, endian: Self::Endian) -> u32 { |
354 | self.flags.get(endian) |
355 | } |
356 | } |
357 | |
358 | impl<Endian: endian::Endian> Section for macho::Section64<Endian> { |
359 | type Word = u64; |
360 | type Endian = Endian; |
361 | |
362 | fn sectname(&self) -> &[u8; 16] { |
363 | &self.sectname |
364 | } |
365 | fn segname(&self) -> &[u8; 16] { |
366 | &self.segname |
367 | } |
368 | fn addr(&self, endian: Self::Endian) -> Self::Word { |
369 | self.addr.get(endian) |
370 | } |
371 | fn size(&self, endian: Self::Endian) -> Self::Word { |
372 | self.size.get(endian) |
373 | } |
374 | fn offset(&self, endian: Self::Endian) -> u32 { |
375 | self.offset.get(endian) |
376 | } |
377 | fn align(&self, endian: Self::Endian) -> u32 { |
378 | self.align.get(endian) |
379 | } |
380 | fn reloff(&self, endian: Self::Endian) -> u32 { |
381 | self.reloff.get(endian) |
382 | } |
383 | fn nreloc(&self, endian: Self::Endian) -> u32 { |
384 | self.nreloc.get(endian) |
385 | } |
386 | fn flags(&self, endian: Self::Endian) -> u32 { |
387 | self.flags.get(endian) |
388 | } |
389 | } |
390 | |