1use core::fmt::Debug;
2use core::{fmt, mem, result, slice, str};
3
4use crate::endian::{self, Endianness, U32};
5use crate::macho;
6use crate::pod::Pod;
7use crate::read::{
8 self, gnu_compression, CompressedData, CompressedFileRange, Error, ObjectSection, ReadError,
9 ReadRef, RelocationMap, Result, SectionFlags, SectionIndex, SectionKind,
10};
11
12use super::{MachHeader, MachOFile, MachORelocationIterator};
13
14pub type MachOSectionIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
16 MachOSectionIterator<'data, 'file, macho::MachHeader32<Endian>, R>;
17pub type MachOSectionIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
19 MachOSectionIterator<'data, 'file, macho::MachHeader64<Endian>, R>;
20
21pub struct MachOSectionIterator<'data, 'file, Mach, R = &'data [u8]>
23where
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
31impl<'data, 'file, Mach, R> fmt::Debug for MachOSectionIterator<'data, 'file, Mach, R>
32where
33 Mach: MachHeader,
34 R: ReadRef<'data>,
35{
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 f.debug_struct("MachOSectionIterator").finish()
39 }
40}
41
42impl<'data, 'file, Mach, R> Iterator for MachOSectionIterator<'data, 'file, Mach, R>
43where
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| MachOSection {
51 file: self.file,
52 internal,
53 })
54 }
55}
56
57pub type MachOSection32<'data, 'file, Endian = Endianness, R = &'data [u8]> =
59 MachOSection<'data, 'file, macho::MachHeader32<Endian>, R>;
60pub type MachOSection64<'data, 'file, Endian = Endianness, R = &'data [u8]> =
62 MachOSection<'data, 'file, macho::MachHeader64<Endian>, R>;
63
64#[derive(Debug)]
68pub struct MachOSection<'data, 'file, Mach, R = &'data [u8]>
69where
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
77impl<'data, 'file, Mach, R> MachOSection<'data, 'file, Mach, R>
78where
79 Mach: MachHeader,
80 R: ReadRef<'data>,
81{
82 pub fn macho_file(&self) -> &'file MachOFile<'data, Mach, R> {
84 self.file
85 }
86
87 pub fn macho_section(&self) -> &'data Mach::Section {
89 self.internal.section
90 }
91
92 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 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
122impl<'data, 'file, Mach, R> read::private::Sealed for MachOSection<'data, 'file, Mach, R>
123where
124 Mach: MachHeader,
125 R: ReadRef<'data>,
126{
127}
128
129impl<'data, 'file, Mach, R> ObjectSection<'data> for MachOSection<'data, 'file, Mach, R>
130where
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.0)
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)]
241pub(super) struct MachOSectionInternal<'data, Mach: MachHeader, R: ReadRef<'data>> {
242 pub index: SectionIndex,
243 pub kind: SectionKind,
244 pub section: &'data Mach::Section,
245 pub data: R,
250}
251
252impl<'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 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#[allow(missing_docs)]
285pub 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 fn reserved1(&self, endian: Self::Endian) -> u32;
299 fn reserved2(&self, endian: Self::Endian) -> u32;
300
301 fn name(&self) -> &[u8] {
303 let sectname = &self.sectname()[..];
304 match memchr::memchr(b'\0', sectname) {
305 Some(end) => §name[..end],
306 None => sectname,
307 }
308 }
309
310 fn segment_name(&self) -> &[u8] {
312 let segname = &self.segname()[..];
313 match memchr::memchr(b'\0', segname) {
314 Some(end) => &segname[..end],
315 None => segname,
316 }
317 }
318
319 fn section_type(&self, endian: Self::Endian) -> u32 {
321 self.flags(endian) & macho::SECTION_TYPE
322 }
323
324 fn file_range(&self, endian: Self::Endian) -> Option<(u64, u64)> {
328 match self.section_type(endian) {
329 macho::S_ZEROFILL | macho::S_GB_ZEROFILL | macho::S_THREAD_LOCAL_ZEROFILL => None,
330 _ => Some((self.offset(endian).into(), self.size(endian).into())),
331 }
332 }
333
334 fn data<'data, R: ReadRef<'data>>(
339 &self,
340 endian: Self::Endian,
341 data: R,
342 ) -> result::Result<&'data [u8], ()> {
343 if let Some((offset, size)) = self.file_range(endian) {
344 data.read_bytes_at(offset, size)
345 } else {
346 Ok(&[])
347 }
348 }
349
350 fn relocations<'data, R: ReadRef<'data>>(
354 &self,
355 endian: Self::Endian,
356 data: R,
357 ) -> Result<&'data [macho::Relocation<Self::Endian>]> {
358 data.read_slice_at(self.reloff(endian).into(), self.nreloc(endian) as usize)
359 .read_error("Invalid Mach-O relocations offset or number")
360 }
361
362 fn symbol_stub_size(&self, endian: Self::Endian) -> u32 {
366 if self.section_type(endian) == macho::S_SYMBOL_STUBS {
367 self.reserved2(endian)
368 } else {
369 0
370 }
371 }
372
373 fn indirect_symbols<'data>(
377 &self,
378 endian: Self::Endian,
379 indirect_symbols: &'data [U32<Self::Endian>],
380 ) -> Result<&'data [U32<Self::Endian>]> {
381 let entry_size = match self.section_type(endian) {
382 macho::S_NON_LAZY_SYMBOL_POINTERS
383 | macho::S_LAZY_SYMBOL_POINTERS
384 | macho::S_LAZY_DYLIB_SYMBOL_POINTERS
385 | macho::S_THREAD_LOCAL_VARIABLE_POINTERS => mem::size_of::<Self::Word>(),
386 macho::S_SYMBOL_STUBS => {
387 let reserved2 = self.reserved2(endian);
388 if reserved2 == 0 {
389 return Err(Error("Invalid Mach-O stub size"));
390 }
391 reserved2 as usize
392 }
393 _ => return Ok(&[]),
394 };
395 let start = self.reserved1(endian) as usize;
396 let count = self.size(endian).into() as usize / entry_size;
397 indirect_symbols
398 .get(start..)
399 .and_then(|symbols| symbols.get(..count))
400 .read_error("Invalid Mach-O indirect symbol index or count")
401 }
402}
403
404impl<Endian: endian::Endian> Section for macho::Section32<Endian> {
405 type Word = u32;
406 type Endian = Endian;
407
408 fn sectname(&self) -> &[u8; 16] {
409 &self.sectname
410 }
411 fn segname(&self) -> &[u8; 16] {
412 &self.segname
413 }
414 fn addr(&self, endian: Self::Endian) -> Self::Word {
415 self.addr.get(endian)
416 }
417 fn size(&self, endian: Self::Endian) -> Self::Word {
418 self.size.get(endian)
419 }
420 fn offset(&self, endian: Self::Endian) -> u32 {
421 self.offset.get(endian)
422 }
423 fn align(&self, endian: Self::Endian) -> u32 {
424 self.align.get(endian)
425 }
426 fn reloff(&self, endian: Self::Endian) -> u32 {
427 self.reloff.get(endian)
428 }
429 fn nreloc(&self, endian: Self::Endian) -> u32 {
430 self.nreloc.get(endian)
431 }
432 fn flags(&self, endian: Self::Endian) -> u32 {
433 self.flags.get(endian)
434 }
435 fn reserved1(&self, endian: Self::Endian) -> u32 {
436 self.reserved1.get(endian)
437 }
438 fn reserved2(&self, endian: Self::Endian) -> u32 {
439 self.reserved2.get(endian)
440 }
441}
442
443impl<Endian: endian::Endian> Section for macho::Section64<Endian> {
444 type Word = u64;
445 type Endian = Endian;
446
447 fn sectname(&self) -> &[u8; 16] {
448 &self.sectname
449 }
450 fn segname(&self) -> &[u8; 16] {
451 &self.segname
452 }
453 fn addr(&self, endian: Self::Endian) -> Self::Word {
454 self.addr.get(endian)
455 }
456 fn size(&self, endian: Self::Endian) -> Self::Word {
457 self.size.get(endian)
458 }
459 fn offset(&self, endian: Self::Endian) -> u32 {
460 self.offset.get(endian)
461 }
462 fn align(&self, endian: Self::Endian) -> u32 {
463 self.align.get(endian)
464 }
465 fn reloff(&self, endian: Self::Endian) -> u32 {
466 self.reloff.get(endian)
467 }
468 fn nreloc(&self, endian: Self::Endian) -> u32 {
469 self.nreloc.get(endian)
470 }
471 fn flags(&self, endian: Self::Endian) -> u32 {
472 self.flags.get(endian)
473 }
474 fn reserved1(&self, endian: Self::Endian) -> u32 {
475 self.reserved1.get(endian)
476 }
477 fn reserved2(&self, endian: Self::Endian) -> u32 {
478 self.reserved2.get(endian)
479 }
480}