]>
Commit | Line | Data |
---|---|---|
f035d41b XL |
1 | use core::fmt::Debug; |
2 | use core::{iter, mem, slice, str}; | |
3 | ||
4 | use crate::elf; | |
5 | use crate::endian::{self, Endianness, U32Bytes}; | |
6 | use crate::pod::{Bytes, Pod}; | |
7 | use crate::read::{ | |
8 | self, CompressedData, CompressionFormat, Error, ObjectSection, ReadError, SectionFlags, | |
9 | SectionIndex, SectionKind, StringTable, | |
10 | }; | |
11 | ||
12 | use 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)] | |
21 | pub struct SectionTable<'data, Elf: FileHeader> { | |
22 | sections: &'data [Elf::SectionHeader], | |
23 | strings: StringTable<'data>, | |
24 | } | |
25 | ||
26 | impl<'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`. | |
108 | pub type ElfSectionIterator32<'data, 'file, Endian = Endianness> = | |
109 | ElfSectionIterator<'data, 'file, elf::FileHeader32<Endian>>; | |
110 | /// An iterator over the sections of an `ElfFile64`. | |
111 | pub 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)] | |
116 | pub struct ElfSectionIterator<'data, 'file, Elf> | |
117 | where | |
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 | ||
125 | impl<'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`. | |
138 | pub type ElfSection32<'data, 'file, Endian = Endianness> = | |
139 | ElfSection<'data, 'file, elf::FileHeader32<Endian>>; | |
140 | /// A section of an `ElfFile64`. | |
141 | pub type ElfSection64<'data, 'file, Endian = Endianness> = | |
142 | ElfSection<'data, 'file, elf::FileHeader64<Endian>>; | |
143 | ||
144 | /// A section of an `ElfFile`. | |
145 | #[derive(Debug)] | |
146 | pub struct ElfSection<'data, 'file, Elf> | |
147 | where | |
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 | ||
156 | impl<'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 | ||
221 | impl<'data, 'file, Elf: FileHeader> read::private::Sealed for ElfSection<'data, 'file, Elf> {} | |
222 | ||
223 | impl<'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)] | |
352 | pub 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 | ||
425 | impl<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 | ||
481 | impl<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 | } |