Thanks to visit codestin.com
Credit goes to docs.rs

elf/
elf_stream.rs

1use core::ops::Range;
2use std::collections::HashMap;
3use std::io::{Read, Seek, SeekFrom};
4
5use crate::abi;
6use crate::compression::CompressionHeader;
7use crate::dynamic::DynamicTable;
8use crate::endian::EndianParse;
9use crate::file::{parse_ident, Class};
10use crate::gnu_symver::{
11    SymbolVersionTable, VerDefIterator, VerNeedIterator, VersionIndex, VersionIndexTable,
12};
13use crate::note::NoteIterator;
14use crate::parse::{ParseAt, ParseError};
15use crate::relocation::{RelIterator, RelaIterator};
16use crate::section::{SectionHeader, SectionHeaderTable};
17use crate::segment::ProgramHeader;
18use crate::segment::SegmentTable;
19use crate::string_table::StringTable;
20use crate::symbol::{Symbol, SymbolTable};
21
22use crate::file::FileHeader;
23
24/// This type encapsulates the stream-oriented interface for parsing ELF objects from
25/// a `Read + Seek`.
26#[derive(Debug)]
27pub struct ElfStream<E: EndianParse, S: std::io::Read + std::io::Seek> {
28    pub ehdr: FileHeader<E>,
29    shdrs: Vec<SectionHeader>,
30    phdrs: Vec<ProgramHeader>,
31    reader: CachingReader<S>,
32}
33
34/// Read the stream bytes backing the section headers table and parse them all into their Rust native type.
35///
36/// Returns a [ParseError] if the data bytes for the section table cannot be read.
37/// i.e. if the ELF [FileHeader]'s e_shnum, e_shoff, e_shentsize are invalid and point
38/// to a range in the file data that does not actually exist, or if any of the headers failed to parse.
39fn parse_section_headers<E: EndianParse, S: Read + Seek>(
40    ehdr: &FileHeader<E>,
41    reader: &mut CachingReader<S>,
42) -> Result<Vec<SectionHeader>, ParseError> {
43    // It's Ok to have no section headers
44    if ehdr.e_shoff == 0 {
45        return Ok(Vec::default());
46    }
47
48    // Validate shentsize before trying to read the table so that we can error early for corrupted files
49    let entsize = SectionHeader::validate_entsize(ehdr.class, ehdr.e_shentsize as usize)?;
50
51    // If the number of sections is greater than or equal to SHN_LORESERVE (0xff00),
52    // e_shnum is zero and the actual number of section header table entries
53    // is contained in the sh_size field of the section header at index 0.
54    let shoff: usize = ehdr.e_shoff.try_into()?;
55    let mut shnum = ehdr.e_shnum as usize;
56    if shnum == 0 {
57        let end = shoff
58            .checked_add(entsize)
59            .ok_or(ParseError::IntegerOverflow)?;
60        let mut offset = 0;
61        let data = reader.read_bytes(shoff, end)?;
62        let shdr0 = SectionHeader::parse_at(ehdr.endianness, ehdr.class, &mut offset, data)?;
63        shnum = shdr0.sh_size.try_into()?;
64    }
65
66    let size = entsize
67        .checked_mul(shnum)
68        .ok_or(ParseError::IntegerOverflow)?;
69    let end = shoff.checked_add(size).ok_or(ParseError::IntegerOverflow)?;
70    let buf = reader.read_bytes(shoff, end)?;
71    let shdr_vec = SectionHeaderTable::new(ehdr.endianness, ehdr.class, buf)
72        .iter()
73        .collect();
74    Ok(shdr_vec)
75}
76
77fn parse_program_headers<E: EndianParse, S: Read + Seek>(
78    ehdr: &FileHeader<E>,
79    reader: &mut CachingReader<S>,
80) -> Result<Vec<ProgramHeader>, ParseError> {
81    // It's Ok to have no program headers
82    if ehdr.e_phoff == 0 {
83        return Ok(Vec::default());
84    }
85
86    // If the number of segments is greater than or equal to PN_XNUM (0xffff),
87    // e_phnum is set to PN_XNUM, and the actual number of program header table
88    // entries is contained in the sh_info field of the section header at index 0.
89    let mut phnum = ehdr.e_phnum as usize;
90    if phnum == abi::PN_XNUM as usize {
91        let shoff: usize = ehdr.e_shoff.try_into()?;
92        let end = shoff
93            .checked_add(SectionHeader::size_for(ehdr.class))
94            .ok_or(ParseError::IntegerOverflow)?;
95        let data = reader.read_bytes(shoff, end)?;
96        let mut offset = 0;
97        let shdr0 = SectionHeader::parse_at(ehdr.endianness, ehdr.class, &mut offset, data)?;
98        phnum = shdr0.sh_info.try_into()?;
99    }
100
101    // Validate phentsize before trying to read the table so that we can error early for corrupted files
102    let entsize = ProgramHeader::validate_entsize(ehdr.class, ehdr.e_phentsize as usize)?;
103
104    let phoff: usize = ehdr.e_phoff.try_into()?;
105    let size = entsize
106        .checked_mul(phnum)
107        .ok_or(ParseError::IntegerOverflow)?;
108    let end = phoff.checked_add(size).ok_or(ParseError::IntegerOverflow)?;
109    let buf = reader.read_bytes(phoff, end)?;
110    let phdrs_vec = SegmentTable::new(ehdr.endianness, ehdr.class, buf)
111        .iter()
112        .collect();
113    Ok(phdrs_vec)
114}
115
116impl<E: EndianParse, S: std::io::Read + std::io::Seek> ElfStream<E, S> {
117    /// Do a minimal amount of parsing work to open an [ElfStream] handle from a Read+Seek containing an ELF object.
118    ///
119    /// This parses the ELF [FileHeader], [SectionHeader] table, and [ProgramHeader] (segments) table.
120    /// All other file data (section data, segment data) is left unread and unparsed.
121    pub fn open_stream(reader: S) -> Result<ElfStream<E, S>, ParseError> {
122        let mut cr = CachingReader::new(reader)?;
123        let ident_buf = cr.read_bytes(0, abi::EI_NIDENT)?;
124        let ident = parse_ident(ident_buf)?;
125
126        let tail_start = abi::EI_NIDENT;
127        let tail_end = match ident.1 {
128            Class::ELF32 => tail_start + crate::file::ELF32_EHDR_TAILSIZE,
129            Class::ELF64 => tail_start + crate::file::ELF64_EHDR_TAILSIZE,
130        };
131        let tail_buf = cr.read_bytes(tail_start, tail_end)?;
132
133        let ehdr = FileHeader::parse_tail(ident, tail_buf)?;
134
135        let shdrs = parse_section_headers(&ehdr, &mut cr)?;
136        let phdrs = parse_program_headers(&ehdr, &mut cr)?;
137
138        // We parsed out the ehdr and shdrs into their own allocated containers, so there's no need to keep
139        // around their backing data anymore.
140        cr.clear_cache();
141
142        Ok(ElfStream {
143            ehdr,
144            shdrs,
145            phdrs,
146            reader: cr,
147        })
148    }
149
150    /// Get the parsed section headers table
151    pub fn segments(&self) -> &Vec<ProgramHeader> {
152        &self.phdrs
153    }
154
155    /// Get the parsed section headers table
156    pub fn section_headers(&self) -> &Vec<SectionHeader> {
157        &self.shdrs
158    }
159
160    /// Get an lazy-parsing table for the Section Headers in the file and its associated StringTable.
161    ///
162    /// The underlying ELF bytes backing the section headers table and string
163    /// table are read all at once when the table is requested, but parsing is
164    /// deferred to be lazily parsed on demand on each table.get(), strtab.get(), or
165    /// table.iter().next() call.
166    ///
167    /// Returns a [ParseError] if the data bytes for these tables cannot be
168    /// read i.e. if the ELF [FileHeader]'s
169    /// [e_shnum](FileHeader#structfield.e_shnum),
170    /// [e_shoff](FileHeader#structfield.e_shoff),
171    /// [e_shentsize](FileHeader#structfield.e_shentsize),
172    /// [e_shstrndx](FileHeader#structfield.e_shstrndx) are invalid and point
173    /// to a ranges in the file data that does not actually exist.
174    pub fn section_headers_with_strtab(
175        &mut self,
176    ) -> Result<(&Vec<SectionHeader>, Option<StringTable<'_>>), ParseError> {
177        // It's Ok to have no section headers
178        if self.shdrs.is_empty() {
179            return Ok((&self.shdrs, None));
180        }
181
182        // It's Ok to not have a string table
183        if self.ehdr.e_shstrndx == abi::SHN_UNDEF {
184            return Ok((&self.shdrs, None));
185        }
186
187        // If the section name string table section index is greater than or
188        // equal to SHN_LORESERVE (0xff00), e_shstrndx has the value SHN_XINDEX
189        // (0xffff) and the actual index of the section name string table section
190        // is contained in the sh_link field of the section header at index 0.
191        let mut shstrndx = self.ehdr.e_shstrndx as usize;
192        if self.ehdr.e_shstrndx == abi::SHN_XINDEX {
193            shstrndx = self.shdrs[0].sh_link as usize;
194        }
195
196        // We have a strtab, so wrap it in a zero-copy StringTable
197        let strtab = self
198            .shdrs
199            .get(shstrndx)
200            .ok_or(ParseError::BadOffset(shstrndx as u64))?;
201        let (strtab_start, strtab_end) = strtab.get_data_range()?;
202        let strtab_buf = self.reader.read_bytes(strtab_start, strtab_end)?;
203        let strtab = StringTable::new(strtab_buf);
204        Ok((&self.shdrs, Some(strtab)))
205    }
206
207    /// Find the parsed section header with the given name (if any).
208    ///
209    /// Returns a ParseError if the section headers string table can't be read
210    ///
211    /// Example to get the ELF file's ABI-tag note
212    /// ```
213    /// use elf::ElfStream;
214    /// use elf::endian::AnyEndian;
215    /// use elf::section::SectionHeader;
216    /// use elf::note::Note;
217    /// use elf::note::NoteGnuAbiTag;
218    ///
219    /// let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
220    /// let io = std::fs::File::open(path).expect("Could not open file.");
221    /// let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
222    ///
223    /// let shdr: SectionHeader = *file
224    ///     .section_header_by_name(".note.ABI-tag")
225    ///     .expect("section table should be parseable")
226    ///     .expect("file should have a .note.ABI-tag section");
227    ///
228    /// let notes: Vec<_> = file
229    ///     .section_data_as_notes(&shdr)
230    ///     .expect("Should be able to get note section data")
231    ///     .collect();
232    /// assert_eq!(
233    ///     notes[0],
234    ///     Note::GnuAbiTag(NoteGnuAbiTag {
235    ///         os: 0,
236    ///         major: 2,
237    ///         minor: 6,
238    ///         subminor: 32
239    ///     }));
240    /// ```
241    pub fn section_header_by_name(
242        &mut self,
243        name: &str,
244    ) -> Result<Option<&SectionHeader>, ParseError> {
245        let (shdrs, strtab) = match self.section_headers_with_strtab()? {
246            (shdr, Some(strtab)) => (shdr, strtab),
247            // We can't look up shdrs by name if there's no strtab.
248            // (hint: try looking it up by its sh_type).
249            _ => {
250                return Ok(None);
251            }
252        };
253
254        Ok(shdrs.iter().find(|shdr| {
255            let sh_name = match strtab.get(shdr.sh_name as usize) {
256                Ok(name) => name,
257                _ => {
258                    return false;
259                }
260            };
261            name == sh_name
262        }))
263    }
264
265    /// Read the section data for the given [SectionHeader](SectionHeader).
266    /// Returns both the secion data and an optional CompressionHeader.
267    ///
268    /// No compression header signals that the section contents are uncompressed and can be used as-is.
269    ///
270    /// Some(chdr) signals that the section contents are compressed and need to be uncompressed via the
271    /// compression algorithm described in [ch_type](CompressionHeader#structfield.ch_type).
272    /// The returned buffer represents the compressed section bytes as found in the file, without the
273    /// CompressionHeader.
274    ///
275    /// It is up to the user to perform the decompression themselves with the compression library of
276    /// their choosing.
277    ///
278    /// SHT_NOBITS sections yield an empty slice.
279    pub fn section_data(
280        &mut self,
281        shdr: &SectionHeader,
282    ) -> Result<(&[u8], Option<CompressionHeader>), ParseError> {
283        if shdr.sh_type == abi::SHT_NOBITS {
284            return Ok((&[], None));
285        }
286
287        let (start, end) = shdr.get_data_range()?;
288        let buf = self.reader.read_bytes(start, end)?;
289
290        if shdr.sh_flags & abi::SHF_COMPRESSED as u64 == 0 {
291            Ok((buf, None))
292        } else {
293            let mut offset = 0;
294            let chdr = CompressionHeader::parse_at(
295                self.ehdr.endianness,
296                self.ehdr.class,
297                &mut offset,
298                buf,
299            )?;
300            let compressed_buf = buf.get(offset..).ok_or(ParseError::SliceReadError((
301                offset,
302                shdr.sh_size.try_into()?,
303            )))?;
304            Ok((compressed_buf, Some(chdr)))
305        }
306    }
307
308    /// Read the section data for the given
309    /// [SectionHeader](SectionHeader) and interpret it in-place as a
310    /// [StringTable](StringTable).
311    ///
312    /// Returns a [ParseError] if the
313    /// [sh_type](SectionHeader#structfield.sh_type) is not
314    /// [SHT_STRTAB](abi::SHT_STRTAB).
315    pub fn section_data_as_strtab(
316        &mut self,
317        shdr: &SectionHeader,
318    ) -> Result<StringTable<'_>, ParseError> {
319        if shdr.sh_type != abi::SHT_STRTAB {
320            return Err(ParseError::UnexpectedSectionType((
321                shdr.sh_type,
322                abi::SHT_STRTAB,
323            )));
324        }
325
326        let (start, end) = shdr.get_data_range()?;
327        let buf = self.reader.read_bytes(start, end)?;
328        Ok(StringTable::new(buf))
329    }
330
331    fn get_symbol_table_of_type(
332        &mut self,
333        symtab_type: u32,
334    ) -> Result<Option<(SymbolTable<'_, E>, StringTable<'_>)>, ParseError> {
335        if self.shdrs.is_empty() {
336            return Ok(None);
337        }
338
339        // Get the symtab header for the symtab. The gABI states there can be zero or one per ELF file.
340        match self.shdrs.iter().find(|shdr| shdr.sh_type == symtab_type) {
341            Some(shdr) => {
342                // Load the section bytes for the symtab
343                // (we want immutable references to both the symtab and its strtab concurrently)
344                let (symtab_start, symtab_end) = shdr.get_data_range()?;
345                self.reader.load_bytes(symtab_start..symtab_end)?;
346
347                // Load the section bytes for the strtab
348                // (we want immutable references to both the symtab and its strtab concurrently)
349                let strtab = self
350                    .shdrs
351                    .get(shdr.sh_link as usize)
352                    .ok_or(ParseError::BadOffset(shdr.sh_link as u64))?;
353                let (strtab_start, strtab_end) = strtab.get_data_range()?;
354                self.reader.load_bytes(strtab_start..strtab_end)?;
355
356                // Validate entsize before trying to read the table so that we can error early for corrupted files
357                Symbol::validate_entsize(self.ehdr.class, shdr.sh_entsize.try_into()?)?;
358                let symtab = SymbolTable::new(
359                    self.ehdr.endianness,
360                    self.ehdr.class,
361                    self.reader.get_bytes(symtab_start..symtab_end),
362                );
363                let strtab = StringTable::new(self.reader.get_bytes(strtab_start..strtab_end));
364                Ok(Some((symtab, strtab)))
365            }
366            None => Ok(None),
367        }
368    }
369
370    /// Get the symbol table (section of type SHT_SYMTAB) and its associated string table.
371    ///
372    /// The gABI specifies that ELF object files may have zero or one sections of type SHT_SYMTAB.
373    pub fn symbol_table(
374        &mut self,
375    ) -> Result<Option<(SymbolTable<'_, E>, StringTable<'_>)>, ParseError> {
376        self.get_symbol_table_of_type(abi::SHT_SYMTAB)
377    }
378
379    /// Get the dynamic symbol table (section of type SHT_DYNSYM) and its associated string table.
380    ///
381    /// The gABI specifies that ELF object files may have zero or one sections of type SHT_DYNSYM.
382    pub fn dynamic_symbol_table(
383        &mut self,
384    ) -> Result<Option<(SymbolTable<'_, E>, StringTable<'_>)>, ParseError> {
385        self.get_symbol_table_of_type(abi::SHT_DYNSYM)
386    }
387
388    /// Get the .dynamic section/segment contents.
389    pub fn dynamic(&mut self) -> Result<Option<DynamicTable<'_, E>>, ParseError> {
390        // If we have section headers, then look it up there
391        if !self.shdrs.is_empty() {
392            if let Some(shdr) = self
393                .shdrs
394                .iter()
395                .find(|shdr| shdr.sh_type == abi::SHT_DYNAMIC)
396            {
397                let (start, end) = shdr.get_data_range()?;
398                let buf = self.reader.read_bytes(start, end)?;
399                return Ok(Some(DynamicTable::new(
400                    self.ehdr.endianness,
401                    self.ehdr.class,
402                    buf,
403                )));
404            }
405        // Otherwise, look up the PT_DYNAMIC segment (if any)
406        } else if !self.phdrs.is_empty() {
407            if let Some(phdr) = self
408                .phdrs
409                .iter()
410                .find(|phdr| phdr.p_type == abi::PT_DYNAMIC)
411            {
412                let (start, end) = phdr.get_file_data_range()?;
413                let buf = self.reader.read_bytes(start, end)?;
414                return Ok(Some(DynamicTable::new(
415                    self.ehdr.endianness,
416                    self.ehdr.class,
417                    buf,
418                )));
419            }
420        }
421        Ok(None)
422    }
423
424    /// Read the section data for the various GNU Symbol Versioning sections (if any)
425    /// and return them in a [SymbolVersionTable] that which can interpret them in-place to
426    /// yield [SymbolRequirement](crate::gnu_symver::SymbolRequirement)s
427    /// and [SymbolDefinition](crate::gnu_symver::SymbolDefinition)s
428    ///
429    /// This is a GNU extension and not all objects use symbol versioning.
430    /// Returns an empty Option if the object does not use symbol versioning.
431    pub fn symbol_version_table(
432        &mut self,
433    ) -> Result<Option<SymbolVersionTable<'_, E>>, ParseError> {
434        // No sections means no GNU symbol versioning sections, which is ok
435        if self.shdrs.is_empty() {
436            return Ok(None);
437        }
438
439        let mut versym_opt: Option<SectionHeader> = None;
440        let mut needs_opt: Option<SectionHeader> = None;
441        let mut defs_opt: Option<SectionHeader> = None;
442        // Find the GNU Symbol versioning sections (if any)
443        for shdr in self.shdrs.iter() {
444            if shdr.sh_type == abi::SHT_GNU_VERSYM {
445                versym_opt = Some(*shdr);
446            } else if shdr.sh_type == abi::SHT_GNU_VERNEED {
447                needs_opt = Some(*shdr);
448            } else if shdr.sh_type == abi::SHT_GNU_VERDEF {
449                defs_opt = Some(*shdr);
450            }
451
452            // If we've found all three sections, then we're done
453            if versym_opt.is_some() && needs_opt.is_some() && defs_opt.is_some() {
454                break;
455            }
456        }
457
458        // No VERSYM section means the object doesn't use symbol versioning, which is ok.
459        if versym_opt.is_none() {
460            return Ok(None);
461        }
462
463        // Load the versym table
464        let versym_shdr = versym_opt.unwrap();
465        // Validate VERSYM entsize before trying to read the table so that we can error early for corrupted files
466        VersionIndex::validate_entsize(self.ehdr.class, versym_shdr.sh_entsize.try_into()?)?;
467        let (versym_start, versym_end) = versym_shdr.get_data_range()?;
468        self.reader.load_bytes(versym_start..versym_end)?;
469
470        // Get the VERNEED string shdr and load the VERNEED section data (if any)
471        let needs_shdrs = match needs_opt {
472            Some(shdr) => {
473                let (start, end) = shdr.get_data_range()?;
474                self.reader.load_bytes(start..end)?;
475
476                let strs_shdr = self
477                    .shdrs
478                    .get(shdr.sh_link as usize)
479                    .ok_or(ParseError::BadOffset(shdr.sh_link as u64))?;
480                let (strs_start, strs_end) = strs_shdr.get_data_range()?;
481                self.reader.load_bytes(strs_start..strs_end)?;
482
483                Some((shdr, strs_shdr))
484            }
485            // It's possible to have symbol versioning with no NEEDs if we're an object that only
486            // exports defined symbols.
487            None => None,
488        };
489
490        // Get the VERDEF string shdr and load the VERDEF section data (if any)
491        let defs_shdrs = match defs_opt {
492            Some(shdr) => {
493                let (start, end) = shdr.get_data_range()?;
494                self.reader.load_bytes(start..end)?;
495
496                let strs_shdr = self
497                    .shdrs
498                    .get(shdr.sh_link as usize)
499                    .ok_or(ParseError::BadOffset(shdr.sh_link as u64))?;
500                let (strs_start, strs_end) = strs_shdr.get_data_range()?;
501                self.reader.load_bytes(strs_start..strs_end)?;
502
503                Some((shdr, strs_shdr))
504            }
505            // It's possible to have symbol versioning with no DEFs if we're an object that doesn't
506            // export any symbols but does use dynamic symbols from other objects.
507            None => None,
508        };
509
510        // Wrap the VERNEED section and strings data in an iterator and string table
511        let verneeds = match needs_shdrs {
512            Some((shdr, strs_shdr)) => {
513                let (strs_start, strs_end) = strs_shdr.get_data_range()?;
514                let strs_buf = self.reader.get_bytes(strs_start..strs_end);
515
516                let (start, end) = shdr.get_data_range()?;
517                let buf = self.reader.get_bytes(start..end);
518                Some((
519                    VerNeedIterator::new(
520                        self.ehdr.endianness,
521                        self.ehdr.class,
522                        shdr.sh_info as u64,
523                        0,
524                        buf,
525                    ),
526                    StringTable::new(strs_buf),
527                ))
528            }
529            // If there's no NEEDs, then construct empty wrappers for them
530            None => None,
531        };
532
533        // Wrap the VERDEF section and strings data in an iterator and string table
534        let verdefs = match defs_shdrs {
535            Some((shdr, strs_shdr)) => {
536                let (strs_start, strs_end) = strs_shdr.get_data_range()?;
537                let strs_buf = self.reader.get_bytes(strs_start..strs_end);
538
539                let (start, end) = shdr.get_data_range()?;
540                let buf = self.reader.get_bytes(start..end);
541                Some((
542                    VerDefIterator::new(
543                        self.ehdr.endianness,
544                        self.ehdr.class,
545                        shdr.sh_info as u64,
546                        0,
547                        buf,
548                    ),
549                    StringTable::new(strs_buf),
550                ))
551            }
552            // If there's no DEFs, then construct empty wrappers for them
553            None => None,
554        };
555
556        // Wrap the versym section data in a parsing table
557        let version_ids = VersionIndexTable::new(
558            self.ehdr.endianness,
559            self.ehdr.class,
560            self.reader.get_bytes(versym_start..versym_end),
561        );
562
563        // whew, we're done here!
564        Ok(Some(SymbolVersionTable::new(
565            version_ids,
566            verneeds,
567            verdefs,
568        )))
569    }
570
571    /// Read the section data for the given
572    /// [SectionHeader](SectionHeader) and interpret it in-place as a
573    /// [RelIterator](RelIterator).
574    ///
575    /// Returns a [ParseError] if the
576    /// [sh_type](SectionHeader#structfield.sh_type) is not
577    /// [SHT_REL](abi::SHT_REL).
578    pub fn section_data_as_rels(
579        &mut self,
580        shdr: &SectionHeader,
581    ) -> Result<RelIterator<'_, E>, ParseError> {
582        if shdr.sh_type != abi::SHT_REL {
583            return Err(ParseError::UnexpectedSectionType((
584                shdr.sh_type,
585                abi::SHT_REL,
586            )));
587        }
588
589        let (start, end) = shdr.get_data_range()?;
590        let buf = self.reader.read_bytes(start, end)?;
591        Ok(RelIterator::new(self.ehdr.endianness, self.ehdr.class, buf))
592    }
593
594    /// Read the section data for the given
595    /// [SectionHeader](SectionHeader) and interpret it in-place as a
596    /// [RelaIterator](RelaIterator).
597    ///
598    /// Returns a [ParseError] if the
599    /// [sh_type](SectionHeader#structfield.sh_type) is not
600    /// [SHT_RELA](abi::SHT_RELA).
601    pub fn section_data_as_relas(
602        &mut self,
603        shdr: &SectionHeader,
604    ) -> Result<RelaIterator<'_, E>, ParseError> {
605        if shdr.sh_type != abi::SHT_RELA {
606            return Err(ParseError::UnexpectedSectionType((
607                shdr.sh_type,
608                abi::SHT_RELA,
609            )));
610        }
611
612        let (start, end) = shdr.get_data_range()?;
613        let buf = self.reader.read_bytes(start, end)?;
614        Ok(RelaIterator::new(
615            self.ehdr.endianness,
616            self.ehdr.class,
617            buf,
618        ))
619    }
620
621    /// Read the section data for the given
622    /// [SectionHeader](SectionHeader) and interpret it in-place as a
623    /// [NoteIterator](NoteIterator).
624    ///
625    /// Returns a [ParseError] if the
626    /// [sh_type](SectionHeader#structfield.sh_type) is not
627    /// [SHT_NOTE](abi::SHT_NOTE).
628    pub fn section_data_as_notes(
629        &mut self,
630        shdr: &SectionHeader,
631    ) -> Result<NoteIterator<'_, E>, ParseError> {
632        if shdr.sh_type != abi::SHT_NOTE {
633            return Err(ParseError::UnexpectedSectionType((
634                shdr.sh_type,
635                abi::SHT_NOTE,
636            )));
637        }
638
639        let (start, end) = shdr.get_data_range()?;
640        let buf = self.reader.read_bytes(start, end)?;
641        Ok(NoteIterator::new(
642            self.ehdr.endianness,
643            self.ehdr.class,
644            shdr.sh_addralign as usize,
645            buf,
646        ))
647    }
648
649    /// Read the segment data for the given [Segment](ProgramHeader).
650    pub fn segment_data(&mut self, phdr: &ProgramHeader) -> Result<&[u8], ParseError> {
651        let (start, end) = phdr.get_file_data_range()?;
652        self.reader.read_bytes(start, end)
653    }
654
655    /// Read the segment data for the given
656    /// [Segment](ProgramHeader) and interpret it in-place as a
657    /// [NoteIterator](NoteIterator).
658    ///
659    /// Returns a [ParseError] if the
660    /// [p_type](ProgramHeader#structfield.p_type) is not
661    /// [PT_NOTE](abi::PT_NOTE).
662    pub fn segment_data_as_notes(
663        &mut self,
664        phdr: &ProgramHeader,
665    ) -> Result<NoteIterator<'_, E>, ParseError> {
666        if phdr.p_type != abi::PT_NOTE {
667            return Err(ParseError::UnexpectedSegmentType((
668                phdr.p_type,
669                abi::PT_NOTE,
670            )));
671        }
672
673        let (start, end) = phdr.get_file_data_range()?;
674        let buf = self.reader.read_bytes(start, end)?;
675        Ok(NoteIterator::new(
676            self.ehdr.endianness,
677            self.ehdr.class,
678            phdr.p_align as usize,
679            buf,
680        ))
681    }
682}
683
684#[derive(Debug)]
685struct CachingReader<R: Read + Seek> {
686    reader: R,
687    stream_len: u64,
688    bufs: HashMap<(usize, usize), Box<[u8]>>,
689}
690
691impl<R: Read + Seek> CachingReader<R> {
692    fn new(mut reader: R) -> Result<Self, ParseError> {
693        // Cache the size of the stream so that we can err (rather than OOM) on invalid
694        // huge read requests.
695        let stream_len = reader.seek(SeekFrom::End(0))?;
696        Ok(CachingReader {
697            reader,
698            stream_len,
699            bufs: HashMap::<(usize, usize), Box<[u8]>>::default(),
700        })
701    }
702
703    fn read_bytes(&mut self, start: usize, end: usize) -> Result<&[u8], ParseError> {
704        self.load_bytes(start..end)?;
705        Ok(self.get_bytes(start..end))
706    }
707
708    fn get_bytes(&self, range: Range<usize>) -> &[u8] {
709        // It's a programmer error to call get_bytes without first calling load_bytes, so
710        // we want to panic here.
711        self.bufs
712            .get(&(range.start, range.end))
713            .expect("load_bytes must be called before get_bytes for every range")
714    }
715
716    fn load_bytes(&mut self, range: Range<usize>) -> Result<(), ParseError> {
717        if self.bufs.contains_key(&(range.start, range.end)) {
718            return Ok(());
719        }
720
721        // Verify that the read range doesn't go past the end of the stream (corrupted files)
722        let end = range.end as u64;
723        if end > self.stream_len {
724            return Err(ParseError::BadOffset(end));
725        }
726
727        self.reader.seek(SeekFrom::Start(range.start as u64))?;
728        let mut bytes = vec![0; range.len()].into_boxed_slice();
729        self.reader.read_exact(&mut bytes)?;
730        self.bufs.insert((range.start, range.end), bytes);
731        Ok(())
732    }
733
734    fn clear_cache(&mut self) {
735        self.bufs.clear()
736    }
737}
738
739#[cfg(test)]
740mod interface_tests {
741    use super::*;
742    use crate::dynamic::Dyn;
743    use crate::endian::AnyEndian;
744    use crate::hash::SysVHashTable;
745    use crate::note::{Note, NoteGnuAbiTag, NoteGnuBuildId};
746    use crate::relocation::Rela;
747
748    #[test]
749    fn test_open_stream() {
750        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
751        let io = std::fs::File::open(path).expect("Could not open file.");
752        let file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
753        assert_eq!(file.ehdr.e_type, abi::ET_EXEC);
754    }
755
756    #[test]
757    fn section_headers_with_strtab() {
758        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
759        let io = std::fs::File::open(path).expect("Could not open file.");
760        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
761
762        let (shdrs, strtab) = file
763            .section_headers_with_strtab()
764            .expect("Failed to get shdrs");
765        let (shdrs, strtab) = (shdrs, strtab.unwrap());
766
767        let shdr_4 = &shdrs[4];
768        let name = strtab
769            .get(shdr_4.sh_name as usize)
770            .expect("Failed to get section name");
771
772        assert_eq!(name, ".gnu.hash");
773        assert_eq!(shdr_4.sh_type, abi::SHT_GNU_HASH);
774    }
775
776    #[test]
777    fn shnum_and_shstrndx_in_shdr0() {
778        let path = std::path::PathBuf::from("sample-objects/shnum.x86_64");
779        let io = std::fs::File::open(path).expect("Could not open file.");
780        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
781
782        let (shdrs, strtab) = file
783            .section_headers_with_strtab()
784            .expect("shdrs should be parsable");
785        let (shdrs, strtab) = (shdrs, strtab.unwrap());
786
787        let shdrs_len = shdrs.len();
788        assert_eq!(shdrs_len, 0xFF15);
789
790        let shdr = shdrs.get(shdrs_len - 1).unwrap();
791        let name = strtab
792            .get(shdr.sh_name as usize)
793            .expect("Failed to get section name");
794
795        assert_eq!(name, ".shstrtab");
796        assert_eq!(shdr.sh_type, abi::SHT_STRTAB);
797    }
798
799    #[test]
800    fn section_header_by_name() {
801        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
802        let io = std::fs::File::open(path).expect("Could not open file.");
803        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
804
805        let shdr: SectionHeader = *file
806            .section_header_by_name(".gnu.hash")
807            .expect("section table should be parseable")
808            .expect("file should have .gnu.hash section");
809
810        assert_eq!(shdr.sh_type, abi::SHT_GNU_HASH);
811
812        let shdr = file
813            .section_header_by_name(".not.found")
814            .expect("section table should be parseable");
815
816        assert_eq!(shdr, None);
817    }
818
819    #[test]
820    fn section_data_for_nobits() {
821        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
822        let io = std::fs::File::open(path).expect("Could not open file.");
823        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
824
825        let shdr = file.section_headers()[26];
826        assert_eq!(shdr.sh_type, abi::SHT_NOBITS);
827        let (data, chdr) = file
828            .section_data(&shdr)
829            .expect("Failed to get section data");
830        assert_eq!(chdr, None);
831        assert_eq!(data, &[]);
832    }
833
834    #[test]
835    fn section_data() {
836        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
837        let io = std::fs::File::open(path).expect("Could not open file.");
838        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
839
840        let shdr = file.section_headers()[7];
841        assert_eq!(shdr.sh_type, abi::SHT_GNU_VERSYM);
842        let (data, chdr) = file
843            .section_data(&shdr)
844            .expect("Failed to get section data");
845        assert_eq!(chdr, None);
846        assert_eq!(data, [0, 0, 2, 0, 2, 0, 0, 0]);
847    }
848
849    #[test]
850    fn section_data_as_strtab() {
851        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
852        let io = std::fs::File::open(path).expect("Could not open file.");
853        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
854
855        let shdr = file.section_headers()[file.ehdr.e_shstrndx as usize];
856        let strtab = file
857            .section_data_as_strtab(&shdr)
858            .expect("Failed to read strtab");
859        assert_eq!(
860            strtab.get(1).expect("Failed to get strtab entry"),
861            ".symtab"
862        );
863    }
864
865    #[test]
866    fn segments() {
867        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
868        let io = std::fs::File::open(path).expect("Could not open file.");
869        let file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
870
871        let segments = file.segments();
872        assert_eq!(
873            segments[0],
874            ProgramHeader {
875                p_type: abi::PT_PHDR,
876                p_offset: 64,
877                p_vaddr: 4194368,
878                p_paddr: 4194368,
879                p_filesz: 448,
880                p_memsz: 448,
881                p_flags: 5,
882                p_align: 8,
883            }
884        )
885    }
886
887    #[test]
888    fn segments_phnum_in_shdr0() {
889        let path = std::path::PathBuf::from("sample-objects/phnum.m68k.so");
890        let io = std::fs::File::open(path).expect("Could not open file.");
891        let file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
892
893        assert_eq!(
894            file.segments()[0],
895            ProgramHeader {
896                p_type: abi::PT_PHDR,
897                p_offset: 92,
898                p_vaddr: 0,
899                p_paddr: 0,
900                p_filesz: 32,
901                p_memsz: 32,
902                p_flags: 0x20003,
903                p_align: 0x40000,
904            }
905        );
906    }
907
908    #[test]
909    fn symbol_table() {
910        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
911        let io = std::fs::File::open(path).expect("Could not open file.");
912        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
913
914        let (symtab, strtab) = file
915            .symbol_table()
916            .expect("Failed to read symbol table")
917            .expect("Failed to find symbol table");
918        let symbol = symtab.get(30).expect("Failed to get symbol");
919        assert_eq!(
920            symbol,
921            Symbol {
922                st_name: 19,
923                st_value: 6293200,
924                st_size: 0,
925                st_shndx: 21,
926                st_info: 1,
927                st_other: 0,
928            }
929        );
930        assert_eq!(
931            strtab
932                .get(symbol.st_name as usize)
933                .expect("Failed to get name from strtab"),
934            "__JCR_LIST__"
935        );
936    }
937
938    #[test]
939    fn dynamic_symbol_table() {
940        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
941        let io = std::fs::File::open(path).expect("Could not open file.");
942        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
943
944        let (symtab, strtab) = file
945            .dynamic_symbol_table()
946            .expect("Failed to read symbol table")
947            .expect("Failed to find symbol table");
948        let symbol = symtab.get(1).expect("Failed to get symbol");
949        assert_eq!(
950            symbol,
951            Symbol {
952                st_name: 11,
953                st_value: 0,
954                st_size: 0,
955                st_shndx: 0,
956                st_info: 18,
957                st_other: 0,
958            }
959        );
960        assert_eq!(
961            strtab
962                .get(symbol.st_name as usize)
963                .expect("Failed to get name from strtab"),
964            "memset"
965        );
966    }
967
968    #[test]
969    fn dynamic() {
970        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
971        let io = std::fs::File::open(path).expect("Could not open file.");
972        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
973
974        let mut dynamic = file
975            .dynamic()
976            .expect("Failed to parse .dynamic")
977            .expect("Failed to find .dynamic")
978            .iter();
979        assert_eq!(
980            dynamic.next().expect("Failed to get dyn entry"),
981            Dyn {
982                d_tag: abi::DT_NEEDED,
983                d_un: 1
984            }
985        );
986        assert_eq!(
987            dynamic.next().expect("Failed to get dyn entry"),
988            Dyn {
989                d_tag: abi::DT_INIT,
990                d_un: 4195216
991            }
992        );
993    }
994
995    #[test]
996    fn section_data_as_rels() {
997        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
998        let io = std::fs::File::open(path).expect("Could not open file.");
999        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
1000
1001        let shdr = file.section_headers()[10];
1002        file.section_data_as_rels(&shdr)
1003            .expect_err("Expected error parsing non-REL scn as RELs");
1004    }
1005
1006    #[test]
1007    fn section_data_as_relas() {
1008        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1009        let io = std::fs::File::open(path).expect("Could not open file.");
1010        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
1011
1012        let shdr = file.section_headers()[10];
1013        let mut relas = file
1014            .section_data_as_relas(&shdr)
1015            .expect("Failed to read relas section");
1016        assert_eq!(
1017            relas.next().expect("Failed to get rela entry"),
1018            Rela {
1019                r_offset: 6293704,
1020                r_sym: 1,
1021                r_type: 7,
1022                r_addend: 0,
1023            }
1024        );
1025        assert_eq!(
1026            relas.next().expect("Failed to get rela entry"),
1027            Rela {
1028                r_offset: 6293712,
1029                r_sym: 2,
1030                r_type: 7,
1031                r_addend: 0,
1032            }
1033        );
1034        assert!(relas.next().is_none());
1035    }
1036
1037    #[test]
1038    fn section_data_as_notes() {
1039        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1040        let io = std::fs::File::open(path).expect("Could not open file.");
1041        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
1042
1043        let shdr = file.section_headers()[2];
1044        let mut notes = file
1045            .section_data_as_notes(&shdr)
1046            .expect("Failed to read relas section");
1047        assert_eq!(
1048            notes.next().expect("Failed to get first note"),
1049            Note::GnuAbiTag(NoteGnuAbiTag {
1050                os: 0,
1051                major: 2,
1052                minor: 6,
1053                subminor: 32
1054            })
1055        );
1056        assert!(notes.next().is_none());
1057    }
1058
1059    #[test]
1060    fn segment_data() {
1061        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1062        let io = std::fs::File::open(path).expect("Could not open file.");
1063        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
1064
1065        let phdrs = file.segments();
1066        let note_phdr = phdrs[5];
1067
1068        let raw_notes = file
1069            .segment_data(&note_phdr)
1070            .expect("Failed to read notes segment");
1071
1072        // Generated with:
1073        // $ dd if=sample-objects/basic.x86_64 bs=1 skip=$((0x21c)) count=$((0x44)) status=none \
1074        //   | xxd -i
1075        let expected = [
1076            0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x4e,
1077            0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
1078            0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00,
1079            0x00, 0x00, 0x47, 0x4e, 0x55, 0x00, 0x77, 0x41, 0x9f, 0x0d, 0xa5, 0x10, 0x83, 0x0c,
1080            0x57, 0xa7, 0xc8, 0xcc, 0xb0, 0xee, 0x85, 0x5f, 0xee, 0xd3, 0x76, 0xa3,
1081        ];
1082        assert_eq!(raw_notes, expected);
1083    }
1084
1085    #[test]
1086    fn segment_data_as_notes() {
1087        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1088        let io = std::fs::File::open(path).expect("Could not open file.");
1089        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
1090
1091        let phdrs = file.segments();
1092        let note_phdr = phdrs[5];
1093        let mut notes = file
1094            .segment_data_as_notes(&note_phdr)
1095            .expect("Failed to read notes segment");
1096        assert_eq!(
1097            notes.next().expect("Failed to get first note"),
1098            Note::GnuAbiTag(NoteGnuAbiTag {
1099                os: 0,
1100                major: 2,
1101                minor: 6,
1102                subminor: 32
1103            })
1104        );
1105        assert_eq!(
1106            notes.next().expect("Failed to get second note"),
1107            Note::GnuBuildId(NoteGnuBuildId(&[
1108                119, 65, 159, 13, 165, 16, 131, 12, 87, 167, 200, 204, 176, 238, 133, 95, 238, 211,
1109                118, 163
1110            ]))
1111        );
1112        assert!(notes.next().is_none());
1113    }
1114
1115    #[test]
1116    fn symbol_version_table() {
1117        let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so");
1118        let io = std::fs::File::open(path).expect("Could not open file.");
1119        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
1120
1121        let vst = file
1122            .symbol_version_table()
1123            .expect("Failed to parse GNU symbol versions")
1124            .expect("Failed to find GNU symbol versions");
1125
1126        let req = vst
1127            .get_requirement(2)
1128            .expect("Failed to parse NEED")
1129            .expect("Failed to find NEED");
1130        assert_eq!(req.file, "libc.so.6");
1131        assert_eq!(req.name, "GLIBC_2.2.5");
1132        assert_eq!(req.hash, 0x9691A75);
1133
1134        let req = vst.get_requirement(3).expect("Failed to parse NEED");
1135        assert!(req.is_none());
1136
1137        let req = vst.get_requirement(4).expect("Failed to parse NEED");
1138        assert!(req.is_none());
1139
1140        let req = vst
1141            .get_requirement(5)
1142            .expect("Failed to parse NEED")
1143            .expect("Failed to find NEED");
1144        assert_eq!(req.file, "libc.so.6");
1145        assert_eq!(req.name, "GLIBC_2.2.5");
1146        assert_eq!(req.hash, 0x9691A75);
1147
1148        let def = vst
1149            .get_definition(3)
1150            .expect("Failed to parse DEF")
1151            .expect("Failed to find DEF");
1152        assert_eq!(def.hash, 0xC33237F);
1153        assert_eq!(def.flags, 1);
1154        assert!(!def.hidden);
1155        let def_names: Vec<&str> = def.names.map(|res| res.expect("should parse")).collect();
1156        assert_eq!(def_names, &["hello.so"]);
1157
1158        let def = vst
1159            .get_definition(7)
1160            .expect("Failed to parse DEF")
1161            .expect("Failed to find DEF");
1162        assert_eq!(def.hash, 0x1570B62);
1163        assert_eq!(def.flags, 0);
1164        assert!(def.hidden);
1165        let def_names: Vec<&str> = def.names.map(|res| res.expect("should parse")).collect();
1166        assert_eq!(def_names, &["HELLO_1.42"]);
1167    }
1168
1169    #[test]
1170    fn sysv_hash_table() {
1171        let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so");
1172        let io = std::fs::File::open(path).expect("Could not open file.");
1173        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
1174
1175        // Look up the SysV hash section header
1176        let hash_shdr = *file
1177            .section_header_by_name(".hash")
1178            .expect("Failed to find sysv hash section")
1179            .expect("Failed to find sysv hash section");
1180
1181        // We don't have a file interface for getting the SysV hash section yet, so clone the section bytes
1182        // So we can use them to back a SysVHashTable
1183        let (data, _) = file
1184            .section_data(&hash_shdr)
1185            .expect("Failed to get hash section data");
1186        let data_copy: Vec<u8> = data.into();
1187        let hash_table =
1188            SysVHashTable::new(file.ehdr.endianness, file.ehdr.class, data_copy.as_ref())
1189                .expect("Failed to parse hash table");
1190
1191        // Get the dynamic symbol table.
1192        let (symtab, strtab) = file
1193            .dynamic_symbol_table()
1194            .expect("Failed to read symbol table")
1195            .expect("Failed to find symbol table");
1196
1197        // Verify that these three symbols all collide in the hash table's buckets
1198        assert_eq!(crate::hash::sysv_hash(b"use_memset_v2"), 0x8080542);
1199        assert_eq!(crate::hash::sysv_hash(b"__gmon_start__"), 0xF4D007F);
1200        assert_eq!(crate::hash::sysv_hash(b"memset"), 0x73C49C4);
1201        assert_eq!(crate::hash::sysv_hash(b"use_memset_v2") % 3, 0);
1202        assert_eq!(crate::hash::sysv_hash(b"__gmon_start__") % 3, 0);
1203        assert_eq!(crate::hash::sysv_hash(b"memset") % 3, 0);
1204
1205        // Use the hash table to find a given symbol in it.
1206        let (sym_idx, sym) = hash_table
1207            .find(b"memset", &symtab, &strtab)
1208            .expect("Failed to parse hash")
1209            .expect("Failed to find hash");
1210
1211        // Verify that we got the same symbol from the hash table we expected
1212        assert_eq!(sym_idx, 2);
1213        assert_eq!(strtab.get(sym.st_name as usize).unwrap(), "memset");
1214        assert_eq!(
1215            sym,
1216            symtab.get(sym_idx).expect("Failed to get expected sym")
1217        );
1218    }
1219}
1220
1221#[cfg(test)]
1222mod arch_tests {
1223    use super::*;
1224    use crate::endian::AnyEndian;
1225
1226    // Basic smoke test which parses out symbols and headers for a given sample object of a given architecture
1227    macro_rules! arch_test {
1228        ( $arch:expr, $e_machine:expr, $endian:expr) => {{
1229            let path_str = format!("sample-objects/symver.{}.so", $arch);
1230            let path = std::path::PathBuf::from(path_str);
1231            let io = std::fs::File::open(path).expect("file should exist");
1232            let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("should parse");
1233
1234            assert_eq!(file.ehdr.e_machine, $e_machine);
1235            assert_eq!(file.ehdr.endianness, $endian);
1236
1237            let (shdrs, strtab) = file.section_headers_with_strtab().expect("should parse");
1238            let (shdrs, strtab) = (shdrs, strtab.unwrap());
1239            let _: Vec<_> = shdrs
1240                .iter()
1241                .map(|shdr| {
1242                    (
1243                        strtab.get(shdr.sh_name as usize).expect("should parse"),
1244                        shdr,
1245                    )
1246                })
1247                .collect();
1248
1249            if let Some((symtab, strtab)) = file.symbol_table().expect("should parse") {
1250                let _: Vec<_> = symtab
1251                    .iter()
1252                    .map(|sym| (strtab.get(sym.st_name as usize).expect("should parse"), sym))
1253                    .collect();
1254            }
1255
1256            if let Some((symtab, strtab)) = file.dynamic_symbol_table().expect("should parse") {
1257                let _: Vec<_> = symtab
1258                    .iter()
1259                    .map(|sym| (strtab.get(sym.st_name as usize).expect("should parse"), sym))
1260                    .collect();
1261            }
1262
1263            let note_phdrs: Vec<_> = file.segments()
1264                .iter()
1265                .filter(|phdr| phdr.p_type == abi::PT_NOTE)
1266                .map(|phdr| *phdr)
1267                .collect();
1268            for phdr in note_phdrs {
1269                let _: Vec<_> = file
1270                    .segment_data_as_notes(&phdr)
1271                    .expect("should parse")
1272                    .collect();
1273            }
1274        }};
1275    }
1276
1277    #[test]
1278    fn x86_64() {
1279        arch_test!("x86_64", abi::EM_X86_64, AnyEndian::Little);
1280    }
1281
1282    #[test]
1283    fn m68k() {
1284        arch_test!("m68k", abi::EM_68K, AnyEndian::Big);
1285    }
1286
1287    #[test]
1288    fn aarch64() {
1289        arch_test!("aarch64", abi::EM_AARCH64, AnyEndian::Little);
1290    }
1291
1292    #[test]
1293    fn armhf() {
1294        arch_test!("armhf", abi::EM_ARM, AnyEndian::Little);
1295    }
1296
1297    #[test]
1298    fn powerpc64() {
1299        arch_test!("powerpc64", abi::EM_PPC64, AnyEndian::Big);
1300    }
1301
1302    #[test]
1303    fn powerpc64le() {
1304        arch_test!("powerpc64le", abi::EM_PPC64, AnyEndian::Little);
1305    }
1306
1307    #[test]
1308    fn riscv64() {
1309        arch_test!("riscv64", abi::EM_RISCV, AnyEndian::Little);
1310    }
1311}