]> git.proxmox.com Git - rustc.git/blame - vendor/object-0.30.3/src/read/coff/section.rs
New upstream version 1.71.1+dfsg1
[rustc.git] / vendor / object-0.30.3 / src / read / coff / section.rs
CommitLineData
9c376795
FG
1use core::convert::TryFrom;
2use core::{iter, result, slice, str};
3
4use crate::endian::LittleEndian as LE;
5use crate::pe;
6use crate::read::util::StringTable;
7use crate::read::{
8 self, CompressedData, CompressedFileRange, Error, ObjectSection, ObjectSegment, ReadError,
9 ReadRef, Result, SectionFlags, SectionIndex, SectionKind, SegmentFlags,
10};
11
12use super::{CoffFile, CoffRelocationIterator};
13
14/// The table of section headers in a COFF or PE file.
15#[derive(Debug, Default, Clone, Copy)]
16pub struct SectionTable<'data> {
17 sections: &'data [pe::ImageSectionHeader],
18}
19
20impl<'data> SectionTable<'data> {
21 /// Parse the section table.
22 ///
23 /// `data` must be the entire file data.
24 /// `offset` must be after the optional file header.
25 pub fn parse<R: ReadRef<'data>>(
26 header: &pe::ImageFileHeader,
27 data: R,
28 offset: u64,
29 ) -> Result<Self> {
30 let sections = data
31 .read_slice_at(offset, header.number_of_sections.get(LE).into())
32 .read_error("Invalid COFF/PE section headers")?;
33 Ok(SectionTable { sections })
34 }
35
36 /// Iterate over the section headers.
37 ///
38 /// Warning: sections indices start at 1.
39 #[inline]
40 pub fn iter(&self) -> slice::Iter<'data, pe::ImageSectionHeader> {
41 self.sections.iter()
42 }
43
44 /// Return true if the section table is empty.
45 #[inline]
46 pub fn is_empty(&self) -> bool {
47 self.sections.is_empty()
48 }
49
50 /// The number of section headers.
51 #[inline]
52 pub fn len(&self) -> usize {
53 self.sections.len()
54 }
55
56 /// Return the section header at the given index.
57 ///
58 /// The index is 1-based.
59 pub fn section(&self, index: usize) -> read::Result<&'data pe::ImageSectionHeader> {
60 self.sections
61 .get(index.wrapping_sub(1))
62 .read_error("Invalid COFF/PE section index")
63 }
64
65 /// Return the section header with the given name.
66 ///
67 /// The returned index is 1-based.
68 ///
69 /// Ignores sections with invalid names.
70 pub fn section_by_name<R: ReadRef<'data>>(
71 &self,
72 strings: StringTable<'data, R>,
73 name: &[u8],
74 ) -> Option<(usize, &'data pe::ImageSectionHeader)> {
75 self.sections
76 .iter()
77 .enumerate()
78 .find(|(_, section)| section.name(strings) == Ok(name))
79 .map(|(index, section)| (index + 1, section))
80 }
81
82 /// Compute the maximum file offset used by sections.
83 ///
84 /// This will usually match the end of file, unless the PE file has a
85 /// [data overlay](https://security.stackexchange.com/questions/77336/how-is-the-file-overlay-read-by-an-exe-virus)
86 pub fn max_section_file_offset(&self) -> u64 {
87 let mut max = 0;
88 for section in self.iter() {
89 match (section.pointer_to_raw_data.get(LE) as u64)
90 .checked_add(section.size_of_raw_data.get(LE) as u64)
91 {
92 None => {
93 // This cannot happen, we're suming two u32 into a u64
94 continue;
95 }
96 Some(end_of_section) => {
97 if end_of_section > max {
98 max = end_of_section;
99 }
100 }
101 }
102 }
103 max
104 }
105}
106
107/// An iterator over the loadable sections of a `CoffFile`.
108#[derive(Debug)]
109pub struct CoffSegmentIterator<'data, 'file, R: ReadRef<'data> = &'data [u8]> {
110 pub(super) file: &'file CoffFile<'data, R>,
111 pub(super) iter: slice::Iter<'data, pe::ImageSectionHeader>,
112}
113
114impl<'data, 'file, R: ReadRef<'data>> Iterator for CoffSegmentIterator<'data, 'file, R> {
115 type Item = CoffSegment<'data, 'file, R>;
116
117 fn next(&mut self) -> Option<Self::Item> {
118 self.iter.next().map(|section| CoffSegment {
119 file: self.file,
120 section,
121 })
122 }
123}
124
125/// A loadable section of a `CoffFile`.
126#[derive(Debug)]
127pub struct CoffSegment<'data, 'file, R: ReadRef<'data> = &'data [u8]> {
128 pub(super) file: &'file CoffFile<'data, R>,
129 pub(super) section: &'data pe::ImageSectionHeader,
130}
131
132impl<'data, 'file, R: ReadRef<'data>> CoffSegment<'data, 'file, R> {
133 fn bytes(&self) -> Result<&'data [u8]> {
134 self.section
135 .coff_data(self.file.data)
136 .read_error("Invalid COFF section offset or size")
137 }
138}
139
140impl<'data, 'file, R: ReadRef<'data>> read::private::Sealed for CoffSegment<'data, 'file, R> {}
141
142impl<'data, 'file, R: ReadRef<'data>> ObjectSegment<'data> for CoffSegment<'data, 'file, R> {
143 #[inline]
144 fn address(&self) -> u64 {
145 u64::from(self.section.virtual_address.get(LE))
146 }
147
148 #[inline]
149 fn size(&self) -> u64 {
150 u64::from(self.section.virtual_size.get(LE))
151 }
152
153 #[inline]
154 fn align(&self) -> u64 {
155 self.section.coff_alignment()
156 }
157
158 #[inline]
159 fn file_range(&self) -> (u64, u64) {
160 let (offset, size) = self.section.coff_file_range().unwrap_or((0, 0));
161 (u64::from(offset), u64::from(size))
162 }
163
164 fn data(&self) -> Result<&'data [u8]> {
165 self.bytes()
166 }
167
168 fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> {
169 Ok(read::util::data_range(
170 self.bytes()?,
171 self.address(),
172 address,
173 size,
174 ))
175 }
176
177 #[inline]
178 fn name_bytes(&self) -> Result<Option<&[u8]>> {
179 self.section
180 .name(self.file.common.symbols.strings())
181 .map(Some)
182 }
183
184 #[inline]
185 fn name(&self) -> Result<Option<&str>> {
186 let name = self.section.name(self.file.common.symbols.strings())?;
187 str::from_utf8(name)
188 .ok()
189 .read_error("Non UTF-8 COFF section name")
190 .map(Some)
191 }
192
193 #[inline]
194 fn flags(&self) -> SegmentFlags {
195 let characteristics = self.section.characteristics.get(LE);
196 SegmentFlags::Coff { characteristics }
197 }
198}
199
200/// An iterator over the sections of a `CoffFile`.
201#[derive(Debug)]
202pub struct CoffSectionIterator<'data, 'file, R: ReadRef<'data> = &'data [u8]> {
203 pub(super) file: &'file CoffFile<'data, R>,
204 pub(super) iter: iter::Enumerate<slice::Iter<'data, pe::ImageSectionHeader>>,
205}
206
207impl<'data, 'file, R: ReadRef<'data>> Iterator for CoffSectionIterator<'data, 'file, R> {
208 type Item = CoffSection<'data, 'file, R>;
209
210 fn next(&mut self) -> Option<Self::Item> {
211 self.iter.next().map(|(index, section)| CoffSection {
212 file: self.file,
213 index: SectionIndex(index + 1),
214 section,
215 })
216 }
217}
218
219/// A section of a `CoffFile`.
220#[derive(Debug)]
221pub struct CoffSection<'data, 'file, R: ReadRef<'data> = &'data [u8]> {
222 pub(super) file: &'file CoffFile<'data, R>,
223 pub(super) index: SectionIndex,
224 pub(super) section: &'data pe::ImageSectionHeader,
225}
226
227impl<'data, 'file, R: ReadRef<'data>> CoffSection<'data, 'file, R> {
228 fn bytes(&self) -> Result<&'data [u8]> {
229 self.section
230 .coff_data(self.file.data)
231 .read_error("Invalid COFF section offset or size")
232 }
233}
234
235impl<'data, 'file, R: ReadRef<'data>> read::private::Sealed for CoffSection<'data, 'file, R> {}
236
237impl<'data, 'file, R: ReadRef<'data>> ObjectSection<'data> for CoffSection<'data, 'file, R> {
238 type RelocationIterator = CoffRelocationIterator<'data, 'file, R>;
239
240 #[inline]
241 fn index(&self) -> SectionIndex {
242 self.index
243 }
244
245 #[inline]
246 fn address(&self) -> u64 {
247 u64::from(self.section.virtual_address.get(LE))
248 }
249
250 #[inline]
251 fn size(&self) -> u64 {
252 // TODO: This may need to be the length from the auxiliary symbol for this section.
253 u64::from(self.section.size_of_raw_data.get(LE))
254 }
255
256 #[inline]
257 fn align(&self) -> u64 {
258 self.section.coff_alignment()
259 }
260
261 #[inline]
262 fn file_range(&self) -> Option<(u64, u64)> {
263 let (offset, size) = self.section.coff_file_range()?;
264 Some((u64::from(offset), u64::from(size)))
265 }
266
267 fn data(&self) -> Result<&'data [u8]> {
268 self.bytes()
269 }
270
271 fn data_range(&self, address: u64, size: u64) -> Result<Option<&'data [u8]>> {
272 Ok(read::util::data_range(
273 self.bytes()?,
274 self.address(),
275 address,
276 size,
277 ))
278 }
279
280 #[inline]
281 fn compressed_file_range(&self) -> Result<CompressedFileRange> {
282 Ok(CompressedFileRange::none(self.file_range()))
283 }
284
285 #[inline]
286 fn compressed_data(&self) -> Result<CompressedData<'data>> {
287 self.data().map(CompressedData::none)
288 }
289
290 #[inline]
291 fn name_bytes(&self) -> Result<&[u8]> {
292 self.section.name(self.file.common.symbols.strings())
293 }
294
295 #[inline]
296 fn name(&self) -> Result<&str> {
297 let name = self.name_bytes()?;
298 str::from_utf8(name)
299 .ok()
300 .read_error("Non UTF-8 COFF section name")
301 }
302
303 #[inline]
304 fn segment_name_bytes(&self) -> Result<Option<&[u8]>> {
305 Ok(None)
306 }
307
308 #[inline]
309 fn segment_name(&self) -> Result<Option<&str>> {
310 Ok(None)
311 }
312
313 #[inline]
314 fn kind(&self) -> SectionKind {
315 self.section.kind()
316 }
317
318 fn relocations(&self) -> CoffRelocationIterator<'data, 'file, R> {
319 let relocations = self.section.coff_relocations(self.file.data).unwrap_or(&[]);
320 CoffRelocationIterator {
321 file: self.file,
322 iter: relocations.iter(),
323 }
324 }
325
326 fn flags(&self) -> SectionFlags {
327 SectionFlags::Coff {
328 characteristics: self.section.characteristics.get(LE),
329 }
330 }
331}
332
333impl pe::ImageSectionHeader {
334 pub(crate) fn kind(&self) -> SectionKind {
335 let characteristics = self.characteristics.get(LE);
336 if characteristics & (pe::IMAGE_SCN_CNT_CODE | pe::IMAGE_SCN_MEM_EXECUTE) != 0 {
337 SectionKind::Text
338 } else if characteristics & pe::IMAGE_SCN_CNT_INITIALIZED_DATA != 0 {
339 if characteristics & pe::IMAGE_SCN_MEM_DISCARDABLE != 0 {
340 SectionKind::Other
341 } else if characteristics & pe::IMAGE_SCN_MEM_WRITE != 0 {
342 SectionKind::Data
343 } else {
344 SectionKind::ReadOnlyData
345 }
346 } else if characteristics & pe::IMAGE_SCN_CNT_UNINITIALIZED_DATA != 0 {
347 SectionKind::UninitializedData
348 } else if characteristics & pe::IMAGE_SCN_LNK_INFO != 0 {
349 SectionKind::Linker
350 } else {
351 SectionKind::Unknown
352 }
353 }
354}
355
356impl pe::ImageSectionHeader {
357 /// Return the string table offset of the section name.
358 ///
359 /// Returns `Ok(None)` if the name doesn't use the string table
360 /// and can be obtained with `raw_name` instead.
361 pub fn name_offset(&self) -> Result<Option<u32>> {
362 let bytes = &self.name;
363 if bytes[0] != b'/' {
364 return Ok(None);
365 }
366
367 if bytes[1] == b'/' {
368 let mut offset = 0;
369 for byte in bytes[2..].iter() {
370 let digit = match byte {
371 b'A'..=b'Z' => byte - b'A',
372 b'a'..=b'z' => byte - b'a' + 26,
373 b'0'..=b'9' => byte - b'0' + 52,
374 b'+' => 62,
375 b'/' => 63,
376 _ => return Err(Error("Invalid COFF section name base-64 offset")),
377 };
378 offset = offset * 64 + digit as u64;
379 }
380 u32::try_from(offset)
381 .ok()
382 .read_error("Invalid COFF section name base-64 offset")
383 .map(Some)
384 } else {
385 let mut offset = 0;
386 for byte in bytes[1..].iter() {
387 let digit = match byte {
388 b'0'..=b'9' => byte - b'0',
389 0 => break,
390 _ => return Err(Error("Invalid COFF section name base-10 offset")),
391 };
392 offset = offset * 10 + digit as u32;
393 }
394 Ok(Some(offset))
395 }
396 }
397
398 /// Return the section name.
399 ///
400 /// This handles decoding names that are offsets into the symbol string table.
401 pub fn name<'data, R: ReadRef<'data>>(
402 &'data self,
403 strings: StringTable<'data, R>,
404 ) -> Result<&'data [u8]> {
405 if let Some(offset) = self.name_offset()? {
406 strings
407 .get(offset)
408 .read_error("Invalid COFF section name offset")
409 } else {
410 Ok(self.raw_name())
411 }
412 }
413
414 /// Return the raw section name.
415 pub fn raw_name(&self) -> &[u8] {
416 let bytes = &self.name;
417 match memchr::memchr(b'\0', bytes) {
418 Some(end) => &bytes[..end],
419 None => &bytes[..],
420 }
421 }
422
423 /// Return the offset and size of the section in a COFF file.
424 ///
425 /// Returns `None` for sections that have no data in the file.
426 pub fn coff_file_range(&self) -> Option<(u32, u32)> {
427 if self.characteristics.get(LE) & pe::IMAGE_SCN_CNT_UNINITIALIZED_DATA != 0 {
428 None
429 } else {
430 let offset = self.pointer_to_raw_data.get(LE);
431 // Note: virtual size is not used for COFF.
432 let size = self.size_of_raw_data.get(LE);
433 Some((offset, size))
434 }
435 }
436
437 /// Return the section data in a COFF file.
438 ///
439 /// Returns `Ok(&[])` if the section has no data.
440 /// Returns `Err` for invalid values.
441 pub fn coff_data<'data, R: ReadRef<'data>>(&self, data: R) -> result::Result<&'data [u8], ()> {
442 if let Some((offset, size)) = self.coff_file_range() {
443 data.read_bytes_at(offset.into(), size.into())
444 } else {
445 Ok(&[])
446 }
447 }
448
449 /// Return the section alignment in bytes.
450 ///
451 /// This is only valid for sections in a COFF file.
452 pub fn coff_alignment(&self) -> u64 {
453 match self.characteristics.get(LE) & pe::IMAGE_SCN_ALIGN_MASK {
454 pe::IMAGE_SCN_ALIGN_1BYTES => 1,
455 pe::IMAGE_SCN_ALIGN_2BYTES => 2,
456 pe::IMAGE_SCN_ALIGN_4BYTES => 4,
457 pe::IMAGE_SCN_ALIGN_8BYTES => 8,
458 pe::IMAGE_SCN_ALIGN_16BYTES => 16,
459 pe::IMAGE_SCN_ALIGN_32BYTES => 32,
460 pe::IMAGE_SCN_ALIGN_64BYTES => 64,
461 pe::IMAGE_SCN_ALIGN_128BYTES => 128,
462 pe::IMAGE_SCN_ALIGN_256BYTES => 256,
463 pe::IMAGE_SCN_ALIGN_512BYTES => 512,
464 pe::IMAGE_SCN_ALIGN_1024BYTES => 1024,
465 pe::IMAGE_SCN_ALIGN_2048BYTES => 2048,
466 pe::IMAGE_SCN_ALIGN_4096BYTES => 4096,
467 pe::IMAGE_SCN_ALIGN_8192BYTES => 8192,
468 _ => 16,
469 }
470 }
471
472 /// Read the relocations in a COFF file.
473 ///
474 /// `data` must be the entire file data.
475 pub fn coff_relocations<'data, R: ReadRef<'data>>(
476 &self,
477 data: R,
478 ) -> read::Result<&'data [pe::ImageRelocation]> {
479 let mut pointer = self.pointer_to_relocations.get(LE).into();
480 let mut number: usize = self.number_of_relocations.get(LE).into();
481 if number == core::u16::MAX.into()
482 && self.characteristics.get(LE) & pe::IMAGE_SCN_LNK_NRELOC_OVFL != 0
483 {
484 // Extended relocations. Read first relocation (which contains extended count) & adjust
485 // relocations pointer.
486 let extended_relocation_info = data
487 .read_at::<pe::ImageRelocation>(pointer)
488 .read_error("Invalid COFF relocation offset or number")?;
489 number = extended_relocation_info.virtual_address.get(LE) as usize;
490 if number == 0 {
491 return Err(Error("Invalid COFF relocation number"));
492 }
493 pointer += core::mem::size_of::<pe::ImageRelocation>() as u64;
494 // Extended relocation info does not contribute to the count of sections.
495 number -= 1;
496 }
497 data.read_slice_at(pointer, number)
498 .read_error("Invalid COFF relocation offset or number")
499 }
500}
501
502#[cfg(test)]
503mod tests {
504 use super::*;
505
506 #[test]
507 fn name_offset() {
508 let mut section = pe::ImageSectionHeader::default();
509 section.name = *b"xxxxxxxx";
510 assert_eq!(section.name_offset(), Ok(None));
511 section.name = *b"/0\0\0\0\0\0\0";
512 assert_eq!(section.name_offset(), Ok(Some(0)));
513 section.name = *b"/9999999";
514 assert_eq!(section.name_offset(), Ok(Some(999_9999)));
515 section.name = *b"//AAAAAA";
516 assert_eq!(section.name_offset(), Ok(Some(0)));
517 section.name = *b"//D/////";
518 assert_eq!(section.name_offset(), Ok(Some(0xffff_ffff)));
519 section.name = *b"//EAAAAA";
520 assert!(section.name_offset().is_err());
521 section.name = *b"////////";
522 assert!(section.name_offset().is_err());
523 }
524}