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, CompressedData, CompressedFileRange, ObjectSection, ReadError, ReadRef, Result,
9 SectionFlags, SectionIndex, SectionKind,
10};
11
12use super::{MachHeader, MachOFile, MachORelocationIterator};
13
14/// An iterator for the sections in a [`MachOFile32`](super::MachOFile32).
15pub 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).
18pub 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`].
22pub 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>>,
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 // It's painful to do much better than this
38 f.debug_struct(name:"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: MachOSectionInternal<'_, …>| MachOSection {
51 file: self.file,
52 internal,
53 })
54 }
55}
56
57/// A section in a [`MachOFile32`](super::MachOFile32).
58pub 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).
61pub 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)]
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>,
75}
76
77impl<'data, 'file, Mach, R> MachOSection<'data, 'file, Mach, R>
78where
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
92impl<'data, 'file, Mach, R> read::private::Sealed for MachOSection<'data, 'file, Mach, R>
93where
94 Mach: MachHeader,
95 R: ReadRef<'data>,
96{
97}
98
99impl<'data, 'file, Mach, R> ObjectSection<'data> for MachOSection<'data, 'file, Mach, R>
100where
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)]
210pub(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
217impl<'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)]
254pub 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) => &sectname[..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
325impl<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
358impl<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