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