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, gnu_compression, CompressedData, CompressedFileRange, ObjectSection, ReadError, ReadRef, |
9 | RelocationMap, Result, 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, R>>, |
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, R>, |
75 | } |
76 | |
77 | impl<'data, 'file, Mach, R> MachOSection<'data, 'file, Mach, R> |
78 | where |
79 | Mach: MachHeader, |
80 | R: ReadRef<'data>, |
81 | { |
82 | /// Get the Mach-O file containing this section. |
83 | pub fn macho_file(&self) -> &'file MachOFile<'data, Mach, R> { |
84 | self.file |
85 | } |
86 | |
87 | /// Get the raw Mach-O section structure. |
88 | pub fn macho_section(&self) -> &'data Mach::Section { |
89 | self.internal.section |
90 | } |
91 | |
92 | /// Get the raw Mach-O relocation entries. |
93 | pub fn macho_relocations(&self) -> Result<&'data [macho::Relocation<Mach::Endian>]> { |
94 | self.internal |
95 | .section |
96 | .relocations(self.file.endian, self.internal.data) |
97 | } |
98 | |
99 | fn bytes(&self) -> Result<&'data [u8]> { |
100 | self.internal |
101 | .section |
102 | .data(self.file.endian, self.internal.data) |
103 | .read_error("Invalid Mach-O section size or offset" ) |
104 | } |
105 | |
106 | // Try GNU-style "ZLIB" header decompression. |
107 | fn maybe_compressed_gnu(&self) -> Result<Option<CompressedFileRange>> { |
108 | if !self |
109 | .name() |
110 | .map_or(false, |name| name.starts_with("__zdebug_" )) |
111 | { |
112 | return Ok(None); |
113 | } |
114 | let (section_offset, section_size) = self |
115 | .file_range() |
116 | .read_error("Invalid ELF GNU compressed section type" )?; |
117 | gnu_compression::compressed_file_range(self.internal.data, section_offset, section_size) |
118 | .map(Some) |
119 | } |
120 | } |
121 | |
122 | impl<'data, 'file, Mach, R> read::private::Sealed for MachOSection<'data, 'file, Mach, R> |
123 | where |
124 | Mach: MachHeader, |
125 | R: ReadRef<'data>, |
126 | { |
127 | } |
128 | |
129 | impl<'data, 'file, Mach, R> ObjectSection<'data> for MachOSection<'data, 'file, Mach, R> |
130 | where |
131 | Mach: MachHeader, |
132 | R: ReadRef<'data>, |
133 | { |
134 | type RelocationIterator = MachORelocationIterator<'data, 'file, Mach, R>; |
135 | |
136 | #[inline ] |
137 | fn index(&self) -> SectionIndex { |
138 | self.internal.index |
139 | } |
140 | |
141 | #[inline ] |
142 | fn address(&self) -> u64 { |
143 | self.internal.section.addr(self.file.endian).into() |
144 | } |
145 | |
146 | #[inline ] |
147 | fn size(&self) -> u64 { |
148 | self.internal.section.size(self.file.endian).into() |
149 | } |
150 | |
151 | #[inline ] |
152 | fn align(&self) -> u64 { |
153 | let align = self.internal.section.align(self.file.endian); |
154 | if align < 64 { |
155 | 1 << align |
156 | } else { |
157 | 0 |
158 | } |
159 | } |
160 | |
161 | #[inline ] |
162 | fn file_range(&self) -> Option<(u64, u64)> { |
163 | self.internal.section.file_range(self.file.endian) |
164 | } |
165 | |
166 | #[inline ] |
167 | fn data(&self) -> Result<&'data [u8]> { |
168 | self.bytes() |
169 | } |
170 | |
171 | fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> { |
172 | Ok(read::util::data_range( |
173 | self.bytes()?, |
174 | self.address(), |
175 | address, |
176 | size, |
177 | )) |
178 | } |
179 | |
180 | fn compressed_file_range(&self) -> Result<CompressedFileRange> { |
181 | Ok(if let Some(data) = self.maybe_compressed_gnu()? { |
182 | data |
183 | } else { |
184 | CompressedFileRange::none(self.file_range()) |
185 | }) |
186 | } |
187 | |
188 | fn compressed_data(&self) -> read::Result<CompressedData<'data>> { |
189 | self.compressed_file_range()?.data(self.file.data) |
190 | } |
191 | |
192 | #[inline ] |
193 | fn name_bytes(&self) -> Result<&'data [u8]> { |
194 | Ok(self.internal.section.name()) |
195 | } |
196 | |
197 | #[inline ] |
198 | fn name(&self) -> Result<&'data str> { |
199 | str::from_utf8(self.internal.section.name()) |
200 | .ok() |
201 | .read_error("Non UTF-8 Mach-O section name" ) |
202 | } |
203 | |
204 | #[inline ] |
205 | fn segment_name_bytes(&self) -> Result<Option<&[u8]>> { |
206 | Ok(Some(self.internal.section.segment_name())) |
207 | } |
208 | |
209 | #[inline ] |
210 | fn segment_name(&self) -> Result<Option<&str>> { |
211 | Ok(Some( |
212 | str::from_utf8(self.internal.section.segment_name()) |
213 | .ok() |
214 | .read_error("Non UTF-8 Mach-O segment name" )?, |
215 | )) |
216 | } |
217 | |
218 | fn kind(&self) -> SectionKind { |
219 | self.internal.kind |
220 | } |
221 | |
222 | fn relocations(&self) -> MachORelocationIterator<'data, 'file, Mach, R> { |
223 | MachORelocationIterator { |
224 | file: self.file, |
225 | relocations: self.macho_relocations().unwrap_or(&[]).iter(), |
226 | } |
227 | } |
228 | |
229 | fn relocation_map(&self) -> read::Result<RelocationMap> { |
230 | RelocationMap::new(self.file, self) |
231 | } |
232 | |
233 | fn flags(&self) -> SectionFlags { |
234 | SectionFlags::MachO { |
235 | flags: self.internal.section.flags(self.file.endian), |
236 | } |
237 | } |
238 | } |
239 | |
240 | #[derive (Debug, Clone, Copy)] |
241 | pub(super) struct MachOSectionInternal<'data, Mach: MachHeader, R: ReadRef<'data>> { |
242 | pub index: SectionIndex, |
243 | pub kind: SectionKind, |
244 | pub section: &'data Mach::Section, |
245 | /// The data for the file that contains the section data. |
246 | /// |
247 | /// This is required for dyld caches, where this may be a different subcache |
248 | /// from the file containing the Mach-O load commands. |
249 | pub data: R, |
250 | } |
251 | |
252 | impl<'data, Mach: MachHeader, R: ReadRef<'data>> MachOSectionInternal<'data, Mach, R> { |
253 | pub(super) fn parse(index: SectionIndex, section: &'data Mach::Section, data: R) -> Self { |
254 | // TODO: we don't validate flags, should we? |
255 | let kind = match (section.segment_name(), section.name()) { |
256 | (b"__TEXT" , b"__text" ) => SectionKind::Text, |
257 | (b"__TEXT" , b"__const" ) => SectionKind::ReadOnlyData, |
258 | (b"__TEXT" , b"__cstring" ) => SectionKind::ReadOnlyString, |
259 | (b"__TEXT" , b"__literal4" ) => SectionKind::ReadOnlyData, |
260 | (b"__TEXT" , b"__literal8" ) => SectionKind::ReadOnlyData, |
261 | (b"__TEXT" , b"__literal16" ) => SectionKind::ReadOnlyData, |
262 | (b"__TEXT" , b"__eh_frame" ) => SectionKind::ReadOnlyData, |
263 | (b"__TEXT" , b"__gcc_except_tab" ) => SectionKind::ReadOnlyData, |
264 | (b"__DATA" , b"__data" ) => SectionKind::Data, |
265 | (b"__DATA" , b"__const" ) => SectionKind::ReadOnlyData, |
266 | (b"__DATA" , b"__bss" ) => SectionKind::UninitializedData, |
267 | (b"__DATA" , b"__common" ) => SectionKind::Common, |
268 | (b"__DATA" , b"__thread_data" ) => SectionKind::Tls, |
269 | (b"__DATA" , b"__thread_bss" ) => SectionKind::UninitializedTls, |
270 | (b"__DATA" , b"__thread_vars" ) => SectionKind::TlsVariables, |
271 | (b"__DWARF" , _) => SectionKind::Debug, |
272 | _ => SectionKind::Unknown, |
273 | }; |
274 | MachOSectionInternal { |
275 | index, |
276 | kind, |
277 | section, |
278 | data, |
279 | } |
280 | } |
281 | } |
282 | |
283 | /// A trait for generic access to [`macho::Section32`] and [`macho::Section64`]. |
284 | #[allow (missing_docs)] |
285 | pub trait Section: Debug + Pod { |
286 | type Word: Into<u64>; |
287 | type Endian: endian::Endian; |
288 | |
289 | fn sectname(&self) -> &[u8; 16]; |
290 | fn segname(&self) -> &[u8; 16]; |
291 | fn addr(&self, endian: Self::Endian) -> Self::Word; |
292 | fn size(&self, endian: Self::Endian) -> Self::Word; |
293 | fn offset(&self, endian: Self::Endian) -> u32; |
294 | fn align(&self, endian: Self::Endian) -> u32; |
295 | fn reloff(&self, endian: Self::Endian) -> u32; |
296 | fn nreloc(&self, endian: Self::Endian) -> u32; |
297 | fn flags(&self, endian: Self::Endian) -> u32; |
298 | |
299 | /// Return the `sectname` bytes up until the null terminator. |
300 | fn name(&self) -> &[u8] { |
301 | let sectname = &self.sectname()[..]; |
302 | match memchr::memchr(b' \0' , sectname) { |
303 | Some(end) => §name[..end], |
304 | None => sectname, |
305 | } |
306 | } |
307 | |
308 | /// Return the `segname` bytes up until the null terminator. |
309 | fn segment_name(&self) -> &[u8] { |
310 | let segname = &self.segname()[..]; |
311 | match memchr::memchr(b' \0' , segname) { |
312 | Some(end) => &segname[..end], |
313 | None => segname, |
314 | } |
315 | } |
316 | |
317 | /// Return the offset and size of the section in the file. |
318 | /// |
319 | /// Returns `None` for sections that have no data in the file. |
320 | fn file_range(&self, endian: Self::Endian) -> Option<(u64, u64)> { |
321 | match self.flags(endian) & macho::SECTION_TYPE { |
322 | macho::S_ZEROFILL | macho::S_GB_ZEROFILL | macho::S_THREAD_LOCAL_ZEROFILL => None, |
323 | _ => Some((self.offset(endian).into(), self.size(endian).into())), |
324 | } |
325 | } |
326 | |
327 | /// Return the section data. |
328 | /// |
329 | /// Returns `Ok(&[])` if the section has no data. |
330 | /// Returns `Err` for invalid values. |
331 | fn data<'data, R: ReadRef<'data>>( |
332 | &self, |
333 | endian: Self::Endian, |
334 | data: R, |
335 | ) -> result::Result<&'data [u8], ()> { |
336 | if let Some((offset, size)) = self.file_range(endian) { |
337 | data.read_bytes_at(offset, size) |
338 | } else { |
339 | Ok(&[]) |
340 | } |
341 | } |
342 | |
343 | /// Return the relocation array. |
344 | /// |
345 | /// Returns `Err` for invalid values. |
346 | fn relocations<'data, R: ReadRef<'data>>( |
347 | &self, |
348 | endian: Self::Endian, |
349 | data: R, |
350 | ) -> Result<&'data [macho::Relocation<Self::Endian>]> { |
351 | data.read_slice_at(self.reloff(endian).into(), self.nreloc(endian) as usize) |
352 | .read_error("Invalid Mach-O relocations offset or number" ) |
353 | } |
354 | } |
355 | |
356 | impl<Endian: endian::Endian> Section for macho::Section32<Endian> { |
357 | type Word = u32; |
358 | type Endian = Endian; |
359 | |
360 | fn sectname(&self) -> &[u8; 16] { |
361 | &self.sectname |
362 | } |
363 | fn segname(&self) -> &[u8; 16] { |
364 | &self.segname |
365 | } |
366 | fn addr(&self, endian: Self::Endian) -> Self::Word { |
367 | self.addr.get(endian) |
368 | } |
369 | fn size(&self, endian: Self::Endian) -> Self::Word { |
370 | self.size.get(endian) |
371 | } |
372 | fn offset(&self, endian: Self::Endian) -> u32 { |
373 | self.offset.get(endian) |
374 | } |
375 | fn align(&self, endian: Self::Endian) -> u32 { |
376 | self.align.get(endian) |
377 | } |
378 | fn reloff(&self, endian: Self::Endian) -> u32 { |
379 | self.reloff.get(endian) |
380 | } |
381 | fn nreloc(&self, endian: Self::Endian) -> u32 { |
382 | self.nreloc.get(endian) |
383 | } |
384 | fn flags(&self, endian: Self::Endian) -> u32 { |
385 | self.flags.get(endian) |
386 | } |
387 | } |
388 | |
389 | impl<Endian: endian::Endian> Section for macho::Section64<Endian> { |
390 | type Word = u64; |
391 | type Endian = Endian; |
392 | |
393 | fn sectname(&self) -> &[u8; 16] { |
394 | &self.sectname |
395 | } |
396 | fn segname(&self) -> &[u8; 16] { |
397 | &self.segname |
398 | } |
399 | fn addr(&self, endian: Self::Endian) -> Self::Word { |
400 | self.addr.get(endian) |
401 | } |
402 | fn size(&self, endian: Self::Endian) -> Self::Word { |
403 | self.size.get(endian) |
404 | } |
405 | fn offset(&self, endian: Self::Endian) -> u32 { |
406 | self.offset.get(endian) |
407 | } |
408 | fn align(&self, endian: Self::Endian) -> u32 { |
409 | self.align.get(endian) |
410 | } |
411 | fn reloff(&self, endian: Self::Endian) -> u32 { |
412 | self.reloff.get(endian) |
413 | } |
414 | fn nreloc(&self, endian: Self::Endian) -> u32 { |
415 | self.nreloc.get(endian) |
416 | } |
417 | fn flags(&self, endian: Self::Endian) -> u32 { |
418 | self.flags.get(endian) |
419 | } |
420 | } |
421 | |