]> git.proxmox.com Git - rustc.git/blame - vendor/object-0.20.0/src/read/elf/section.rs
New upstream version 1.49.0+dfsg1
[rustc.git] / vendor / object-0.20.0 / src / read / elf / section.rs
CommitLineData
f035d41b
XL
1use core::fmt::Debug;
2use core::{iter, mem, slice, str};
3
4use crate::elf;
5use crate::endian::{self, Endianness, U32Bytes};
6use crate::pod::{Bytes, Pod};
7use crate::read::{
8 self, CompressedData, CompressionFormat, Error, ObjectSection, ReadError, SectionFlags,
9 SectionIndex, SectionKind, StringTable,
10};
11
12use super::{
13 CompressionHeader, ElfFile, ElfNoteIterator, ElfRelocationIterator, FileHeader,
14 RelocationSections, SymbolTable,
15};
16
17/// The table of section headers in an ELF file.
18///
19/// Also includes the string table used for the section names.
20#[derive(Debug, Default, Clone, Copy)]
21pub struct SectionTable<'data, Elf: FileHeader> {
22 sections: &'data [Elf::SectionHeader],
23 strings: StringTable<'data>,
24}
25
26impl<'data, Elf: FileHeader> SectionTable<'data, Elf> {
27 /// Create a new section table.
28 #[inline]
29 pub fn new(sections: &'data [Elf::SectionHeader], strings: StringTable<'data>) -> Self {
30 SectionTable { sections, strings }
31 }
32
33 /// Iterate over the section headers.
34 #[inline]
35 pub fn iter(&self) -> slice::Iter<'data, Elf::SectionHeader> {
36 self.sections.iter()
37 }
38
39 /// Return true if the section table is empty.
40 #[inline]
41 pub fn is_empty(&self) -> bool {
42 self.sections.is_empty()
43 }
44
45 /// The number of section headers.
46 #[inline]
47 pub fn len(&self) -> usize {
48 self.sections.len()
49 }
50
51 /// Return the section header at the given index.
52 pub fn section(&self, index: usize) -> read::Result<&'data Elf::SectionHeader> {
53 self.sections
54 .get(index)
55 .read_error("Invalid ELF section index")
56 }
57
58 /// Return the section header with the given name.
59 ///
60 /// Ignores sections with invalid names.
61 pub fn section_by_name(
62 &self,
63 endian: Elf::Endian,
64 name: &[u8],
65 ) -> Option<(usize, &'data Elf::SectionHeader)> {
66 self.sections
67 .iter()
68 .enumerate()
69 .find(|(_, section)| self.section_name(endian, section) == Ok(name))
70 }
71
72 /// Return the section name for the given section header.
73 pub fn section_name(
74 &self,
75 endian: Elf::Endian,
76 section: &'data Elf::SectionHeader,
77 ) -> read::Result<&'data [u8]> {
78 self.strings
79 .get(section.sh_name(endian))
80 .read_error("Invalid ELF section name offset")
81 }
82
83 /// Return the symbol table of the given section type.
84 ///
85 /// Returns an empty symbol table if the symbol table does not exist.
86 #[inline]
87 pub fn symbols(
88 &self,
89 endian: Elf::Endian,
90 data: Bytes<'data>,
91 sh_type: u32,
92 ) -> read::Result<SymbolTable<'data, Elf>> {
93 SymbolTable::parse(endian, data, self, sh_type)
94 }
95
96 /// Create a mapping from section index to associated relocation sections.
97 #[inline]
98 pub fn relocation_sections(
99 &self,
100 endian: Elf::Endian,
101 symbol_section: usize,
102 ) -> read::Result<RelocationSections> {
103 RelocationSections::parse(endian, self, symbol_section)
104 }
105}
106
107/// An iterator over the sections of an `ElfFile32`.
108pub type ElfSectionIterator32<'data, 'file, Endian = Endianness> =
109 ElfSectionIterator<'data, 'file, elf::FileHeader32<Endian>>;
110/// An iterator over the sections of an `ElfFile64`.
111pub type ElfSectionIterator64<'data, 'file, Endian = Endianness> =
112 ElfSectionIterator<'data, 'file, elf::FileHeader64<Endian>>;
113
114/// An iterator over the sections of an `ElfFile`.
115#[derive(Debug)]
116pub struct ElfSectionIterator<'data, 'file, Elf>
117where
118 'data: 'file,
119 Elf: FileHeader,
120{
121 pub(super) file: &'file ElfFile<'data, Elf>,
122 pub(super) iter: iter::Enumerate<slice::Iter<'data, Elf::SectionHeader>>,
123}
124
125impl<'data, 'file, Elf: FileHeader> Iterator for ElfSectionIterator<'data, 'file, Elf> {
126 type Item = ElfSection<'data, 'file, Elf>;
127
128 fn next(&mut self) -> Option<Self::Item> {
129 self.iter.next().map(|(index, section)| ElfSection {
130 index: SectionIndex(index),
131 file: self.file,
132 section,
133 })
134 }
135}
136
137/// A section of an `ElfFile32`.
138pub type ElfSection32<'data, 'file, Endian = Endianness> =
139 ElfSection<'data, 'file, elf::FileHeader32<Endian>>;
140/// A section of an `ElfFile64`.
141pub type ElfSection64<'data, 'file, Endian = Endianness> =
142 ElfSection<'data, 'file, elf::FileHeader64<Endian>>;
143
144/// A section of an `ElfFile`.
145#[derive(Debug)]
146pub struct ElfSection<'data, 'file, Elf>
147where
148 'data: 'file,
149 Elf: FileHeader,
150{
151 pub(super) file: &'file ElfFile<'data, Elf>,
152 pub(super) index: SectionIndex,
153 pub(super) section: &'data Elf::SectionHeader,
154}
155
156impl<'data, 'file, Elf: FileHeader> ElfSection<'data, 'file, Elf> {
157 fn bytes(&self) -> read::Result<Bytes<'data>> {
158 self.section
159 .data(self.file.endian, self.file.data)
160 .read_error("Invalid ELF section size or offset")
161 }
162
163 fn maybe_compressed_data(&self) -> read::Result<Option<CompressedData<'data>>> {
164 let endian = self.file.endian;
165 if (self.section.sh_flags(endian).into() & u64::from(elf::SHF_COMPRESSED)) == 0 {
166 return Ok(None);
167 }
168
169 let mut data = self
170 .section
171 .data(endian, self.file.data)
172 .read_error("Invalid ELF compressed section offset or size")?;
173 let header = data
174 .read::<Elf::CompressionHeader>()
175 .read_error("Invalid ELF compression header size or alignment")?;
176 if header.ch_type(endian) != elf::ELFCOMPRESS_ZLIB {
177 return Err(Error("Unsupported ELF compression type"));
178 }
179 let uncompressed_size: u64 = header.ch_size(endian).into();
180 Ok(Some(CompressedData {
181 format: CompressionFormat::Zlib,
182 data: data.0,
183 uncompressed_size: uncompressed_size as usize,
184 }))
185 }
186
187 /// Try GNU-style "ZLIB" header decompression.
188 fn maybe_compressed_data_gnu(&self) -> read::Result<Option<CompressedData<'data>>> {
189 let name = match self.name() {
190 Ok(name) => name,
191 // I think it's ok to ignore this error?
192 Err(_) => return Ok(None),
193 };
194 if !name.starts_with(".zdebug_") {
195 return Ok(None);
196 }
197 let mut data = self.bytes()?;
198 // Assume ZLIB-style uncompressed data is no more than 4GB to avoid accidentally
199 // huge allocations. This also reduces the chance of accidentally matching on a
200 // .debug_str that happens to start with "ZLIB".
201 if data
202 .read_bytes(8)
203 .read_error("ELF GNU compressed section is too short")?
204 .0
205 != b"ZLIB\0\0\0\0"
206 {
207 return Err(Error("Invalid ELF GNU compressed section header"));
208 }
209 let uncompressed_size = data
210 .read::<U32Bytes<_>>()
211 .read_error("ELF GNU compressed section is too short")?
212 .get(endian::BigEndian);
213 Ok(Some(CompressedData {
214 format: CompressionFormat::Zlib,
215 data: data.0,
216 uncompressed_size: uncompressed_size as usize,
217 }))
218 }
219}
220
221impl<'data, 'file, Elf: FileHeader> read::private::Sealed for ElfSection<'data, 'file, Elf> {}
222
223impl<'data, 'file, Elf: FileHeader> ObjectSection<'data> for ElfSection<'data, 'file, Elf> {
224 type RelocationIterator = ElfRelocationIterator<'data, 'file, Elf>;
225
226 #[inline]
227 fn index(&self) -> SectionIndex {
228 self.index
229 }
230
231 #[inline]
232 fn address(&self) -> u64 {
233 self.section.sh_addr(self.file.endian).into()
234 }
235
236 #[inline]
237 fn size(&self) -> u64 {
238 self.section.sh_size(self.file.endian).into()
239 }
240
241 #[inline]
242 fn align(&self) -> u64 {
243 self.section.sh_addralign(self.file.endian).into()
244 }
245
246 #[inline]
247 fn file_range(&self) -> Option<(u64, u64)> {
248 self.section.file_range(self.file.endian)
249 }
250
251 #[inline]
252 fn data(&self) -> read::Result<&'data [u8]> {
253 Ok(self.bytes()?.0)
254 }
255
256 fn data_range(&self, address: u64, size: u64) -> read::Result<Option<&'data [u8]>> {
257 Ok(read::data_range(
258 self.bytes()?,
259 self.address(),
260 address,
261 size,
262 ))
263 }
264
265 fn compressed_data(&self) -> read::Result<CompressedData<'data>> {
266 Ok(if let Some(data) = self.maybe_compressed_data()? {
267 data
268 } else if let Some(data) = self.maybe_compressed_data_gnu()? {
269 data
270 } else {
271 CompressedData::none(self.data()?)
272 })
273 }
274
275 fn name(&self) -> read::Result<&str> {
276 let name = self
277 .file
278 .sections
279 .section_name(self.file.endian, self.section)?;
280 str::from_utf8(name)
281 .ok()
282 .read_error("Non UTF-8 ELF section name")
283 }
284
285 #[inline]
286 fn segment_name(&self) -> read::Result<Option<&str>> {
287 Ok(None)
288 }
289
290 fn kind(&self) -> SectionKind {
291 let flags = self.section.sh_flags(self.file.endian).into();
292 match self.section.sh_type(self.file.endian) {
293 elf::SHT_PROGBITS => {
294 if flags & u64::from(elf::SHF_ALLOC) != 0 {
295 if flags & u64::from(elf::SHF_EXECINSTR) != 0 {
296 SectionKind::Text
297 } else if flags & u64::from(elf::SHF_TLS) != 0 {
298 SectionKind::Tls
299 } else if flags & u64::from(elf::SHF_WRITE) != 0 {
300 SectionKind::Data
301 } else if flags & u64::from(elf::SHF_STRINGS) != 0 {
302 SectionKind::ReadOnlyString
303 } else {
304 SectionKind::ReadOnlyData
305 }
306 } else if flags & u64::from(elf::SHF_STRINGS) != 0 {
307 SectionKind::OtherString
308 } else {
309 SectionKind::Other
310 }
311 }
312 elf::SHT_NOBITS => {
313 if flags & u64::from(elf::SHF_TLS) != 0 {
314 SectionKind::UninitializedTls
315 } else {
316 SectionKind::UninitializedData
317 }
318 }
319 elf::SHT_NOTE => SectionKind::Note,
320 elf::SHT_NULL
321 | elf::SHT_SYMTAB
322 | elf::SHT_STRTAB
323 | elf::SHT_RELA
324 | elf::SHT_HASH
325 | elf::SHT_DYNAMIC
326 | elf::SHT_REL
327 | elf::SHT_DYNSYM => SectionKind::Metadata,
328 _ => {
329 // TODO: maybe add more specialised kinds based on sh_type (e.g. Unwind)
330 SectionKind::Unknown
331 }
332 }
333 }
334
335 fn relocations(&self) -> ElfRelocationIterator<'data, 'file, Elf> {
336 ElfRelocationIterator {
337 section_index: self.index.0,
338 file: self.file,
339 relocations: None,
340 }
341 }
342
343 fn flags(&self) -> SectionFlags {
344 SectionFlags::Elf {
345 sh_flags: self.section.sh_flags(self.file.endian).into(),
346 }
347 }
348}
349
350/// A trait for generic access to `SectionHeader32` and `SectionHeader64`.
351#[allow(missing_docs)]
352pub trait SectionHeader: Debug + Pod {
353 type Word: Into<u64>;
354 type Endian: endian::Endian;
355 type Elf: FileHeader<Word = Self::Word, Endian = Self::Endian>;
356
357 fn sh_name(&self, endian: Self::Endian) -> u32;
358 fn sh_type(&self, endian: Self::Endian) -> u32;
359 fn sh_flags(&self, endian: Self::Endian) -> Self::Word;
360 fn sh_addr(&self, endian: Self::Endian) -> Self::Word;
361 fn sh_offset(&self, endian: Self::Endian) -> Self::Word;
362 fn sh_size(&self, endian: Self::Endian) -> Self::Word;
363 fn sh_link(&self, endian: Self::Endian) -> u32;
364 fn sh_info(&self, endian: Self::Endian) -> u32;
365 fn sh_addralign(&self, endian: Self::Endian) -> Self::Word;
366 fn sh_entsize(&self, endian: Self::Endian) -> Self::Word;
367
368 /// Return the offset and size of the section in the file.
369 ///
370 /// Returns `None` for sections that have no data in the file.
371 fn file_range(&self, endian: Self::Endian) -> Option<(u64, u64)> {
372 if self.sh_type(endian) == elf::SHT_NOBITS {
373 None
374 } else {
375 Some((self.sh_offset(endian).into(), self.sh_size(endian).into()))
376 }
377 }
378
379 /// Return the section data.
380 ///
381 /// Returns `Ok(&[])` if the section has no data.
382 /// Returns `Err` for invalid values.
383 fn data<'data>(&self, endian: Self::Endian, data: Bytes<'data>) -> Result<Bytes<'data>, ()> {
384 if let Some((offset, size)) = self.file_range(endian) {
385 data.read_bytes_at(offset as usize, size as usize)
386 } else {
387 Ok(Bytes(&[]))
388 }
389 }
390
391 /// Return the section data as a slice of the given type.
392 ///
393 /// Allows padding at the end of the data.
394 /// Returns `Ok(&[])` if the section has no data.
395 /// Returns `Err` for invalid values, including bad alignment.
396 fn data_as_array<'data, T: Pod>(
397 &self,
398 endian: Self::Endian,
399 data: Bytes<'data>,
400 ) -> Result<&'data [T], ()> {
401 let mut data = self.data(endian, data)?;
402 data.read_slice(data.len() / mem::size_of::<T>())
403 }
404
405 /// Return a note iterator for the section data.
406 ///
407 /// Returns `Ok(None)` if the section does not contain notes.
408 /// Returns `Err` for invalid values.
409 fn notes<'data>(
410 &self,
411 endian: Self::Endian,
412 data: Bytes<'data>,
413 ) -> read::Result<Option<ElfNoteIterator<'data, Self::Elf>>> {
414 if self.sh_type(endian) != elf::SHT_NOTE {
415 return Ok(None);
416 }
417 let data = self
418 .data(endian, data)
419 .read_error("Invalid ELF note section offset or size")?;
420 let notes = ElfNoteIterator::new(endian, self.sh_addralign(endian), data)?;
421 Ok(Some(notes))
422 }
423}
424
425impl<Endian: endian::Endian> SectionHeader for elf::SectionHeader32<Endian> {
426 type Word = u32;
427 type Endian = Endian;
428 type Elf = elf::FileHeader32<Endian>;
429
430 #[inline]
431 fn sh_name(&self, endian: Self::Endian) -> u32 {
432 self.sh_name.get(endian)
433 }
434
435 #[inline]
436 fn sh_type(&self, endian: Self::Endian) -> u32 {
437 self.sh_type.get(endian)
438 }
439
440 #[inline]
441 fn sh_flags(&self, endian: Self::Endian) -> Self::Word {
442 self.sh_flags.get(endian)
443 }
444
445 #[inline]
446 fn sh_addr(&self, endian: Self::Endian) -> Self::Word {
447 self.sh_addr.get(endian)
448 }
449
450 #[inline]
451 fn sh_offset(&self, endian: Self::Endian) -> Self::Word {
452 self.sh_offset.get(endian)
453 }
454
455 #[inline]
456 fn sh_size(&self, endian: Self::Endian) -> Self::Word {
457 self.sh_size.get(endian)
458 }
459
460 #[inline]
461 fn sh_link(&self, endian: Self::Endian) -> u32 {
462 self.sh_link.get(endian)
463 }
464
465 #[inline]
466 fn sh_info(&self, endian: Self::Endian) -> u32 {
467 self.sh_info.get(endian)
468 }
469
470 #[inline]
471 fn sh_addralign(&self, endian: Self::Endian) -> Self::Word {
472 self.sh_addralign.get(endian)
473 }
474
475 #[inline]
476 fn sh_entsize(&self, endian: Self::Endian) -> Self::Word {
477 self.sh_entsize.get(endian)
478 }
479}
480
481impl<Endian: endian::Endian> SectionHeader for elf::SectionHeader64<Endian> {
482 type Word = u64;
483 type Endian = Endian;
484 type Elf = elf::FileHeader64<Endian>;
485
486 #[inline]
487 fn sh_name(&self, endian: Self::Endian) -> u32 {
488 self.sh_name.get(endian)
489 }
490
491 #[inline]
492 fn sh_type(&self, endian: Self::Endian) -> u32 {
493 self.sh_type.get(endian)
494 }
495
496 #[inline]
497 fn sh_flags(&self, endian: Self::Endian) -> Self::Word {
498 self.sh_flags.get(endian)
499 }
500
501 #[inline]
502 fn sh_addr(&self, endian: Self::Endian) -> Self::Word {
503 self.sh_addr.get(endian)
504 }
505
506 #[inline]
507 fn sh_offset(&self, endian: Self::Endian) -> Self::Word {
508 self.sh_offset.get(endian)
509 }
510
511 #[inline]
512 fn sh_size(&self, endian: Self::Endian) -> Self::Word {
513 self.sh_size.get(endian)
514 }
515
516 #[inline]
517 fn sh_link(&self, endian: Self::Endian) -> u32 {
518 self.sh_link.get(endian)
519 }
520
521 #[inline]
522 fn sh_info(&self, endian: Self::Endian) -> u32 {
523 self.sh_info.get(endian)
524 }
525
526 #[inline]
527 fn sh_addralign(&self, endian: Self::Endian) -> Self::Word {
528 self.sh_addralign.get(endian)
529 }
530
531 #[inline]
532 fn sh_entsize(&self, endian: Self::Endian) -> Self::Word {
533 self.sh_entsize.get(endian)
534 }
535}