1 use core::fmt::Debug;
2 use core::{result, str};
3 
4 use crate::endian::{self, Endianness};
5 use crate::macho;
6 use crate::pod::{Bytes, Pod};
7 use crate::read::{self, ObjectSegment, ReadError, Result};
8 
9 use super::{MachHeader, MachOFile, MachOLoadCommand, MachOLoadCommandIterator, Section};
10 
11 /// An iterator over the segments of a `MachOFile32`.
12 pub type MachOSegmentIterator32<'data, 'file, Endian = Endianness> =
13     MachOSegmentIterator<'data, 'file, macho::MachHeader32<Endian>>;
14 /// An iterator over the segments of a `MachOFile64`.
15 pub type MachOSegmentIterator64<'data, 'file, Endian = Endianness> =
16     MachOSegmentIterator<'data, 'file, macho::MachHeader64<Endian>>;
17 
18 /// An iterator over the segments of a `MachOFile`.
19 #[derive(Debug)]
20 pub struct MachOSegmentIterator<'data, 'file, Mach>
21 where
22     'data: 'file,
23     Mach: MachHeader,
24 {
25     pub(super) file: &'file MachOFile<'data, Mach>,
26     pub(super) commands: MachOLoadCommandIterator<'data, Mach::Endian>,
27 }
28 
29 impl<'data, 'file, Mach: MachHeader> Iterator for MachOSegmentIterator<'data, 'file, Mach> {
30     type Item = MachOSegment<'data, 'file, Mach>;
31 
next(&mut self) -> Option<Self::Item>32     fn next(&mut self) -> Option<Self::Item> {
33         loop {
34             let command = self.commands.next().ok()??;
35             if let Ok(Some((segment, _))) = Mach::Segment::from_command(command) {
36                 return Some(MachOSegment {
37                     file: self.file,
38                     segment,
39                 });
40             }
41         }
42     }
43 }
44 
45 /// A segment of a `MachOFile32`.
46 pub type MachOSegment32<'data, 'file, Endian = Endianness> =
47     MachOSegment<'data, 'file, macho::MachHeader32<Endian>>;
48 /// A segment of a `MachOFile64`.
49 pub type MachOSegment64<'data, 'file, Endian = Endianness> =
50     MachOSegment<'data, 'file, macho::MachHeader64<Endian>>;
51 
52 /// A segment of a `MachOFile`.
53 #[derive(Debug)]
54 pub struct MachOSegment<'data, 'file, Mach>
55 where
56     'data: 'file,
57     Mach: MachHeader,
58 {
59     file: &'file MachOFile<'data, Mach>,
60     segment: &'data Mach::Segment,
61 }
62 
63 impl<'data, 'file, Mach: MachHeader> MachOSegment<'data, 'file, Mach> {
bytes(&self) -> Result<Bytes<'data>>64     fn bytes(&self) -> Result<Bytes<'data>> {
65         self.segment
66             .data(self.file.endian, self.file.data)
67             .read_error("Invalid Mach-O segment size or offset")
68     }
69 }
70 
71 impl<'data, 'file, Mach: MachHeader> read::private::Sealed for MachOSegment<'data, 'file, Mach> {}
72 
73 impl<'data, 'file, Mach: MachHeader> ObjectSegment<'data> for MachOSegment<'data, 'file, Mach> {
74     #[inline]
address(&self) -> u6475     fn address(&self) -> u64 {
76         self.segment.vmaddr(self.file.endian).into()
77     }
78 
79     #[inline]
size(&self) -> u6480     fn size(&self) -> u64 {
81         self.segment.vmsize(self.file.endian).into()
82     }
83 
84     #[inline]
align(&self) -> u6485     fn align(&self) -> u64 {
86         // Page size.
87         0x1000
88     }
89 
90     #[inline]
file_range(&self) -> (u64, u64)91     fn file_range(&self) -> (u64, u64) {
92         self.segment.file_range(self.file.endian)
93     }
94 
data(&self) -> Result<&'data [u8]>95     fn data(&self) -> Result<&'data [u8]> {
96         Ok(self.bytes()?.0)
97     }
98 
data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>>99     fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> {
100         Ok(read::data_range(
101             self.bytes()?,
102             self.address(),
103             address,
104             size,
105         ))
106     }
107 
108     #[inline]
name(&self) -> Result<Option<&str>>109     fn name(&self) -> Result<Option<&str>> {
110         Ok(Some(
111             str::from_utf8(self.segment.name())
112                 .ok()
113                 .read_error("Non UTF-8 Mach-O segment name")?,
114         ))
115     }
116 }
117 
118 /// A trait for generic access to `SegmentCommand32` and `SegmentCommand64`.
119 #[allow(missing_docs)]
120 pub trait Segment: Debug + Pod {
121     type Word: Into<u64>;
122     type Endian: endian::Endian;
123     type Section: Section<Endian = Self::Endian>;
124 
from_command(command: MachOLoadCommand<Self::Endian>) -> Result<Option<(&Self, Bytes)>>125     fn from_command(command: MachOLoadCommand<Self::Endian>) -> Result<Option<(&Self, Bytes)>>;
126 
cmd(&self, endian: Self::Endian) -> u32127     fn cmd(&self, endian: Self::Endian) -> u32;
cmdsize(&self, endian: Self::Endian) -> u32128     fn cmdsize(&self, endian: Self::Endian) -> u32;
segname(&self) -> &[u8; 16]129     fn segname(&self) -> &[u8; 16];
vmaddr(&self, endian: Self::Endian) -> Self::Word130     fn vmaddr(&self, endian: Self::Endian) -> Self::Word;
vmsize(&self, endian: Self::Endian) -> Self::Word131     fn vmsize(&self, endian: Self::Endian) -> Self::Word;
fileoff(&self, endian: Self::Endian) -> Self::Word132     fn fileoff(&self, endian: Self::Endian) -> Self::Word;
filesize(&self, endian: Self::Endian) -> Self::Word133     fn filesize(&self, endian: Self::Endian) -> Self::Word;
maxprot(&self, endian: Self::Endian) -> u32134     fn maxprot(&self, endian: Self::Endian) -> u32;
initprot(&self, endian: Self::Endian) -> u32135     fn initprot(&self, endian: Self::Endian) -> u32;
nsects(&self, endian: Self::Endian) -> u32136     fn nsects(&self, endian: Self::Endian) -> u32;
flags(&self, endian: Self::Endian) -> u32137     fn flags(&self, endian: Self::Endian) -> u32;
138 
139     /// Return the `segname` bytes up until the null terminator.
name(&self) -> &[u8]140     fn name(&self) -> &[u8] {
141         let segname = &self.segname()[..];
142         match segname.iter().position(|&x| x == 0) {
143             Some(end) => &segname[..end],
144             None => segname,
145         }
146     }
147 
148     /// Return the offset and size of the segment in the file.
file_range(&self, endian: Self::Endian) -> (u64, u64)149     fn file_range(&self, endian: Self::Endian) -> (u64, u64) {
150         (self.fileoff(endian).into(), self.filesize(endian).into())
151     }
152 
153     /// Get the segment data from the file data.
154     ///
155     /// Returns `Err` for invalid values.
data<'data>( &self, endian: Self::Endian, data: Bytes<'data>, ) -> result::Result<Bytes<'data>, ()>156     fn data<'data>(
157         &self,
158         endian: Self::Endian,
159         data: Bytes<'data>,
160     ) -> result::Result<Bytes<'data>, ()> {
161         let (offset, size) = self.file_range(endian);
162         data.read_bytes_at(offset as usize, size as usize)
163     }
164 
165     /// Get the array of sections from the data following the segment command.
166     ///
167     /// Returns `Err` for invalid values.
sections<'data>( &self, endian: Self::Endian, data: Bytes<'data>, ) -> Result<&'data [Self::Section]>168     fn sections<'data>(
169         &self,
170         endian: Self::Endian,
171         data: Bytes<'data>,
172     ) -> Result<&'data [Self::Section]> {
173         data.read_slice_at(0, self.nsects(endian) as usize)
174             .read_error("Invalid Mach-O number of sections")
175     }
176 }
177 
178 impl<Endian: endian::Endian> Segment for macho::SegmentCommand32<Endian> {
179     type Word = u32;
180     type Endian = Endian;
181     type Section = macho::Section32<Self::Endian>;
182 
from_command(command: MachOLoadCommand<Self::Endian>) -> Result<Option<(&Self, Bytes)>>183     fn from_command(command: MachOLoadCommand<Self::Endian>) -> Result<Option<(&Self, Bytes)>> {
184         command.segment_32()
185     }
186 
cmd(&self, endian: Self::Endian) -> u32187     fn cmd(&self, endian: Self::Endian) -> u32 {
188         self.cmd.get(endian)
189     }
cmdsize(&self, endian: Self::Endian) -> u32190     fn cmdsize(&self, endian: Self::Endian) -> u32 {
191         self.cmdsize.get(endian)
192     }
segname(&self) -> &[u8; 16]193     fn segname(&self) -> &[u8; 16] {
194         &self.segname
195     }
vmaddr(&self, endian: Self::Endian) -> Self::Word196     fn vmaddr(&self, endian: Self::Endian) -> Self::Word {
197         self.vmaddr.get(endian)
198     }
vmsize(&self, endian: Self::Endian) -> Self::Word199     fn vmsize(&self, endian: Self::Endian) -> Self::Word {
200         self.vmsize.get(endian)
201     }
fileoff(&self, endian: Self::Endian) -> Self::Word202     fn fileoff(&self, endian: Self::Endian) -> Self::Word {
203         self.fileoff.get(endian)
204     }
filesize(&self, endian: Self::Endian) -> Self::Word205     fn filesize(&self, endian: Self::Endian) -> Self::Word {
206         self.filesize.get(endian)
207     }
maxprot(&self, endian: Self::Endian) -> u32208     fn maxprot(&self, endian: Self::Endian) -> u32 {
209         self.maxprot.get(endian)
210     }
initprot(&self, endian: Self::Endian) -> u32211     fn initprot(&self, endian: Self::Endian) -> u32 {
212         self.initprot.get(endian)
213     }
nsects(&self, endian: Self::Endian) -> u32214     fn nsects(&self, endian: Self::Endian) -> u32 {
215         self.nsects.get(endian)
216     }
flags(&self, endian: Self::Endian) -> u32217     fn flags(&self, endian: Self::Endian) -> u32 {
218         self.flags.get(endian)
219     }
220 }
221 
222 impl<Endian: endian::Endian> Segment for macho::SegmentCommand64<Endian> {
223     type Word = u64;
224     type Endian = Endian;
225     type Section = macho::Section64<Self::Endian>;
226 
from_command(command: MachOLoadCommand<Self::Endian>) -> Result<Option<(&Self, Bytes)>>227     fn from_command(command: MachOLoadCommand<Self::Endian>) -> Result<Option<(&Self, Bytes)>> {
228         command.segment_64()
229     }
230 
cmd(&self, endian: Self::Endian) -> u32231     fn cmd(&self, endian: Self::Endian) -> u32 {
232         self.cmd.get(endian)
233     }
cmdsize(&self, endian: Self::Endian) -> u32234     fn cmdsize(&self, endian: Self::Endian) -> u32 {
235         self.cmdsize.get(endian)
236     }
segname(&self) -> &[u8; 16]237     fn segname(&self) -> &[u8; 16] {
238         &self.segname
239     }
vmaddr(&self, endian: Self::Endian) -> Self::Word240     fn vmaddr(&self, endian: Self::Endian) -> Self::Word {
241         self.vmaddr.get(endian)
242     }
vmsize(&self, endian: Self::Endian) -> Self::Word243     fn vmsize(&self, endian: Self::Endian) -> Self::Word {
244         self.vmsize.get(endian)
245     }
fileoff(&self, endian: Self::Endian) -> Self::Word246     fn fileoff(&self, endian: Self::Endian) -> Self::Word {
247         self.fileoff.get(endian)
248     }
filesize(&self, endian: Self::Endian) -> Self::Word249     fn filesize(&self, endian: Self::Endian) -> Self::Word {
250         self.filesize.get(endian)
251     }
maxprot(&self, endian: Self::Endian) -> u32252     fn maxprot(&self, endian: Self::Endian) -> u32 {
253         self.maxprot.get(endian)
254     }
initprot(&self, endian: Self::Endian) -> u32255     fn initprot(&self, endian: Self::Endian) -> u32 {
256         self.initprot.get(endian)
257     }
nsects(&self, endian: Self::Endian) -> u32258     fn nsects(&self, endian: Self::Endian) -> u32 {
259         self.nsects.get(endian)
260     }
flags(&self, endian: Self::Endian) -> u32261     fn flags(&self, endian: Self::Endian) -> u32 {
262         self.flags.get(endian)
263     }
264 }
265