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