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(&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 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 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 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 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(¬e_phdr)
1070 .expect("Failed to read notes segment");
1071
1072 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(¬e_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 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 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 let (symtab, strtab) = file
1193 .dynamic_symbol_table()
1194 .expect("Failed to read symbol table")
1195 .expect("Failed to find symbol table");
1196
1197 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 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 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 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}