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_RELA](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
650    /// [Segment](ProgramHeader) and interpret it in-place as a
651    /// [NoteIterator](NoteIterator).
652    ///
653    /// Returns a [ParseError] if the
654    /// [p_type](ProgramHeader#structfield.p_type) is not
655    /// [PT_RELA](abi::PT_NOTE).
656    pub fn segment_data_as_notes(
657        &mut self,
658        phdr: &ProgramHeader,
659    ) -> Result<NoteIterator<'_, E>, ParseError> {
660        if phdr.p_type != abi::PT_NOTE {
661            return Err(ParseError::UnexpectedSegmentType((
662                phdr.p_type,
663                abi::PT_NOTE,
664            )));
665        }
666
667        let (start, end) = phdr.get_file_data_range()?;
668        let buf = self.reader.read_bytes(start, end)?;
669        Ok(NoteIterator::new(
670            self.ehdr.endianness,
671            self.ehdr.class,
672            phdr.p_align as usize,
673            buf,
674        ))
675    }
676}
677
678#[derive(Debug)]
679struct CachingReader<R: Read + Seek> {
680    reader: R,
681    stream_len: u64,
682    bufs: HashMap<(usize, usize), Box<[u8]>>,
683}
684
685impl<R: Read + Seek> CachingReader<R> {
686    fn new(mut reader: R) -> Result<Self, ParseError> {
687        // Cache the size of the stream so that we can err (rather than OOM) on invalid
688        // huge read requests.
689        let stream_len = reader.seek(SeekFrom::End(0))?;
690        Ok(CachingReader {
691            reader,
692            stream_len,
693            bufs: HashMap::<(usize, usize), Box<[u8]>>::default(),
694        })
695    }
696
697    fn read_bytes(&mut self, start: usize, end: usize) -> Result<&[u8], ParseError> {
698        self.load_bytes(start..end)?;
699        Ok(self.get_bytes(start..end))
700    }
701
702    fn get_bytes(&self, range: Range<usize>) -> &[u8] {
703        // It's a programmer error to call get_bytes without first calling load_bytes, so
704        // we want to panic here.
705        self.bufs
706            .get(&(range.start, range.end))
707            .expect("load_bytes must be called before get_bytes for every range")
708    }
709
710    fn load_bytes(&mut self, range: Range<usize>) -> Result<(), ParseError> {
711        if self.bufs.contains_key(&(range.start, range.end)) {
712            return Ok(());
713        }
714
715        // Verify that the read range doesn't go past the end of the stream (corrupted files)
716        let end = range.end as u64;
717        if end > self.stream_len {
718            return Err(ParseError::BadOffset(end));
719        }
720
721        self.reader.seek(SeekFrom::Start(range.start as u64))?;
722        let mut bytes = vec![0; range.len()].into_boxed_slice();
723        self.reader.read_exact(&mut bytes)?;
724        self.bufs.insert((range.start, range.end), bytes);
725        Ok(())
726    }
727
728    fn clear_cache(&mut self) {
729        self.bufs.clear()
730    }
731}
732
733#[cfg(test)]
734mod interface_tests {
735    use super::*;
736    use crate::dynamic::Dyn;
737    use crate::endian::AnyEndian;
738    use crate::hash::SysVHashTable;
739    use crate::note::{Note, NoteGnuAbiTag, NoteGnuBuildId};
740    use crate::relocation::Rela;
741    use crate::symbol::Symbol;
742
743    #[test]
744    fn test_open_stream() {
745        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
746        let io = std::fs::File::open(path).expect("Could not open file.");
747        let file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
748        assert_eq!(file.ehdr.e_type, abi::ET_EXEC);
749    }
750
751    #[test]
752    fn section_headers_with_strtab() {
753        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
754        let io = std::fs::File::open(path).expect("Could not open file.");
755        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
756
757        let (shdrs, strtab) = file
758            .section_headers_with_strtab()
759            .expect("Failed to get shdrs");
760        let (shdrs, strtab) = (shdrs, strtab.unwrap());
761
762        let shdr_4 = &shdrs[4];
763        let name = strtab
764            .get(shdr_4.sh_name as usize)
765            .expect("Failed to get section name");
766
767        assert_eq!(name, ".gnu.hash");
768        assert_eq!(shdr_4.sh_type, abi::SHT_GNU_HASH);
769    }
770
771    #[test]
772    fn shnum_and_shstrndx_in_shdr0() {
773        let path = std::path::PathBuf::from("sample-objects/shnum.x86_64");
774        let io = std::fs::File::open(path).expect("Could not open file.");
775        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
776
777        let (shdrs, strtab) = file
778            .section_headers_with_strtab()
779            .expect("shdrs should be parsable");
780        let (shdrs, strtab) = (shdrs, strtab.unwrap());
781
782        let shdrs_len = shdrs.len();
783        assert_eq!(shdrs_len, 0xFF15);
784
785        let shdr = shdrs.get(shdrs_len - 1).unwrap();
786        let name = strtab
787            .get(shdr.sh_name as usize)
788            .expect("Failed to get section name");
789
790        assert_eq!(name, ".shstrtab");
791        assert_eq!(shdr.sh_type, abi::SHT_STRTAB);
792    }
793
794    #[test]
795    fn section_header_by_name() {
796        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
797        let io = std::fs::File::open(path).expect("Could not open file.");
798        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
799
800        let shdr: SectionHeader = *file
801            .section_header_by_name(".gnu.hash")
802            .expect("section table should be parseable")
803            .expect("file should have .gnu.hash section");
804
805        assert_eq!(shdr.sh_type, abi::SHT_GNU_HASH);
806
807        let shdr = file
808            .section_header_by_name(".not.found")
809            .expect("section table should be parseable");
810
811        assert_eq!(shdr, None);
812    }
813
814    #[test]
815    fn section_data_for_nobits() {
816        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
817        let io = std::fs::File::open(path).expect("Could not open file.");
818        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
819
820        let shdr = file.section_headers()[26];
821        assert_eq!(shdr.sh_type, abi::SHT_NOBITS);
822        let (data, chdr) = file
823            .section_data(&shdr)
824            .expect("Failed to get section data");
825        assert_eq!(chdr, None);
826        assert_eq!(data, &[]);
827    }
828
829    #[test]
830    fn section_data() {
831        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
832        let io = std::fs::File::open(path).expect("Could not open file.");
833        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
834
835        let shdr = file.section_headers()[7];
836        assert_eq!(shdr.sh_type, abi::SHT_GNU_VERSYM);
837        let (data, chdr) = file
838            .section_data(&shdr)
839            .expect("Failed to get section data");
840        assert_eq!(chdr, None);
841        assert_eq!(data, [0, 0, 2, 0, 2, 0, 0, 0]);
842    }
843
844    #[test]
845    fn section_data_as_strtab() {
846        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
847        let io = std::fs::File::open(path).expect("Could not open file.");
848        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
849
850        let shdr = file.section_headers()[file.ehdr.e_shstrndx as usize];
851        let strtab = file
852            .section_data_as_strtab(&shdr)
853            .expect("Failed to read strtab");
854        assert_eq!(
855            strtab.get(1).expect("Failed to get strtab entry"),
856            ".symtab"
857        );
858    }
859
860    #[test]
861    fn segments() {
862        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
863        let io = std::fs::File::open(path).expect("Could not open file.");
864        let file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
865
866        let segments = file.segments();
867        assert_eq!(
868            segments[0],
869            ProgramHeader {
870                p_type: abi::PT_PHDR,
871                p_offset: 64,
872                p_vaddr: 4194368,
873                p_paddr: 4194368,
874                p_filesz: 448,
875                p_memsz: 448,
876                p_flags: 5,
877                p_align: 8,
878            }
879        )
880    }
881
882    #[test]
883    fn segments_phnum_in_shdr0() {
884        let path = std::path::PathBuf::from("sample-objects/phnum.m68k.so");
885        let io = std::fs::File::open(path).expect("Could not open file.");
886        let file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
887
888        assert_eq!(
889            file.segments()[0],
890            ProgramHeader {
891                p_type: abi::PT_PHDR,
892                p_offset: 92,
893                p_vaddr: 0,
894                p_paddr: 0,
895                p_filesz: 32,
896                p_memsz: 32,
897                p_flags: 0x20003,
898                p_align: 0x40000,
899            }
900        );
901    }
902
903    #[test]
904    fn symbol_table() {
905        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
906        let io = std::fs::File::open(path).expect("Could not open file.");
907        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
908
909        let (symtab, strtab) = file
910            .symbol_table()
911            .expect("Failed to read symbol table")
912            .expect("Failed to find symbol table");
913        let symbol = symtab.get(30).expect("Failed to get symbol");
914        assert_eq!(
915            symbol,
916            Symbol {
917                st_name: 19,
918                st_value: 6293200,
919                st_size: 0,
920                st_shndx: 21,
921                st_info: 1,
922                st_other: 0,
923            }
924        );
925        assert_eq!(
926            strtab
927                .get(symbol.st_name as usize)
928                .expect("Failed to get name from strtab"),
929            "__JCR_LIST__"
930        );
931    }
932
933    #[test]
934    fn dynamic_symbol_table() {
935        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
936        let io = std::fs::File::open(path).expect("Could not open file.");
937        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
938
939        let (symtab, strtab) = file
940            .dynamic_symbol_table()
941            .expect("Failed to read symbol table")
942            .expect("Failed to find symbol table");
943        let symbol = symtab.get(1).expect("Failed to get symbol");
944        assert_eq!(
945            symbol,
946            Symbol {
947                st_name: 11,
948                st_value: 0,
949                st_size: 0,
950                st_shndx: 0,
951                st_info: 18,
952                st_other: 0,
953            }
954        );
955        assert_eq!(
956            strtab
957                .get(symbol.st_name as usize)
958                .expect("Failed to get name from strtab"),
959            "memset"
960        );
961    }
962
963    #[test]
964    fn dynamic() {
965        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
966        let io = std::fs::File::open(path).expect("Could not open file.");
967        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
968
969        let mut dynamic = file
970            .dynamic()
971            .expect("Failed to parse .dynamic")
972            .expect("Failed to find .dynamic")
973            .iter();
974        assert_eq!(
975            dynamic.next().expect("Failed to get dyn entry"),
976            Dyn {
977                d_tag: abi::DT_NEEDED,
978                d_un: 1
979            }
980        );
981        assert_eq!(
982            dynamic.next().expect("Failed to get dyn entry"),
983            Dyn {
984                d_tag: abi::DT_INIT,
985                d_un: 4195216
986            }
987        );
988    }
989
990    #[test]
991    fn section_data_as_rels() {
992        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
993        let io = std::fs::File::open(path).expect("Could not open file.");
994        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
995
996        let shdr = file.section_headers()[10];
997        file.section_data_as_rels(&shdr)
998            .expect_err("Expected error parsing non-REL scn as RELs");
999    }
1000
1001    #[test]
1002    fn section_data_as_relas() {
1003        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1004        let io = std::fs::File::open(path).expect("Could not open file.");
1005        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
1006
1007        let shdr = file.section_headers()[10];
1008        let mut relas = file
1009            .section_data_as_relas(&shdr)
1010            .expect("Failed to read relas section");
1011        assert_eq!(
1012            relas.next().expect("Failed to get rela entry"),
1013            Rela {
1014                r_offset: 6293704,
1015                r_sym: 1,
1016                r_type: 7,
1017                r_addend: 0,
1018            }
1019        );
1020        assert_eq!(
1021            relas.next().expect("Failed to get rela entry"),
1022            Rela {
1023                r_offset: 6293712,
1024                r_sym: 2,
1025                r_type: 7,
1026                r_addend: 0,
1027            }
1028        );
1029        assert!(relas.next().is_none());
1030    }
1031
1032    #[test]
1033    fn section_data_as_notes() {
1034        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1035        let io = std::fs::File::open(path).expect("Could not open file.");
1036        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
1037
1038        let shdr = file.section_headers()[2];
1039        let mut notes = file
1040            .section_data_as_notes(&shdr)
1041            .expect("Failed to read relas section");
1042        assert_eq!(
1043            notes.next().expect("Failed to get first note"),
1044            Note::GnuAbiTag(NoteGnuAbiTag {
1045                os: 0,
1046                major: 2,
1047                minor: 6,
1048                subminor: 32
1049            })
1050        );
1051        assert!(notes.next().is_none());
1052    }
1053
1054    #[test]
1055    fn segment_data_as_notes() {
1056        let path = std::path::PathBuf::from("sample-objects/basic.x86_64");
1057        let io = std::fs::File::open(path).expect("Could not open file.");
1058        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
1059
1060        let phdrs = file.segments();
1061        let note_phdr = phdrs[5];
1062        let mut notes = file
1063            .segment_data_as_notes(&note_phdr)
1064            .expect("Failed to read relas section");
1065        assert_eq!(
1066            notes.next().expect("Failed to get first note"),
1067            Note::GnuAbiTag(NoteGnuAbiTag {
1068                os: 0,
1069                major: 2,
1070                minor: 6,
1071                subminor: 32
1072            })
1073        );
1074        assert_eq!(
1075            notes.next().expect("Failed to get second note"),
1076            Note::GnuBuildId(NoteGnuBuildId(&[
1077                119, 65, 159, 13, 165, 16, 131, 12, 87, 167, 200, 204, 176, 238, 133, 95, 238, 211,
1078                118, 163
1079            ]))
1080        );
1081        assert!(notes.next().is_none());
1082    }
1083
1084    #[test]
1085    fn symbol_version_table() {
1086        let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so");
1087        let io = std::fs::File::open(path).expect("Could not open file.");
1088        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
1089
1090        let vst = file
1091            .symbol_version_table()
1092            .expect("Failed to parse GNU symbol versions")
1093            .expect("Failed to find GNU symbol versions");
1094
1095        let req = vst
1096            .get_requirement(2)
1097            .expect("Failed to parse NEED")
1098            .expect("Failed to find NEED");
1099        assert_eq!(req.file, "libc.so.6");
1100        assert_eq!(req.name, "GLIBC_2.2.5");
1101        assert_eq!(req.hash, 0x9691A75);
1102
1103        let req = vst.get_requirement(3).expect("Failed to parse NEED");
1104        assert!(req.is_none());
1105
1106        let req = vst.get_requirement(4).expect("Failed to parse NEED");
1107        assert!(req.is_none());
1108
1109        let req = vst
1110            .get_requirement(5)
1111            .expect("Failed to parse NEED")
1112            .expect("Failed to find NEED");
1113        assert_eq!(req.file, "libc.so.6");
1114        assert_eq!(req.name, "GLIBC_2.2.5");
1115        assert_eq!(req.hash, 0x9691A75);
1116
1117        let def = vst
1118            .get_definition(3)
1119            .expect("Failed to parse DEF")
1120            .expect("Failed to find DEF");
1121        assert_eq!(def.hash, 0xC33237F);
1122        assert_eq!(def.flags, 1);
1123        assert!(!def.hidden);
1124        let def_names: Vec<&str> = def.names.map(|res| res.expect("should parse")).collect();
1125        assert_eq!(def_names, &["hello.so"]);
1126
1127        let def = vst
1128            .get_definition(7)
1129            .expect("Failed to parse DEF")
1130            .expect("Failed to find DEF");
1131        assert_eq!(def.hash, 0x1570B62);
1132        assert_eq!(def.flags, 0);
1133        assert!(def.hidden);
1134        let def_names: Vec<&str> = def.names.map(|res| res.expect("should parse")).collect();
1135        assert_eq!(def_names, &["HELLO_1.42"]);
1136    }
1137
1138    #[test]
1139    fn sysv_hash_table() {
1140        let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so");
1141        let io = std::fs::File::open(path).expect("Could not open file.");
1142        let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("Open test1");
1143
1144        // Look up the SysV hash section header
1145        let hash_shdr = *file
1146            .section_header_by_name(".hash")
1147            .expect("Failed to find sysv hash section")
1148            .expect("Failed to find sysv hash section");
1149
1150        // We don't have a file interface for getting the SysV hash section yet, so clone the section bytes
1151        // So we can use them to back a SysVHashTable
1152        let (data, _) = file
1153            .section_data(&hash_shdr)
1154            .expect("Failed to get hash section data");
1155        let data_copy: Vec<u8> = data.into();
1156        let hash_table =
1157            SysVHashTable::new(file.ehdr.endianness, file.ehdr.class, data_copy.as_ref())
1158                .expect("Failed to parse hash table");
1159
1160        // Get the dynamic symbol table.
1161        let (symtab, strtab) = file
1162            .dynamic_symbol_table()
1163            .expect("Failed to read symbol table")
1164            .expect("Failed to find symbol table");
1165
1166        // Verify that these three symbols all collide in the hash table's buckets
1167        assert_eq!(crate::hash::sysv_hash(b"use_memset_v2"), 0x8080542);
1168        assert_eq!(crate::hash::sysv_hash(b"__gmon_start__"), 0xF4D007F);
1169        assert_eq!(crate::hash::sysv_hash(b"memset"), 0x73C49C4);
1170        assert_eq!(crate::hash::sysv_hash(b"use_memset_v2") % 3, 0);
1171        assert_eq!(crate::hash::sysv_hash(b"__gmon_start__") % 3, 0);
1172        assert_eq!(crate::hash::sysv_hash(b"memset") % 3, 0);
1173
1174        // Use the hash table to find a given symbol in it.
1175        let (sym_idx, sym) = hash_table
1176            .find(b"memset", &symtab, &strtab)
1177            .expect("Failed to parse hash")
1178            .expect("Failed to find hash");
1179
1180        // Verify that we got the same symbol from the hash table we expected
1181        assert_eq!(sym_idx, 2);
1182        assert_eq!(strtab.get(sym.st_name as usize).unwrap(), "memset");
1183        assert_eq!(
1184            sym,
1185            symtab.get(sym_idx).expect("Failed to get expected sym")
1186        );
1187    }
1188}
1189
1190#[cfg(test)]
1191mod arch_tests {
1192    use super::*;
1193    use crate::endian::AnyEndian;
1194
1195    // Basic smoke test which parses out symbols and headers for a given sample object of a given architecture
1196    macro_rules! arch_test {
1197        ( $arch:expr, $e_machine:expr, $endian:expr) => {{
1198            let path_str = format!("sample-objects/symver.{}.so", $arch);
1199            let path = std::path::PathBuf::from(path_str);
1200            let io = std::fs::File::open(path).expect("file should exist");
1201            let mut file = ElfStream::<AnyEndian, _>::open_stream(io).expect("should parse");
1202
1203            assert_eq!(file.ehdr.e_machine, $e_machine);
1204            assert_eq!(file.ehdr.endianness, $endian);
1205
1206            let (shdrs, strtab) = file.section_headers_with_strtab().expect("should parse");
1207            let (shdrs, strtab) = (shdrs, strtab.unwrap());
1208            let _: Vec<_> = shdrs
1209                .iter()
1210                .map(|shdr| {
1211                    (
1212                        strtab.get(shdr.sh_name as usize).expect("should parse"),
1213                        shdr,
1214                    )
1215                })
1216                .collect();
1217
1218            if let Some((symtab, strtab)) = file.symbol_table().expect("should parse") {
1219                let _: Vec<_> = symtab
1220                    .iter()
1221                    .map(|sym| (strtab.get(sym.st_name as usize).expect("should parse"), sym))
1222                    .collect();
1223            }
1224
1225            if let Some((symtab, strtab)) = file.dynamic_symbol_table().expect("should parse") {
1226                let _: Vec<_> = symtab
1227                    .iter()
1228                    .map(|sym| (strtab.get(sym.st_name as usize).expect("should parse"), sym))
1229                    .collect();
1230            }
1231
1232            let note_phdrs: Vec<_> = file.segments()
1233                .iter()
1234                .filter(|phdr| phdr.p_type == abi::PT_NOTE)
1235                .map(|phdr| *phdr)
1236                .collect();
1237            for phdr in note_phdrs {
1238                let _: Vec<_> = file
1239                    .segment_data_as_notes(&phdr)
1240                    .expect("should parse")
1241                    .collect();
1242            }
1243        }};
1244    }
1245
1246    #[test]
1247    fn x86_64() {
1248        arch_test!("x86_64", abi::EM_X86_64, AnyEndian::Little);
1249    }
1250
1251    #[test]
1252    fn m68k() {
1253        arch_test!("m68k", abi::EM_68K, AnyEndian::Big);
1254    }
1255
1256    #[test]
1257    fn aarch64() {
1258        arch_test!("aarch64", abi::EM_AARCH64, AnyEndian::Little);
1259    }
1260
1261    #[test]
1262    fn armhf() {
1263        arch_test!("armhf", abi::EM_ARM, AnyEndian::Little);
1264    }
1265
1266    #[test]
1267    fn powerpc64() {
1268        arch_test!("powerpc64", abi::EM_PPC64, AnyEndian::Big);
1269    }
1270
1271    #[test]
1272    fn powerpc64le() {
1273        arch_test!("powerpc64le", abi::EM_PPC64, AnyEndian::Little);
1274    }
1275
1276    #[test]
1277    fn riscv64() {
1278        arch_test!("riscv64", abi::EM_RISCV, AnyEndian::Little);
1279    }
1280}