1 use core::{iter, result, slice, str}; 2 3 use crate::endian::LittleEndian as LE; 4 use crate::pe; 5 use crate::pod::Bytes; 6 use crate::read::util::StringTable; 7 use crate::read::{ 8 self, CompressedData, Error, ObjectSection, ObjectSegment, ReadError, Result, SectionFlags, 9 SectionIndex, SectionKind, 10 }; 11 12 use super::{CoffFile, CoffRelocationIterator}; 13 14 /// The table of section headers in a COFF or PE file. 15 #[derive(Debug, Default, Clone, Copy)] 16 pub struct SectionTable<'data> { 17 sections: &'data [pe::ImageSectionHeader], 18 } 19 20 impl<'data> SectionTable<'data> { 21 /// Parse the section table. 22 /// 23 /// `data` must be the data following the optional header. parse(header: &pe::ImageFileHeader, mut data: Bytes<'data>) -> Result<Self>24 pub fn parse(header: &pe::ImageFileHeader, mut data: Bytes<'data>) -> Result<Self> { 25 let sections = data 26 .read_slice(header.number_of_sections.get(LE) as usize) 27 .read_error("Invalid COFF/PE section headers")?; 28 Ok(SectionTable { sections }) 29 } 30 31 /// Iterate over the section headers. 32 /// 33 /// Warning: sections indices start at 1. 34 #[inline] iter(&self) -> slice::Iter<'data, pe::ImageSectionHeader>35 pub fn iter(&self) -> slice::Iter<'data, pe::ImageSectionHeader> { 36 self.sections.iter() 37 } 38 39 /// Return true if the section table is empty. 40 #[inline] is_empty(&self) -> bool41 pub fn is_empty(&self) -> bool { 42 self.sections.is_empty() 43 } 44 45 /// The number of section headers. 46 #[inline] len(&self) -> usize47 pub fn len(&self) -> usize { 48 self.sections.len() 49 } 50 51 /// Return the section header at the given index. 52 /// 53 /// The index is 1-based. section(&self, index: usize) -> read::Result<&'data pe::ImageSectionHeader>54 pub fn section(&self, index: usize) -> read::Result<&'data pe::ImageSectionHeader> { 55 self.sections 56 .get(index.wrapping_sub(1)) 57 .read_error("Invalid COFF/PE section index") 58 } 59 60 /// Return the section header with the given name. 61 /// 62 /// The returned index is 1-based. 63 /// 64 /// Ignores sections with invalid names. section_by_name( &self, strings: StringTable<'data>, name: &[u8], ) -> Option<(usize, &'data pe::ImageSectionHeader)>65 pub fn section_by_name( 66 &self, 67 strings: StringTable<'data>, 68 name: &[u8], 69 ) -> Option<(usize, &'data pe::ImageSectionHeader)> { 70 self.sections 71 .iter() 72 .enumerate() 73 .find(|(_, section)| section.name(strings) == Ok(name)) 74 .map(|(index, section)| (index + 1, section)) 75 } 76 } 77 78 /// An iterator over the loadable sections of a `CoffFile`. 79 #[derive(Debug)] 80 pub struct CoffSegmentIterator<'data, 'file> 81 where 82 'data: 'file, 83 { 84 pub(super) file: &'file CoffFile<'data>, 85 pub(super) iter: slice::Iter<'data, pe::ImageSectionHeader>, 86 } 87 88 impl<'data, 'file> Iterator for CoffSegmentIterator<'data, 'file> { 89 type Item = CoffSegment<'data, 'file>; 90 next(&mut self) -> Option<Self::Item>91 fn next(&mut self) -> Option<Self::Item> { 92 self.iter.next().map(|section| CoffSegment { 93 file: self.file, 94 section, 95 }) 96 } 97 } 98 99 /// A loadable section of a `CoffFile`. 100 #[derive(Debug)] 101 pub struct CoffSegment<'data, 'file> 102 where 103 'data: 'file, 104 { 105 pub(super) file: &'file CoffFile<'data>, 106 pub(super) section: &'data pe::ImageSectionHeader, 107 } 108 109 impl<'data, 'file> CoffSegment<'data, 'file> { bytes(&self) -> Result<Bytes<'data>>110 fn bytes(&self) -> Result<Bytes<'data>> { 111 self.section 112 .coff_bytes(self.file.data) 113 .read_error("Invalid COFF section offset or size") 114 } 115 } 116 117 impl<'data, 'file> read::private::Sealed for CoffSegment<'data, 'file> {} 118 119 impl<'data, 'file> ObjectSegment<'data> for CoffSegment<'data, 'file> { 120 #[inline] address(&self) -> u64121 fn address(&self) -> u64 { 122 u64::from(self.section.virtual_address.get(LE)) 123 } 124 125 #[inline] size(&self) -> u64126 fn size(&self) -> u64 { 127 u64::from(self.section.virtual_size.get(LE)) 128 } 129 130 #[inline] align(&self) -> u64131 fn align(&self) -> u64 { 132 self.section.coff_alignment() 133 } 134 135 #[inline] file_range(&self) -> (u64, u64)136 fn file_range(&self) -> (u64, u64) { 137 let (offset, size) = self.section.coff_file_range().unwrap_or((0, 0)); 138 (u64::from(offset), u64::from(size)) 139 } 140 data(&self) -> Result<&'data [u8]>141 fn data(&self) -> Result<&'data [u8]> { 142 Ok(self.bytes()?.0) 143 } 144 data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>>145 fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> { 146 Ok(read::data_range( 147 self.bytes()?, 148 self.address(), 149 address, 150 size, 151 )) 152 } 153 154 #[inline] name(&self) -> Result<Option<&str>>155 fn name(&self) -> Result<Option<&str>> { 156 let name = self.section.name(self.file.common.symbols.strings())?; 157 Ok(Some( 158 str::from_utf8(name) 159 .ok() 160 .read_error("Non UTF-8 COFF section name")?, 161 )) 162 } 163 } 164 165 /// An iterator over the sections of a `CoffFile`. 166 #[derive(Debug)] 167 pub struct CoffSectionIterator<'data, 'file> 168 where 169 'data: 'file, 170 { 171 pub(super) file: &'file CoffFile<'data>, 172 pub(super) iter: iter::Enumerate<slice::Iter<'data, pe::ImageSectionHeader>>, 173 } 174 175 impl<'data, 'file> Iterator for CoffSectionIterator<'data, 'file> { 176 type Item = CoffSection<'data, 'file>; 177 next(&mut self) -> Option<Self::Item>178 fn next(&mut self) -> Option<Self::Item> { 179 self.iter.next().map(|(index, section)| CoffSection { 180 file: self.file, 181 index: SectionIndex(index + 1), 182 section, 183 }) 184 } 185 } 186 187 /// A section of a `CoffFile`. 188 #[derive(Debug)] 189 pub struct CoffSection<'data, 'file> 190 where 191 'data: 'file, 192 { 193 pub(super) file: &'file CoffFile<'data>, 194 pub(super) index: SectionIndex, 195 pub(super) section: &'data pe::ImageSectionHeader, 196 } 197 198 impl<'data, 'file> CoffSection<'data, 'file> { bytes(&self) -> Result<Bytes<'data>>199 fn bytes(&self) -> Result<Bytes<'data>> { 200 self.section 201 .coff_bytes(self.file.data) 202 .read_error("Invalid COFF section offset or size") 203 } 204 } 205 206 impl<'data, 'file> read::private::Sealed for CoffSection<'data, 'file> {} 207 208 impl<'data, 'file> ObjectSection<'data> for CoffSection<'data, 'file> { 209 type RelocationIterator = CoffRelocationIterator<'data, 'file>; 210 211 #[inline] index(&self) -> SectionIndex212 fn index(&self) -> SectionIndex { 213 self.index 214 } 215 216 #[inline] address(&self) -> u64217 fn address(&self) -> u64 { 218 u64::from(self.section.virtual_address.get(LE)) 219 } 220 221 #[inline] size(&self) -> u64222 fn size(&self) -> u64 { 223 // TODO: This may need to be the length from the auxiliary symbol for this section. 224 u64::from(self.section.size_of_raw_data.get(LE)) 225 } 226 227 #[inline] align(&self) -> u64228 fn align(&self) -> u64 { 229 self.section.coff_alignment() 230 } 231 232 #[inline] file_range(&self) -> Option<(u64, u64)>233 fn file_range(&self) -> Option<(u64, u64)> { 234 let (offset, size) = self.section.coff_file_range()?; 235 Some((u64::from(offset), u64::from(size))) 236 } 237 data(&self) -> Result<&'data [u8]>238 fn data(&self) -> Result<&'data [u8]> { 239 Ok(self.bytes()?.0) 240 } 241 data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>>242 fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> { 243 Ok(read::data_range( 244 self.bytes()?, 245 self.address(), 246 address, 247 size, 248 )) 249 } 250 251 #[inline] compressed_data(&self) -> Result<CompressedData<'data>>252 fn compressed_data(&self) -> Result<CompressedData<'data>> { 253 self.data().map(CompressedData::none) 254 } 255 256 #[inline] name(&self) -> Result<&str>257 fn name(&self) -> Result<&str> { 258 let name = self.section.name(self.file.common.symbols.strings())?; 259 str::from_utf8(name) 260 .ok() 261 .read_error("Non UTF-8 COFF section name") 262 } 263 264 #[inline] segment_name(&self) -> Result<Option<&str>>265 fn segment_name(&self) -> Result<Option<&str>> { 266 Ok(None) 267 } 268 269 #[inline] kind(&self) -> SectionKind270 fn kind(&self) -> SectionKind { 271 self.section.kind() 272 } 273 relocations(&self) -> CoffRelocationIterator<'data, 'file>274 fn relocations(&self) -> CoffRelocationIterator<'data, 'file> { 275 let pointer = self.section.pointer_to_relocations.get(LE) as usize; 276 let number = self.section.number_of_relocations.get(LE) as usize; 277 let relocations = self.file.data.read_slice_at(pointer, number).unwrap_or(&[]); 278 CoffRelocationIterator { 279 file: self.file, 280 iter: relocations.iter(), 281 } 282 } 283 flags(&self) -> SectionFlags284 fn flags(&self) -> SectionFlags { 285 SectionFlags::Coff { 286 characteristics: self.section.characteristics.get(LE), 287 } 288 } 289 } 290 291 impl pe::ImageSectionHeader { 292 /// Return the offset and size of the section in the file. 293 /// 294 /// Returns `None` for sections that have no data in the file. coff_file_range(&self) -> Option<(u32, u32)>295 fn coff_file_range(&self) -> Option<(u32, u32)> { 296 if self.characteristics.get(LE) & pe::IMAGE_SCN_CNT_UNINITIALIZED_DATA != 0 { 297 None 298 } else { 299 let offset = self.pointer_to_raw_data.get(LE); 300 // Note: virtual size is not used for COFF. 301 let size = self.size_of_raw_data.get(LE); 302 Some((offset, size)) 303 } 304 } 305 306 /// Return the section data. 307 /// 308 /// Returns `Ok(&[])` if the section has no data. 309 /// Returns `Err` for invalid values. coff_bytes<'data>(&self, data: Bytes<'data>) -> result::Result<Bytes<'data>, ()>310 fn coff_bytes<'data>(&self, data: Bytes<'data>) -> result::Result<Bytes<'data>, ()> { 311 if let Some((offset, size)) = self.coff_file_range() { 312 data.read_bytes_at(offset as usize, size as usize) 313 } else { 314 Ok(Bytes(&[])) 315 } 316 } 317 name<'data>(&'data self, strings: StringTable<'data>) -> Result<&'data [u8]>318 pub(crate) fn name<'data>(&'data self, strings: StringTable<'data>) -> Result<&'data [u8]> { 319 let bytes = &self.name; 320 Ok(if bytes[0] == b'/' { 321 let mut offset = 0; 322 if bytes[1] == b'/' { 323 for byte in bytes[2..].iter() { 324 let digit = match byte { 325 b'A'..=b'Z' => byte - b'A', 326 b'a'..=b'z' => byte - b'a' + 26, 327 b'0'..=b'9' => byte - b'0' + 52, 328 b'+' => 62, 329 b'/' => 63, 330 _ => return Err(Error("Invalid COFF section name base-64 offset")), 331 }; 332 offset = offset * 64 + digit as u32; 333 } 334 } else { 335 for byte in bytes[1..].iter() { 336 let digit = match byte { 337 b'0'..=b'9' => byte - b'0', 338 0 => break, 339 _ => return Err(Error("Invalid COFF section name base-10 offset")), 340 }; 341 offset = offset * 10 + digit as u32; 342 } 343 }; 344 strings 345 .get(offset) 346 .read_error("Invalid COFF section name offset")? 347 } else { 348 match bytes.iter().position(|&x| x == 0) { 349 Some(end) => &bytes[..end], 350 None => &bytes[..], 351 } 352 }) 353 } 354 kind(&self) -> SectionKind355 pub(crate) fn kind(&self) -> SectionKind { 356 let characteristics = self.characteristics.get(LE); 357 if characteristics & (pe::IMAGE_SCN_CNT_CODE | pe::IMAGE_SCN_MEM_EXECUTE) != 0 { 358 SectionKind::Text 359 } else if characteristics & pe::IMAGE_SCN_CNT_INITIALIZED_DATA != 0 { 360 if characteristics & pe::IMAGE_SCN_MEM_DISCARDABLE != 0 { 361 SectionKind::Other 362 } else if characteristics & pe::IMAGE_SCN_MEM_WRITE != 0 { 363 SectionKind::Data 364 } else { 365 SectionKind::ReadOnlyData 366 } 367 } else if characteristics & pe::IMAGE_SCN_CNT_UNINITIALIZED_DATA != 0 { 368 SectionKind::UninitializedData 369 } else if characteristics & pe::IMAGE_SCN_LNK_INFO != 0 { 370 SectionKind::Linker 371 } else { 372 SectionKind::Unknown 373 } 374 } 375 coff_alignment(&self) -> u64376 fn coff_alignment(&self) -> u64 { 377 match self.characteristics.get(LE) & pe::IMAGE_SCN_ALIGN_MASK { 378 pe::IMAGE_SCN_ALIGN_1BYTES => 1, 379 pe::IMAGE_SCN_ALIGN_2BYTES => 2, 380 pe::IMAGE_SCN_ALIGN_4BYTES => 4, 381 pe::IMAGE_SCN_ALIGN_8BYTES => 8, 382 pe::IMAGE_SCN_ALIGN_16BYTES => 16, 383 pe::IMAGE_SCN_ALIGN_32BYTES => 32, 384 pe::IMAGE_SCN_ALIGN_64BYTES => 64, 385 pe::IMAGE_SCN_ALIGN_128BYTES => 128, 386 pe::IMAGE_SCN_ALIGN_256BYTES => 256, 387 pe::IMAGE_SCN_ALIGN_512BYTES => 512, 388 pe::IMAGE_SCN_ALIGN_1024BYTES => 1024, 389 pe::IMAGE_SCN_ALIGN_2048BYTES => 2048, 390 pe::IMAGE_SCN_ALIGN_4096BYTES => 4096, 391 pe::IMAGE_SCN_ALIGN_8192BYTES => 8192, 392 _ => 16, 393 } 394 } 395 } 396