1use core::fmt::Debug;
2use core::{fmt, result, slice, str};
3
4use crate::endian::{self, Endianness};
5use crate::macho;
6use crate::pod::Pod;
7use crate::read::{
8 self, gnu_compression, CompressedData, CompressedFileRange, ObjectSection, ReadError, ReadRef,
9 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)
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
299 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 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 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 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 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
356impl<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
389impl<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}