1 use core
::{iter, result, slice, str}
;
3 use crate::endian
::LittleEndian
as LE
;
5 use crate::read
::util
::StringTable
;
7 self, CompressedData
, CompressedFileRange
, Error
, ObjectSection
, ObjectSegment
, ReadError
,
8 ReadRef
, Result
, SectionFlags
, SectionIndex
, SectionKind
,
11 use super::{CoffFile, CoffRelocationIterator}
;
13 /// The table of section headers in a COFF or PE file.
14 #[derive(Debug, Default, Clone, Copy)]
15 pub struct SectionTable
<'data
> {
16 sections
: &'data
[pe
::ImageSectionHeader
],
19 impl<'data
> SectionTable
<'data
> {
20 /// Parse the section table.
22 /// `data` must be the entire file data.
23 /// `offset` must be after the optional file header.
24 pub fn parse
<R
: ReadRef
<'data
>>(
25 header
: &pe
::ImageFileHeader
,
30 .read_slice_at(offset
, header
.number_of_sections
.get(LE
).into())
31 .read_error("Invalid COFF/PE section headers")?
;
32 Ok(SectionTable { sections }
)
35 /// Iterate over the section headers.
37 /// Warning: sections indices start at 1.
39 pub fn iter(&self) -> slice
::Iter
<'data
, pe
::ImageSectionHeader
> {
43 /// Return true if the section table is empty.
45 pub fn is_empty(&self) -> bool
{
46 self.sections
.is_empty()
49 /// The number of section headers.
51 pub fn len(&self) -> usize {
55 /// Return the section header at the given index.
57 /// The index is 1-based.
58 pub fn section(&self, index
: usize) -> read
::Result
<&'data pe
::ImageSectionHeader
> {
60 .get(index
.wrapping_sub(1))
61 .read_error("Invalid COFF/PE section index")
64 /// Return the section header with the given name.
66 /// The returned index is 1-based.
68 /// Ignores sections with invalid names.
69 pub fn section_by_name(
71 strings
: StringTable
<'data
>,
73 ) -> Option
<(usize, &'data pe
::ImageSectionHeader
)> {
77 .find(|(_
, section
)| section
.name(strings
) == Ok(name
))
78 .map(|(index
, section
)| (index
+ 1, section
))
82 /// An iterator over the loadable sections of a `CoffFile`.
84 pub struct CoffSegmentIterator
<'data
, 'file
, R
: ReadRef
<'data
> = &'data
[u8]> {
85 pub(super) file
: &'file CoffFile
<'data
, R
>,
86 pub(super) iter
: slice
::Iter
<'data
, pe
::ImageSectionHeader
>,
89 impl<'data
, 'file
, R
: ReadRef
<'data
>> Iterator
for CoffSegmentIterator
<'data
, 'file
, R
> {
90 type Item
= CoffSegment
<'data
, 'file
, R
>;
92 fn next(&mut self) -> Option
<Self::Item
> {
93 self.iter
.next().map(|section
| CoffSegment
{
100 /// A loadable section of a `CoffFile`.
102 pub struct CoffSegment
<'data
, 'file
, R
: ReadRef
<'data
> = &'data
[u8]> {
103 pub(super) file
: &'file CoffFile
<'data
, R
>,
104 pub(super) section
: &'data pe
::ImageSectionHeader
,
107 impl<'data
, 'file
, R
: ReadRef
<'data
>> CoffSegment
<'data
, 'file
, R
> {
108 fn bytes(&self) -> Result
<&'data
[u8]> {
110 .coff_data(self.file
.data
)
111 .read_error("Invalid COFF section offset or size")
115 impl<'data
, 'file
, R
: ReadRef
<'data
>> read
::private
::Sealed
for CoffSegment
<'data
, 'file
, R
> {}
117 impl<'data
, 'file
, R
: ReadRef
<'data
>> ObjectSegment
<'data
> for CoffSegment
<'data
, 'file
, R
> {
119 fn address(&self) -> u64 {
120 u64::from(self.section
.virtual_address
.get(LE
))
124 fn size(&self) -> u64 {
125 u64::from(self.section
.virtual_size
.get(LE
))
129 fn align(&self) -> u64 {
130 self.section
.coff_alignment()
134 fn file_range(&self) -> (u64, u64) {
135 let (offset
, size
) = self.section
.coff_file_range().unwrap_or((0, 0));
136 (u64::from(offset
), u64::from(size
))
139 fn data(&self) -> Result
<&'data
[u8]> {
143 fn data_range(&self, address
: u64, size
: u64) -> Result
<Option
<&'data
[u8]>> {
144 Ok(read
::util
::data_range(
153 fn name(&self) -> Result
<Option
<&str>> {
154 let name
= self.section
.name(self.file
.common
.symbols
.strings())?
;
158 .read_error("Non UTF-8 COFF section name")?
,
163 /// An iterator over the sections of a `CoffFile`.
165 pub struct CoffSectionIterator
<'data
, 'file
, R
: ReadRef
<'data
> = &'data
[u8]> {
166 pub(super) file
: &'file CoffFile
<'data
, R
>,
167 pub(super) iter
: iter
::Enumerate
<slice
::Iter
<'data
, pe
::ImageSectionHeader
>>,
170 impl<'data
, 'file
, R
: ReadRef
<'data
>> Iterator
for CoffSectionIterator
<'data
, 'file
, R
> {
171 type Item
= CoffSection
<'data
, 'file
, R
>;
173 fn next(&mut self) -> Option
<Self::Item
> {
174 self.iter
.next().map(|(index
, section
)| CoffSection
{
176 index
: SectionIndex(index
+ 1),
182 /// A section of a `CoffFile`.
184 pub struct CoffSection
<'data
, 'file
, R
: ReadRef
<'data
> = &'data
[u8]> {
185 pub(super) file
: &'file CoffFile
<'data
, R
>,
186 pub(super) index
: SectionIndex
,
187 pub(super) section
: &'data pe
::ImageSectionHeader
,
190 impl<'data
, 'file
, R
: ReadRef
<'data
>> CoffSection
<'data
, 'file
, R
> {
191 fn bytes(&self) -> Result
<&'data
[u8]> {
193 .coff_data(self.file
.data
)
194 .read_error("Invalid COFF section offset or size")
198 impl<'data
, 'file
, R
: ReadRef
<'data
>> read
::private
::Sealed
for CoffSection
<'data
, 'file
, R
> {}
200 impl<'data
, 'file
, R
: ReadRef
<'data
>> ObjectSection
<'data
> for CoffSection
<'data
, 'file
, R
> {
201 type RelocationIterator
= CoffRelocationIterator
<'data
, 'file
, R
>;
204 fn index(&self) -> SectionIndex
{
209 fn address(&self) -> u64 {
210 u64::from(self.section
.virtual_address
.get(LE
))
214 fn size(&self) -> u64 {
215 // TODO: This may need to be the length from the auxiliary symbol for this section.
216 u64::from(self.section
.size_of_raw_data
.get(LE
))
220 fn align(&self) -> u64 {
221 self.section
.coff_alignment()
225 fn file_range(&self) -> Option
<(u64, u64)> {
226 let (offset
, size
) = self.section
.coff_file_range()?
;
227 Some((u64::from(offset
), u64::from(size
)))
230 fn data(&self) -> Result
<&'data
[u8]> {
234 fn data_range(&self, address
: u64, size
: u64) -> Result
<Option
<&'data
[u8]>> {
235 Ok(read
::util
::data_range(
244 fn compressed_file_range(&self) -> Result
<CompressedFileRange
> {
245 Ok(CompressedFileRange
::none(self.file_range()))
249 fn compressed_data(&self) -> Result
<CompressedData
<'data
>> {
250 self.data().map(CompressedData
::none
)
254 fn name(&self) -> Result
<&str> {
255 let name
= self.section
.name(self.file
.common
.symbols
.strings())?
;
258 .read_error("Non UTF-8 COFF section name")
262 fn segment_name(&self) -> Result
<Option
<&str>> {
267 fn kind(&self) -> SectionKind
{
271 fn relocations(&self) -> CoffRelocationIterator
<'data
, 'file
, R
> {
272 let relocations
= self.section
.coff_relocations(self.file
.data
).unwrap_or(&[]);
273 CoffRelocationIterator
{
275 iter
: relocations
.iter(),
279 fn flags(&self) -> SectionFlags
{
281 characteristics
: self.section
.characteristics
.get(LE
),
286 impl pe
::ImageSectionHeader
{
287 pub(crate) fn kind(&self) -> SectionKind
{
288 let characteristics
= self.characteristics
.get(LE
);
289 if characteristics
& (pe
::IMAGE_SCN_CNT_CODE
| pe
::IMAGE_SCN_MEM_EXECUTE
) != 0 {
291 } else if characteristics
& pe
::IMAGE_SCN_CNT_INITIALIZED_DATA
!= 0 {
292 if characteristics
& pe
::IMAGE_SCN_MEM_DISCARDABLE
!= 0 {
294 } else if characteristics
& pe
::IMAGE_SCN_MEM_WRITE
!= 0 {
297 SectionKind
::ReadOnlyData
299 } else if characteristics
& pe
::IMAGE_SCN_CNT_UNINITIALIZED_DATA
!= 0 {
300 SectionKind
::UninitializedData
301 } else if characteristics
& pe
::IMAGE_SCN_LNK_INFO
!= 0 {
309 impl pe
::ImageSectionHeader
{
310 /// Return the section name.
312 /// This handles decoding names that are offsets into the symbol string table.
313 pub fn name
<'data
>(&'data
self, strings
: StringTable
<'data
>) -> Result
<&'data
[u8]> {
314 let bytes
= &self.name
;
315 Ok(if bytes
[0] == b'
/'
{
317 if bytes
[1] == b'
/'
{
318 for byte
in bytes
[2..].iter() {
319 let digit
= match byte
{
320 b'A'
..=b'Z'
=> byte
- b'A'
,
321 b'a'
..=b'z'
=> byte
- b'a'
+ 26,
322 b'
0'
..=b'
9'
=> byte
- b'
0'
+ 52,
325 _
=> return Err(Error("Invalid COFF section name base-64 offset")),
327 offset
= offset
* 64 + digit
as u32;
330 for byte
in bytes
[1..].iter() {
331 let digit
= match byte
{
332 b'
0'
..=b'
9'
=> byte
- b'
0'
,
334 _
=> return Err(Error("Invalid COFF section name base-10 offset")),
336 offset
= offset
* 10 + digit
as u32;
341 .read_error("Invalid COFF section name offset")?
347 /// Return the raw section name.
348 pub fn raw_name(&self) -> &[u8] {
349 let bytes
= &self.name
;
350 match memchr
::memchr(b'
\0'
, bytes
) {
351 Some(end
) => &bytes
[..end
],
356 /// Return the offset and size of the section in a COFF file.
358 /// Returns `None` for sections that have no data in the file.
359 pub fn coff_file_range(&self) -> Option
<(u32, u32)> {
360 if self.characteristics
.get(LE
) & pe
::IMAGE_SCN_CNT_UNINITIALIZED_DATA
!= 0 {
363 let offset
= self.pointer_to_raw_data
.get(LE
);
364 // Note: virtual size is not used for COFF.
365 let size
= self.size_of_raw_data
.get(LE
);
370 /// Return the section data in a COFF file.
372 /// Returns `Ok(&[])` if the section has no data.
373 /// Returns `Err` for invalid values.
374 pub fn coff_data
<'data
, R
: ReadRef
<'data
>>(&self, data
: R
) -> result
::Result
<&'data
[u8], ()> {
375 if let Some((offset
, size
)) = self.coff_file_range() {
376 data
.read_bytes_at(offset
.into(), size
.into())
382 /// Return the section alignment in bytes.
384 /// This is only valid for sections in a COFF file.
385 pub fn coff_alignment(&self) -> u64 {
386 match self.characteristics
.get(LE
) & pe
::IMAGE_SCN_ALIGN_MASK
{
387 pe
::IMAGE_SCN_ALIGN_1BYTES
=> 1,
388 pe
::IMAGE_SCN_ALIGN_2BYTES
=> 2,
389 pe
::IMAGE_SCN_ALIGN_4BYTES
=> 4,
390 pe
::IMAGE_SCN_ALIGN_8BYTES
=> 8,
391 pe
::IMAGE_SCN_ALIGN_16BYTES
=> 16,
392 pe
::IMAGE_SCN_ALIGN_32BYTES
=> 32,
393 pe
::IMAGE_SCN_ALIGN_64BYTES
=> 64,
394 pe
::IMAGE_SCN_ALIGN_128BYTES
=> 128,
395 pe
::IMAGE_SCN_ALIGN_256BYTES
=> 256,
396 pe
::IMAGE_SCN_ALIGN_512BYTES
=> 512,
397 pe
::IMAGE_SCN_ALIGN_1024BYTES
=> 1024,
398 pe
::IMAGE_SCN_ALIGN_2048BYTES
=> 2048,
399 pe
::IMAGE_SCN_ALIGN_4096BYTES
=> 4096,
400 pe
::IMAGE_SCN_ALIGN_8192BYTES
=> 8192,
405 /// Read the relocations in a COFF file.
407 /// `data` must be the entire file data.
408 pub fn coff_relocations
<'data
, R
: ReadRef
<'data
>>(
411 ) -> read
::Result
<&'data
[pe
::ImageRelocation
]> {
412 let pointer
= self.pointer_to_relocations
.get(LE
).into();
413 let number
= self.number_of_relocations
.get(LE
).into();
414 data
.read_slice_at(pointer
, number
)
415 .read_error("Invalid COFF relocation offset or number")