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#[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
34fn parse_section_headers<E: EndianParse, S: Read + Seek>(
40 ehdr: &FileHeader<E>,
41 reader: &mut CachingReader<S>,
42) -> Result<Vec<SectionHeader>, ParseError> {
43 if ehdr.e_shoff == 0 {
45 return Ok(Vec::default());
46 }
47
48 let entsize = SectionHeader::validate_entsize(ehdr.class, ehdr.e_shentsize as usize)?;
50
51 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 if ehdr.e_phoff == 0 {
83 return Ok(Vec::default());
84 }
85
86 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 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 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 cr.clear_cache();
141
142 Ok(ElfStream {
143 ehdr,
144 shdrs,
145 phdrs,
146 reader: cr,
147 })
148 }
149
150 pub fn segments(&self) -> &Vec<ProgramHeader> {
152 &self.phdrs
153 }
154
155 pub fn section_headers(&self) -> &Vec<SectionHeader> {
157 &self.shdrs
158 }
159
160 pub fn section_headers_with_strtab(
175 &mut self,
176 ) -> Result<(&Vec<SectionHeader>, Option<StringTable<'_>>), ParseError> {
177 if self.shdrs.is_empty() {
179 return Ok((&self.shdrs, None));
180 }
181
182 if self.ehdr.e_shstrndx == abi::SHN_UNDEF {
184 return Ok((&self.shdrs, None));
185 }
186
187 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 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 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 _ => {
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 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 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 match self.shdrs.iter().find(|shdr| shdr.sh_type == symtab_type) {
341 Some(shdr) => {
342 let (symtab_start, symtab_end) = shdr.get_data_range()?;
345 self.reader.load_bytes(symtab_start..symtab_end)?;
346
347 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 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 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 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 pub fn dynamic(&mut self) -> Result<Option<DynamicTable<'_, E>>, ParseError> {
390 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 } 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 pub fn symbol_version_table(
432 &mut self,
433 ) -> Result<Option<SymbolVersionTable<'_, E>>, ParseError> {
434 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 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 versym_opt.is_some() && needs_opt.is_some() && defs_opt.is_some() {
454 break;
455 }
456 }
457
458 if versym_opt.is_none() {
460 return Ok(None);
461 }
462
463 let versym_shdr = versym_opt.unwrap();
465 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 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 None => None,
488 };
489
490 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 None => None,
508 };
509
510 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 None => None,
531 };
532
533 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 None => None,
554 };
555
556 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 Ok(Some(SymbolVersionTable::new(
565 version_ids,
566 verneeds,
567 verdefs,
568 )))
569 }
570
571 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 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 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 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 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 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 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(¬e_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 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 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 let (symtab, strtab) = file
1162 .dynamic_symbol_table()
1163 .expect("Failed to read symbol table")
1164 .expect("Failed to find symbol table");
1165
1166 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 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 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 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}