1 use core::marker::PhantomData;
2 
3 use crate::endian::Endian;
4 use crate::macho;
5 use crate::pod::Bytes;
6 use crate::read::macho::{MachHeader, SymbolTable};
7 use crate::read::{ReadError, Result, StringTable};
8 
9 /// An iterator over the load commands of a `MachHeader`.
10 #[derive(Debug, Default, Clone, Copy)]
11 pub struct MachOLoadCommandIterator<'data, E: Endian> {
12     endian: E,
13     data: Bytes<'data>,
14     ncmds: u32,
15 }
16 
17 impl<'data, E: Endian> MachOLoadCommandIterator<'data, E> {
new(endian: E, data: Bytes<'data>, ncmds: u32) -> Self18     pub(super) fn new(endian: E, data: Bytes<'data>, ncmds: u32) -> Self {
19         MachOLoadCommandIterator {
20             endian,
21             data,
22             ncmds,
23         }
24     }
25 
26     /// Return the next load command.
next(&mut self) -> Result<Option<MachOLoadCommand<'data, E>>>27     pub fn next(&mut self) -> Result<Option<MachOLoadCommand<'data, E>>> {
28         if self.ncmds == 0 {
29             return Ok(None);
30         }
31         let header = self
32             .data
33             .read_at::<macho::LoadCommand<E>>(0)
34             .read_error("Invalid Mach-O load command header")?;
35         let cmd = header.cmd.get(self.endian);
36         let cmdsize = header.cmdsize.get(self.endian) as usize;
37         let data = self
38             .data
39             .read_bytes(cmdsize)
40             .read_error("Invalid Mach-O load command size")?;
41         self.ncmds -= 1;
42         Ok(Some(MachOLoadCommand {
43             cmd,
44             data,
45             marker: Default::default(),
46         }))
47     }
48 }
49 
50 /// A parsed `LoadCommand`.
51 #[derive(Debug, Clone, Copy)]
52 pub struct MachOLoadCommand<'data, E: Endian> {
53     cmd: u32,
54     // Includes the header.
55     data: Bytes<'data>,
56     marker: PhantomData<E>,
57 }
58 
59 impl<'data, E: Endian> MachOLoadCommand<'data, E> {
60     /// Try to parse this command as a `SegmentCommand32`.
segment_32(self) -> Result<Option<(&'data macho::SegmentCommand32<E>, Bytes<'data>)>>61     pub fn segment_32(self) -> Result<Option<(&'data macho::SegmentCommand32<E>, Bytes<'data>)>> {
62         if self.cmd == macho::LC_SEGMENT {
63             let mut data = self.data;
64             let command = data
65                 .read()
66                 .read_error("Invalid Mach-O LC_SEGMENT command size")?;
67             Ok(Some((command, data)))
68         } else {
69             Ok(None)
70         }
71     }
72 
73     /// Try to parse this command as a `SymtabCommand`.
symtab(self) -> Result<Option<&'data macho::SymtabCommand<E>>>74     pub fn symtab(self) -> Result<Option<&'data macho::SymtabCommand<E>>> {
75         if self.cmd == macho::LC_SYMTAB {
76             Some(
77                 self.data
78                     .clone()
79                     .read()
80                     .read_error("Invalid Mach-O LC_SYMTAB command size"),
81             )
82             .transpose()
83         } else {
84             Ok(None)
85         }
86     }
87 
88     /// Try to parse this command as a `UuidCommand`.
uuid(self) -> Result<Option<&'data macho::UuidCommand<E>>>89     pub fn uuid(self) -> Result<Option<&'data macho::UuidCommand<E>>> {
90         if self.cmd == macho::LC_UUID {
91             Some(
92                 self.data
93                     .clone()
94                     .read()
95                     .read_error("Invalid Mach-O LC_UUID command size"),
96             )
97             .transpose()
98         } else {
99             Ok(None)
100         }
101     }
102 
103     /// Try to parse this command as a `SegmentCommand64`.
segment_64(self) -> Result<Option<(&'data macho::SegmentCommand64<E>, Bytes<'data>)>>104     pub fn segment_64(self) -> Result<Option<(&'data macho::SegmentCommand64<E>, Bytes<'data>)>> {
105         if self.cmd == macho::LC_SEGMENT_64 {
106             let mut data = self.data;
107             let command = data
108                 .read()
109                 .read_error("Invalid Mach-O LC_SEGMENT_64 command size")?;
110             Ok(Some((command, data)))
111         } else {
112             Ok(None)
113         }
114     }
115 
116     /// Try to parse this command as an `EntryPointCommand`.
entry_point(self) -> Result<Option<&'data macho::EntryPointCommand<E>>>117     pub fn entry_point(self) -> Result<Option<&'data macho::EntryPointCommand<E>>> {
118         if self.cmd == macho::LC_MAIN {
119             Some(
120                 self.data
121                     .clone()
122                     .read()
123                     .read_error("Invalid Mach-O LC_MAIN command size"),
124             )
125             .transpose()
126         } else {
127             Ok(None)
128         }
129     }
130 }
131 
132 impl<E: Endian> macho::SymtabCommand<E> {
133     /// Return the symbol table that this command references.
symbols<'data, Mach: MachHeader<Endian = E>>( &self, endian: E, data: Bytes<'data>, ) -> Result<SymbolTable<'data, Mach>>134     pub fn symbols<'data, Mach: MachHeader<Endian = E>>(
135         &self,
136         endian: E,
137         data: Bytes<'data>,
138     ) -> Result<SymbolTable<'data, Mach>> {
139         let symbols = data
140             .read_slice_at(
141                 self.symoff.get(endian) as usize,
142                 self.nsyms.get(endian) as usize,
143             )
144             .read_error("Invalid Mach-O symbol table offset or size")?;
145         let strings = data
146             .read_bytes_at(
147                 self.stroff.get(endian) as usize,
148                 self.strsize.get(endian) as usize,
149             )
150             .read_error("Invalid Mach-O string table offset or size")?;
151         let strings = StringTable::new(strings);
152         Ok(SymbolTable::new(symbols, strings))
153     }
154 }
155