| 1 | use core::fmt::Debug; | 
| 2 | use core::{fmt, result, slice, str}; | 
| 3 |  | 
| 4 | use crate::endian::{self, Endianness}; | 
| 5 | use crate::macho; | 
| 6 | use crate::pod::Pod; | 
| 7 | use crate::read::{ | 
| 8 |     self, CompressedData, CompressedFileRange, ObjectSection, ReadError, ReadRef, Result, | 
| 9 |     SectionFlags, SectionIndex, SectionKind, | 
| 10 | }; | 
| 11 |  | 
| 12 | use super::{MachHeader, MachOFile, MachORelocationIterator}; | 
| 13 |  | 
| 14 | /// An iterator for the sections in a [`MachOFile32`](super::MachOFile32). | 
| 15 | pub 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). | 
| 18 | pub 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`]. | 
| 22 | pub struct MachOSectionIterator<'data, 'file, Mach, R = &'data [u8]> | 
| 23 | where | 
| 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 |  | 
| 31 | impl<'data, 'file, Mach, R> fmt::Debug for MachOSectionIterator<'data, 'file, Mach, R> | 
| 32 | where | 
| 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 |  | 
| 42 | impl<'data, 'file, Mach, R> Iterator for MachOSectionIterator<'data, 'file, Mach, R> | 
| 43 | where | 
| 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). | 
| 58 | pub 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). | 
| 61 | pub 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)] | 
| 68 | pub struct MachOSection<'data, 'file, Mach, R = &'data [u8]> | 
| 69 | where | 
| 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 |  | 
| 77 | impl<'data, 'file, Mach, R> MachOSection<'data, 'file, Mach, R> | 
| 78 | where | 
| 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 |  | 
| 92 | impl<'data, 'file, Mach, R> read::private::Sealed for MachOSection<'data, 'file, Mach, R> | 
| 93 | where | 
| 94 |     Mach: MachHeader, | 
| 95 |     R: ReadRef<'data>, | 
| 96 | { | 
| 97 | } | 
| 98 |  | 
| 99 | impl<'data, 'file, Mach, R> ObjectSection<'data> for MachOSection<'data, 'file, Mach, R> | 
| 100 | where | 
| 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)] | 
| 210 | pub(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 |  | 
| 217 | impl<'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)] | 
| 254 | pub 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) => §name[..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 |  | 
| 325 | impl<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 |  | 
| 358 | impl<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 |  |