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